Repository: mbrg/genai-attacks

Issue:
Introduce subtechniques
- Add support for subtechniques 
- Document a few subtechniques to serve as an example

Discussion:

Solution patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index fbb9339..ef1c7f1 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,11 +6,14 @@
     "BHUSA",
     "Cefalu",
     "Copirate",
+    "dmitry",
     "ishai",
     "ishay",
     "jailbreaking",
     "levelname",
+    "lozovoy",
     "malka",
+    "mbgsec",
     "mdbook",
     "mitigations",
     "OWASP",
diff --git a/build_scripts/generate_content_as_md.py b/build_scripts/generate_content_as_md.py
index df111e2..655ffe2 100644
--- a/build_scripts/generate_content_as_md.py
+++ b/build_scripts/generate_content_as_md.py
@@ -111,14 +111,34 @@ def load_json_files(base_dir, version):
 def create_matrix(tactics, techniques):
     logger.debug("Creating matrix of tactics and techniques")
     matrix = defaultdict(list)
+
+    # primary techniques
     for technique in techniques.values():
         for ref in technique.get("object_references", []):
+            if "is_sub_object" in ref:
+                continue
             if ref["$type"] == "tactic":
                 matrix[ref["$id"]].append(technique["$id"])
                 logger.debug(
                     f"Added technique {technique['$id']} to tactic {ref['$id']}"
                 )
 
+    technique_to_tactic = defaultdict(list)
+    for tactic_id, technique_ids in matrix.items():
+        for technique_id in technique_ids:
+            technique_to_tactic[technique_id].append(tactic_id)
+
+    # sub techniques
+    for technique in techniques.values():
+        for ref in technique.get("object_references", []):
+            if "is_sub_object" not in ref:
+                continue
+            for inherited_tactic_id in technique_to_tactic[ref["$id"]]:
+                matrix[inherited_tactic_id].append(technique["$id"])
+                logger.debug(
+                    f"Added sub-technique {technique['$id']} to tactic {inherited_tactic_id}"
+                )
+
     invalid_tactic_ids = set(matrix.keys()) - set(tactics.keys())
     if len(invalid_tactic_ids) > 0:
         raise ValueError(f"Matrix contains invalid tactic $ids: {invalid_tactic_ids}")
diff --git a/entity/dmitry_lozovoy.json b/entity/dmitry_lozovoy.json
new file mode 100644
index 0000000..f57c93b
--- /dev/null
+++ b/entity/dmitry_lozovoy.json
@@ -0,0 +1,9 @@
+{
+  "$id": "$gai-entity/dmitry_lozovoy",
+  "$schema": "../schema/entity.schema.json",
+  "$type": "entity",
+  "description": "",
+  "entity_type": "person",
+  "external_references": [],
+  "name": "Dmitry Lozovoy"
+}
diff --git a/schema/common.schema.json b/schema/common.schema.json
index ce04830..2dc4e9b 100644
--- a/schema/common.schema.json
+++ b/schema/common.schema.json
@@ -57,6 +57,11 @@
         "description": {
           "$comment": "More details about the relation between these objects.",
           "type": "string"
+        },
+        "is_sub_object": {
+          "$comment": "Is this object a sub-object of another. Used for sub-techniques, for example.",
+          "default": false,
+          "type": "boolean"
         }
       },
       "required": [
diff --git a/technique/off_target_language.json b/technique/off_target_language.json
new file mode 100644
index 0000000..89a8768
--- /dev/null
+++ b/technique/off_target_language.json
@@ -0,0 +1,34 @@
+{
+  "$id": "$gai-technique/off_target_language",
+  "$schema": "../schema/technique.schema.json",
+  "$type": "technique",
+  "description": "While system instructions are typically written in English, underlying foundational models may understand other languages as well. Using prompt injection techniques in a language other than the other used by the system instructions can effectively bypass their control. Ths method is also effective bypassing a model's inherent trained controls.",
+  "external_references": [
+    {
+      "href": "https://labs.zenity.io/p/outsmarting-copilot-creating-hyperlinks-copilot-365/",
+      "source": "Zenity Labs",
+      "title": "Outsmarting Copilot: Creating Hyperlinks in Copilot 365"
+    }
+  ],
+  "framework_references": [],
+  "name": "Off-Target Language",
+  "object_references": [
+    {
+      "$id": "$gai-technique/prompt_injection",
+      "$type": "technique",
+      "description": "Sub-technique of",
+      "is_sub_object": true
+    },
+    {
+      "$id": "$gai-technique/jailbreaking",
+      "$type": "technique",
+      "description": "Sub-technique of",
+      "is_sub_object": true
+    },
+    {
+      "$id": "$gai-entity/dmitry_lozovoy",
+      "$type": "entity",
+      "description": "Demonstrated by"
+    }
+  ]
+}
diff --git a/technique/system_instruction_keywords.json b/technique/system_instruction_keywords.json
new file mode 100644
index 0000000..bc5f51f
--- /dev/null
+++ b/technique/system_instruction_keywords.json
@@ -0,0 +1,38 @@
+{
+  "$id": "$gai-technique/system_instruction_keywords",
+  "$schema": "../schema/technique.schema.json",
+  "$type": "technique",
+  "description": "Including keywords from the system instructions can increase the probably that the AI system will interpret the content as privileged instructions.",
+  "external_references": [],
+  "framework_references": [],
+  "name": "System Instruction Keywords",
+  "object_references": [
+    {
+      "$id": "$gai-technique/prompt_injection",
+      "$type": "technique",
+      "description": "Sub-technique of",
+      "is_sub_object": true
+    },
+    {
+      "$id": "$gai-technique/jailbreaking",
+      "$type": "technique",
+      "description": "Sub-technique of",
+      "is_sub_object": true
+    },
+    {
+      "$id": "$gai-technique/system_instructions_extraction",
+      "$type": "technique",
+      "description": "Extracting the system instructions is a pre-requisite to applying this technique"
+    },
+    {
+      "$id": "$gai-entity/tamir_ishay_sharbat",
+      "$type": "entity",
+      "description": "Demonstrated by"
+    },
+    {
+      "$id": "$gai-entity/michael_bargury",
+      "$type": "entity",
+      "description": "Demonstrated by"
+    }
+  ]
+}


Test patch:
diff --git a/tests/object_ref_test.py b/tests/object_ref_test.py
index 465e27d..ce3d42d 100644
--- a/tests/object_ref_test.py
+++ b/tests/object_ref_test.py
@@ -2,11 +2,15 @@
 from test_utils import *
 
 
+def _validate_referenced_object_id(ref_obj_id):
+    ref_file_name = ref_obj_id.replace("$gai-", "") + ".json"
+    assert (
+        ref_file_name in OBJECT_FILE_NAMES
+    ), f"Invalid reference to object id: {ref_obj_id}"
+
+
 @pytest.mark.parametrize("json_object_path", OBJECT_FILE_NAMES)
 @load_json_object_wrapper
-def test_reference_is_valid(obj):
+def test_object_references(obj):
     for ref in obj.get("object_references", []):
-        ref_file_name = ref["$id"].replace("$gai-", "") + ".json"
-        assert (
-            ref_file_name in OBJECT_FILE_NAMES
-        ), f"Object {obj['$id']} has invalid reference to {ref['$id']}"
+        _validate_referenced_object_id(ref["$id"])