diff --git a/capectracer/capec_specs_dict.py b/capectracer/capec_dict.py similarity index 86% rename from capectracer/capec_specs_dict.py rename to capectracer/capec_dict.py index 9c1cc599694cb9f382f29b807a35a61fb323f3dd..c28b584dcfe96859a3e48ee31c2962a64120a64d 100644 --- a/capectracer/capec_specs_dict.py +++ b/capectracer/capec_dict.py @@ -3,9 +3,9 @@ import xmltodict from tokens_dict import TokensDict -# Define the CapecSpecsDict class, inheriting from TokensDict -class CapecSpecsDict(TokensDict): - def __init__(self, capec_file, spec_file): +# Define the CapecDict class, inheriting from TokensDict +class CapecDict(TokensDict): + def __init__(self, capec_file, input_file): super().__init__() self.capec_names = [] @@ -15,11 +15,11 @@ class CapecSpecsDict(TokensDict): self.capec_tokens = [] self.capec_sentences = [] - self.system_spec_tokens = [] - self.system_spec_sentences = [] + self.comparison_input_tokens = [] + self.comparison_input_sentences = [] self.parse_capecs(capec_file) - self.parse_system_specs(spec_file) + self.parse_input(input_file) def parse_capecs(self, capec_file): capec_descriptions = [] @@ -106,12 +106,12 @@ class CapecSpecsDict(TokensDict): self.capec_tokens = capec_tokens self.capec_sentences = capec_sentences - def parse_system_specs(self, spec_file): - with open(spec_file, 'r') as file: + def parse_input(self, input_file): + with open(input_file, 'r') as file: file_contents = file.read() - system_spec_texts = [super().clean_text(file_contents)] - system_spec_sentences, system_spec_tokens = super().preprocess(system_spec_texts) + input_texts = [super().clean_text(file_contents)] + input_sentences, input_tokens = super().preprocess(input_texts) - self.system_spec_tokens = system_spec_tokens[0] - self.system_spec_sentences = system_spec_sentences[0] + self.comparison_input_tokens = input_tokens[0] + self.comparison_input_sentences = input_sentences[0] diff --git a/capectracer/capec_tracer.py b/capectracer/capec_tracer.py index 26fe88628a3a3d3f6d674d20b8304a95febc6739..e3fe86d2a12d58c43bc2a267c9be5c7cee5124a8 100644 --- a/capectracer/capec_tracer.py +++ b/capectracer/capec_tracer.py @@ -2,8 +2,9 @@ import requests import os import torch import decimal +import argparse -from capec_specs_dict import CapecSpecsDict +from capec_dict import CapecDict from sentence_transformers import SentenceTransformer, util @@ -33,66 +34,104 @@ def round_dec(x, place, round_up): return float(rounded) -def trace_capecs(abs_path): - try: - dicti = CapecSpecsDict(abs_path + "capec_latest.xml", abs_path + "system_specs.txt") - capec_names = dicti.capec_names - capec_descs = dicti.capec_descriptions - capec_attack_steps = dicti.capec_execution_flows - all_capec_sentences = dicti.capec_sentences - spec_sentences = dicti.system_spec_sentences - - model = SentenceTransformer('basel/ATTACK-BERT') - spec_embeddings = model.encode(spec_sentences, convert_to_tensor=True) - - sim_scores = [] - - for index, capec_sentences in enumerate(all_capec_sentences): - capec_embeddings = model.encode(capec_sentences, convert_to_tensor=True) - - cos_scores = util.cos_sim(spec_embeddings, capec_embeddings) - mean_cos_score = torch.mean(cos_scores).item() - score = [index, mean_cos_score] - sim_scores.append(score) - - # Sort the arrays based on the value of the second index in each array - sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True) - max_score = round_dec(sim_scores[0][1], 2, True) - min_score = 0 - - with open(abs_path + 'traced_capecs.txt', 'w') as output: - for score in sim_scores: - if score[1] > 0: - normalized_score = (score[1] - min_score) / (max_score - min_score) - confidence_score = int(100 * normalized_score) - - if confidence_score > 0: - output.write(f'Confidence score: {confidence_score}%\n') - output.write(f'Name: {capec_names[score[0]]}\n') - output.write(f'Description:\n') - output.write(f'{capec_descs[score[0]]}\n') - - if capec_attack_steps[score[0]]: - output.write(f'Attack Steps:\n') - - for attack_step in capec_attack_steps[score[0]]: - output.write(f'{attack_step}\n') - output.write('\n') - except Exception as e: - with open(abs_path + 'traced_capecs.txt', 'w') as output: - output.write(str(e)) +def provide_att_patterns(abs_path): + dicti = CapecDict(abs_path + "capec_latest.xml", abs_path + "system_specs.txt") + capec_names = dicti.capec_names + capec_descs = dicti.capec_descriptions + capec_attack_steps = dicti.capec_execution_flows + all_capec_sentences = dicti.capec_sentences + spec_sentences = dicti.comparison_input_sentences + + model = SentenceTransformer('basel/ATTACK-BERT') + spec_embeddings = model.encode(spec_sentences, convert_to_tensor=True) + + sim_scores = [] + + for index, capec_sentences in enumerate(all_capec_sentences): + capec_embeddings = model.encode(capec_sentences, convert_to_tensor=True) + + cos_scores = util.cos_sim(spec_embeddings, capec_embeddings) + mean_cos_score = torch.mean(cos_scores).item() + score = [index, mean_cos_score] + sim_scores.append(score) + + # Sort the arrays based on the value of the second index in each array + sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True) + max_score = round_dec(sim_scores[0][1], 2, True) + min_score = 0 + + with open(abs_path + 'traces.txt', 'w') as output: + for score in sim_scores: + if score[1] > 0: + normalized_score = (score[1] - min_score) / (max_score - min_score) + confidence_score = int(100 * normalized_score) + + if confidence_score > 0: + output.write(f'Confidence score: {confidence_score}%\n') + output.write(f'Name: {capec_names[score[0]]}\n') + output.write(f'Description:\n') + output.write(f'{capec_descs[score[0]]}\n') + + if capec_attack_steps[score[0]]: + output.write(f'Attack Steps:\n') + + for attack_step in capec_attack_steps[score[0]]: + output.write(f'{attack_step}\n') + output.write('\n') + +def provide_mitigations(abs_path): + dicti = CapecDict(abs_path + "capec_latest.xml", abs_path + "attack.txt") + capec_mitigations = dicti.capec_mitigations + all_capec_sentences = dicti.capec_sentences + attack_sentences = dicti.comparison_input_sentences + + model = SentenceTransformer('basel/ATTACK-BERT') + attack_embeddings = model.encode(attack_sentences, convert_to_tensor=True) + + sim_scores = [] + + for index, capec_sentences in enumerate(all_capec_sentences): + capec_embeddings = model.encode(capec_sentences, convert_to_tensor=True) + + cos_scores = util.cos_sim(attack_embeddings, capec_embeddings) + mean_cos_score = torch.mean(cos_scores).item() + score = [index, mean_cos_score] + sim_scores.append(score) + + # Sort the arrays based on the value of the second index in each array + sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True) + already_printed_mitis = [] + + with open(abs_path + 'traces.txt', 'w') as output: + for score in sim_scores: + if score[1] > 0.4 and capec_mitigations[score[0]]: + for mitigation in capec_mitigations[score[0]]: + if mitigation not in already_printed_mitis: + output.write(f'{mitigation}\n\n') + already_printed_mitis.append(mitigation) if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Provides a list of most related CAPECs to a provided input.') + parser.add_argument('--mitigations', action='store_true', help='Provide most relevant mitigations to an attack.') + args = parser.parse_args() + abs_path = os.path.abspath(__file__) last_slash_index = abs_path.rfind("/") abs_path = abs_path[:last_slash_index + 1] - - response = get_capec_file(abs_path) - if response.status_code == 200: - trace_capecs(abs_path) - else: - with open(abs_path + 'traced_capecs.txt', 'w') as output: - output.write(f"Failed to download the list of CAPECs from MITRE. Status code: {response.status_code}.") + try: + response = get_capec_file(abs_path) + + if response.status_code == 200: + if args.mitigations: + provide_mitigations(abs_path) + else: + provide_att_patterns(abs_path) + else: + with open(abs_path + 'traces.txt', 'w') as output: + output.write(f"Failed to download the list of CAPECs from MITRE. Status code: {response.status_code}.") + except Exception as e: + with open(abs_path + 'traces.txt', 'w') as output: + output.write(str(e)) - print("Results (or errors if any were encountered) have been published to traced_capecs.txt.") + print("Results (or errors if any were encountered) have been published to traces.txt.") diff --git a/src/main/java/ai/AIAPTMitigations.java b/src/main/java/ai/AIAPTMitigations.java new file mode 100644 index 0000000000000000000000000000000000000000..0a5d35d92d77a8e20e63c724839d2a1f9bef1f24 --- /dev/null +++ b/src/main/java/ai/AIAPTMitigations.java @@ -0,0 +1,227 @@ +package ai; + +import attacktrees.Attack; +import attacktrees.AttackNode; +import attacktrees.AttackTree; +import myutil.TraceManager; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class AIAPTMitigations extends AIInteract { + private static final String KNOWLEDGE_ON_JSON_FOR_MITIGATIONS = "When you are asked to identify " + + "mitigations for a provided list of attack steps, " + + "return them as a JSON specification formatted as follows: " + + "{\"mitigations\": [{\"name\": \"NameOfMitigation\", \"description\": " + + "\"The description of the mitigation and how it prevents the attack.\"}, " + + "\"attacksteps\": [\"TheNameOfAProvidedAttackStep\" ...]} ...]} " + + "# Respect: All words in \"name\" must be conjoined together. " + + "# Respect: There must be no more than forty characters in \"name\". " + + "# Respect: For each word in \"name\", its first letter must be capitalized. " + + "# Respect: All words in \"description\" must be separated with spaces. " + + "# Respect: \"attacksteps\" is the list of attack steps that are associated with the " + + "mitigation. It must contain only the names of the associated attack steps that are " + + "from the provided list of attack steps. " + + "# Respect: If there are no mitigations that are able to be identified, have \"mitigations\" be " + + "an empty array."; + + private static final String[] KNOWLEDGE_STAGES = { + KNOWLEDGE_ON_JSON_FOR_MITIGATIONS + }; + + private final String[] QUESTION_IDENTIFY_ATD = { + "Using the specified JSON format, " + + "identify a list of possible mitigations that would prevent an attacker " + + "from using most, if not all, of the provided attack steps to further advance " + + "the attacker's attack scenario. Each mitigation should be associated with at least " + + "one attack step from the provided attack step list. If applicable, use the " + + "provided list of possible countermeasures as support for identifying mitigations. " + + "Do respect the JSON format, and provide only JSON (no explanation before or after).\n" + }; + + private final APTAuditor auditor; + private int maxRetries; + private final CAPECTracer tracer; + private AttackTree at; + + public AIAPTMitigations(AIChatData _chatData) { + super(_chatData); + maxRetries = 10; + auditor = new APTAuditor(); + tracer = new CAPECTracer(chatData); + } + + public void setReferenceAT(AttackTree at) { + this.at = at; + } + + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + public Object applyAnswer(Object input) { + return at; + } + + private void initKnowledge() { + chatData.aiinterface.clearKnowledge(); + } + + private void makeKnowledge(String[][] attackSteps, String[] counters) { + String[] know = KNOWLEDGE_STAGES[0].split("#"); + + for (String s : know) { + TraceManager.addDev("\nKnowledge added: " + s); + chatData.aiinterface.addKnowledge(s, "ok"); + } + + if (attackSteps != null && attackSteps.length > 0) { + String attackStepsString = buildDelimAttSteps(attackSteps); + TraceManager.addDev("\nKnowledge added: " + attackStepsString); + chatData.aiinterface.addKnowledge("The attack steps are: " + attackStepsString, "ok"); + } + + if (counters != null && counters.length > 0) { + String countersString = buildDelimString(counters); + TraceManager.addDev("\nKnowledge added: " + countersString); + chatData.aiinterface.addKnowledge("The possible counters are: " + countersString, "ok"); + } + } + + public void internalRequest() { + ArrayList<Attack> attackNodes = at.getAttacks(); + ArrayList<String> attackSteps = new ArrayList<>(); + Set<String> countersSet = new HashSet<>(); + + for (Attack attackNode : attackNodes) { + if (!attackNode.isRoot() && attackNode.isLeaf()) { + String attackStepName = attackNode.getName(); + String attackStepDesc = attackNode.getDescription(); + String attackStep = attackStepName + ". " + attackStepDesc; + attackSteps.add(attackStep); + + tracer.getMitigations(attackStepDesc); + String traces = tracer.getResults(); + + if (!traces.equals("The tracer failed to run successfully.")) { + String[] possibleCounters = tracer.getResults().split("\n\n"); + countersSet.addAll(Arrays.asList(possibleCounters)); + } + else { + return; + } + } + } + + String[] counters = countersSet.toArray(new String[0]); + String[] attSteps = attackSteps.toArray(new String[0]); + makeRequest(attSteps, counters); + + waitIfConditionTrue(true); + TraceManager.addDev("Reached end of AIAPTMitigations internal request."); + } + + private void makeRequest(String[] attackSteps, String[] counters) { + int numRetries = 0; + boolean done = false; + String json = ""; + String questionT = QUESTION_IDENTIFY_ATD[0]; + String[][] attackStepData = extractAttStepData(attackSteps); + + while (numRetries < maxRetries && !done) { + initKnowledge(); + makeKnowledge(attackStepData, counters); + boolean ok = makeQuestion(questionT); + + if (!ok) { + TraceManager.addDev("Make question failed"); + } + + ArrayList<String> errors = new ArrayList<>(); + + try { + TraceManager.addDev("\n\nMaking specification from " + chatData.lastAnswer + "\n\n"); + json = extractJSON(); + JSONObject mitigationsJSON = auditor.checkMitigations(at, json, attackStepData, errors); + + if (mitigationsJSON.isEmpty() && !errors.isEmpty()) { + errors.add("You must provide a list of possible mitigations that would prevent an attacker " + + "from using most, if not all, of the provided attack steps to further advance " + + "the attacker's attack scenario. Do respect the JSON format, and " + + "provide only JSON (no explanation before or after)."); + } + + if (!mitigationsJSON.isEmpty()) { + TraceManager.addDev("Identified mitigations: " + mitigationsJSON); + } + } + catch (org.json.JSONException e) { + TraceManager.addDev("Invalid JSON spec: " + extractJSON() + " because " + e.getMessage() + ": INJECTING ERROR"); + errors = new ArrayList<>(); + errors.add("There is an error in your JSON: " + e.getMessage() + ". Probably the JSON spec was incomplete. " + + "Do correct it. I need the full specification at once."); + } + + if (!errors.isEmpty()) { + questionT = "Your answer was as follows: " + json + "\n\nYet, it was not correct because of the following errors:"; + // Updating knowledge + for (String s : errors) { + questionT += "\n- " + s; + } + + numRetries++; + } + else { + done = true; + } + } + } + + private String buildDelimString(String[] list) { + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i <= list.length - 1; i++) { + String listItemCleaned = list[i].trim(); + builder.append(listItemCleaned); + + if (i < list.length - 1) { + builder.append(" | "); + } + } + + return builder.toString(); + } + + private String buildDelimAttSteps(String[][] list) { + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i <= list.length - 1; i++) { + String attStepName = list[i][0].trim(); + String attStepDesc = list[i][1].trim(); + builder.append("Name: ").append(attStepName).append(" Description: ").append(attStepDesc); + + if (i < list.length - 1) { + builder.append(" | "); + } + } + + return builder.toString(); + } + + private String[][] extractAttStepData(String[] attackSteps) { + String[][] attStepData = new String[attackSteps.length][2]; + + for (int i = 0; i < attackSteps.length; i++) { + int periodIndex = attackSteps[i].indexOf('.'); + String attackStepName = attackSteps[i].substring(0, periodIndex); + String attackStepDesc = attackSteps[i].substring(periodIndex + 2); + String[] data = {attackStepName, attackStepDesc}; + attStepData[i] = data; + } + + return attStepData; + } +} diff --git a/src/main/java/ai/AIAttackPatternTree3.java b/src/main/java/ai/AIAttackPatternTree.java similarity index 80% rename from src/main/java/ai/AIAttackPatternTree3.java rename to src/main/java/ai/AIAttackPatternTree.java index 15cd80a2d4c832b002b68993a3cd4e57ca973d59..36d6d3b08bdd66209f8aa40f1960e884a13ebf7c 100644 --- a/src/main/java/ai/AIAttackPatternTree3.java +++ b/src/main/java/ai/AIAttackPatternTree.java @@ -14,8 +14,8 @@ import java.util.*; * * @author Alan Birchler De Allende */ -public class AIAttackPatternTree3 extends AIInteract { - private static final String KNOWLEDGE_ON_JSON_FOR_ROOT = "When you are asked to identify a motive, " + +public class AIAttackPatternTree extends AIInteract { + private final String KNOWLEDGE_ON_JSON_FOR_ROOT = "When you are asked to identify a motive, " + "return the motive formatted as JSON like so: " + "{\"rootattack\": {\"name\": \"NameOfMotive\", \"description\": \"" + "The description of the motive.\"}} " + @@ -24,7 +24,7 @@ public class AIAttackPatternTree3 extends AIInteract { "# Respect: For each word in \"name\", its first letter must be capitalized. " + "# Respect: All words in \"description\" must be separated with spaces."; - private static final String KNOWLEDGE_ON_JSON_FOR_ATTACK_SCEN = "When you are asked to identify all the possible " + + private final String KNOWLEDGE_ON_JSON_FOR_ATTACK_SCEN = "When you are asked to identify all the possible " + "attack scenarios that an attacker would perform to successfully achieve an " + "attack, return them as a JSON specification formatted as follows: " + "{\"attack\": \"NameOfAttack\", \"attackscenarios\": [{\"name\": \"NameOfAttackScenario\", \"description\": \"" + @@ -37,7 +37,7 @@ public class AIAttackPatternTree3 extends AIInteract { "# Respect: If there are no attack scenarios that are able to be identified, have \"attackscenarios\" be " + "an empty array."; - private static final String KNOWLEDGE_ON_JSON_FOR_ATTACK_GROUPS = "When you are asked to group all of the provided " + + private final String KNOWLEDGE_ON_JSON_FOR_ATTACK_GROUPS = "When you are asked to group all of the provided " + "attack scenarios, return them as a JSON specification formatted as follows: " + "{\"attscengroups\": [{\"name\": \"NameOfAttackScenario\", \"operator\": \"OR or AND\", " + "\"groupnumber\": integer} ...]} " + @@ -53,7 +53,7 @@ public class AIAttackPatternTree3 extends AIInteract { "# Respect: The attack scenario groups must contain more than one attack scenario. " + "# Respect: An attack scenario can only belong to one group."; - private static final String KNOWLEDGE_ON_JSON_FOR_ATT_STEPS = "When you are asked to identify all of the steps that " + + private final String KNOWLEDGE_ON_JSON_FOR_ATT_STEPS = "When you are asked to identify all of the steps that " + "that an attacker needs to complete to successfully perform an attack scenario, " + "return them as a JSON specification formatted as follows: " + "{\"attackscenario\": \"NameOfAttackScenario\", \"attacksteps\": [{\"name\": \"NameOfAttackStep\", \"description\": \"" + @@ -68,40 +68,15 @@ public class AIAttackPatternTree3 extends AIInteract { "that an attacker needs to complete last. " + "# Respect: There must be at least two attack steps in \"attacksteps\"."; -// private static final String KNOWLEDGE_ON_JSON_FOR_MITIGATIONS = "When you are asked to identify the mitigations " + -// "that prevent an attacker from performing an attack node, " + -// "return them as a JSON specification formatted as follows: " + -// "{\"mitigations\": [{\"name\": \"NameOfMitigation\", \"description\": \"" + -// "The description of the mitigation and how it prevents an attacker from completing " + -// "its associated attack node.\"} ...]} " + -// "# Respect: All words in the \"name\" of each mitigation must be conjoined together. " + -// "# Respect: There must be no more than forty characters in the \"name\" of each mitigation. " + -// "# Respect: For each word in each mitigation's \"name\", its first letter must be capitalized. " + -// "# Respect: Include what the mitigation is and how it is used to prevent an attacker from " + -// "completing its associated attack node in the same \"description\" key. " + -// "# Respect: If there are no mitigations that can be applied to the provided attack nodes, return the " + -// "following JSON: {\"mitigations\": \"No mitigations were identified.\"} " + -// "# Respect: All words in the \"description\" key must be separated with spaces. "; -// -// private static final String KNOWLEDGE_ON_JSON_FOR_MITI_PAIRS = "When you are asked to identify the " + -// "mitigation that can be applied to an attack node to prevent " + -// "an attacker from performing the attack node, " + -// "return them as a JSON specification formatted as follows: " + -// "{\"mitigationpairings\": [{\"mitigation\": \"NameOfMitigation\", \"attacknode\": \"NameOfAttackNode\"} ...]} " + -// "# Respect: The value of \"mitigation\" should be only one of the names of the given mitigations. " + -// "# Respect: The value of \"attacknode\" should be only one of the names of the given attack nodes. " + -// "# Respect: The value of \"attacknode\" should not be the name of the root attack."; - - private static final String[] KNOWLEDGE_STAGES = { + private final String[] KNOWLEDGE_STAGES = { KNOWLEDGE_ON_JSON_FOR_ROOT, KNOWLEDGE_ON_JSON_FOR_ATTACK_SCEN, KNOWLEDGE_ON_JSON_FOR_ATTACK_GROUPS, KNOWLEDGE_ON_JSON_FOR_ATT_STEPS -// KNOWLEDGE_ON_JSON_FOR_MITIGATIONS, -// KNOWLEDGE_ON_JSON_FOR_MITI_PAIRS }; - private final String[] QUESTION_IDENTIFY_ATD = {"From the provided system specification " + + private final String[] QUESTION_IDENTIFY_ATD = { + "From the provided system specification " + "and using the specified JSON format, identify a motive as to why an attacker would " + "want to exploit the system. Use at least one attack pattern to identify a possible motive. " + "Do respect the JSON format, and provide only JSON (no explanation before or after). \n", @@ -121,17 +96,6 @@ public class AIAttackPatternTree3 extends AIInteract { "Identify all of the attack steps that an attacker needs to conduct to " + "achieve the provided attack scenario. Do respect the JSON format, and " + "provide only JSON (no explanation before or after).\n", - -// "From the provided system specification, attack pattern, and attack nodes " + -// "and using the specified JSON format, " + -// "identify possible mitigations, if there are any, that could prevent an attacker from completing " + -// "an attack node. Do respect the JSON format, and provide only JSON (no explanation before or after).\n", -// -// "From the provided system specification, attack pattern, attack nodes, and mitigations " + -// "and using the specified JSON format, " + -// "identify what mitigation can be applied to which attack node such that the mitigation prevents " + -// "an attacker from performing the attack node. " + -// "Do respect the JSON format, and provide only JSON (no explanation before or after).\n" }; private JSONObject rootAttack; @@ -146,7 +110,7 @@ public class AIAttackPatternTree3 extends AIInteract { private final APTAuditor auditor; private final CAPECTracer tracer; - public AIAttackPatternTree3(AIChatData _chatData) { + public AIAttackPatternTree(AIChatData _chatData) { super(_chatData); atList = new ArrayList<>(); maxRetries = 10; @@ -191,22 +155,10 @@ public class AIAttackPatternTree3 extends AIInteract { } if (_attackPatterns != null && _attackPatterns.length > 0) { - StringBuilder builder = new StringBuilder(); - - for (int i = 0; i <= _attackPatterns.length - 1; i++) { - String attackPatternCleaned = _attackPatterns[i]. - replaceAll("Confidence score: [0-9]+%", ""). - replace("\n", " "). - trim(); - builder.append(attackPatternCleaned); - - if (i < _attackPatterns.length - 1) { - builder.append(" | "); - } - } - - TraceManager.addDev("\nKnowledge added: " + builder); - chatData.aiinterface.addKnowledge("The attack patterns are: " + builder, "ok"); + String attackPatternsString = buildAttackPatternsString(_attackPatterns); + TraceManager.addDev("\nKnowledge added: " + attackPatternsString); + chatData.aiinterface.addKnowledge("The attack patterns are: " + attackPatternsString, + "ok"); } if (previousRootAtts != null && !previousRootAtts.isEmpty()) { @@ -322,11 +274,12 @@ public class AIAttackPatternTree3 extends AIInteract { waitIfConditionTrue(!done); } - TraceManager.addDev("Reached end of AIAttackPatternTree internal request."); atList.add(at); previousRootAttacks = previousRootAttacks + rootAttack + " "; } } + + TraceManager.addDev("Reached end of AIAttackPatternTree internal request."); } private int makeRequest(int stage, String systemSpec, String[] attackPatterns, JSONObject attSc, @@ -461,8 +414,8 @@ public class AIAttackPatternTree3 extends AIInteract { catch (org.json.JSONException e) { TraceManager.addDev("Invalid JSON spec: " + extractJSON() + " because " + e.getMessage() + ": INJECTING ERROR"); errors = new ArrayList<>(); - errors.add("There is an error in your JSON: " + e.getMessage() + ". Probably the JSON spec was incomplete. Do correct it. I need " + - "the full specification at once."); + errors.add("There is an error in your JSON: " + e.getMessage() + ". Probably the JSON spec was incomplete. " + + "Do correct it. I need the full specification at once."); } if (!errors.isEmpty()) { @@ -482,7 +435,7 @@ public class AIAttackPatternTree3 extends AIInteract { return numRetries; } - private static JSONObject findAttackStep(Iterable<JSONArray> attackSteps, String attackStepName) { + private JSONObject findAttackStep(Iterable<JSONArray> attackSteps, String attackStepName) { for (JSONArray attackStepsArray : attackSteps) { for (Object attackStepObj : attackStepsArray) { JSONObject attackStepJSON = (JSONObject) attackStepObj; @@ -496,4 +449,22 @@ public class AIAttackPatternTree3 extends AIInteract { return new JSONObject(); } + + private String buildAttackPatternsString(String[] _attackPatterns) { + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i <= _attackPatterns.length - 1; i++) { + String attackPatternCleaned = _attackPatterns[i]. + replaceAll("Confidence score: [0-9]+%", ""). + replace("\n", " "). + trim(); + builder.append(attackPatternCleaned); + + if (i < _attackPatterns.length - 1) { + builder.append(" | "); + } + } + + return builder.toString(); + } } \ No newline at end of file diff --git a/src/main/java/ai/AIAttackPatternTree1.java b/src/main/java/ai/AIAttackPatternTree1.java deleted file mode 100644 index bd11221c5a6a63bacf5d42416428ab178e04a9b4..0000000000000000000000000000000000000000 --- a/src/main/java/ai/AIAttackPatternTree1.java +++ /dev/null @@ -1,836 +0,0 @@ -package ai; - -import attacktrees.*; -import myutil.TraceManager; - -import org.json.JSONArray; -import org.json.JSONObject; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Class AIUseCaseDiagram - * <p> - * Creation: 19/03/2024 - * - * @author Alan Birchler De Allende - */ -public class AIAttackPatternTree1 extends AIInteract { - private static final String KNOWLEDGE_ON_JSON_FOR_ROOT = "When you are asked to identify the root attack of " + - "an attack pattern and how it can be used to exploit a provided system specification, " + - "return the root attack formatted as JSON like so: " + - "{\"rootattack\": {\"name\": \"NameOfRootAttack\", \"description\": \"" + - "The description of the root attack and how it can be used to exploit the " + - "system specifications.\"}} " + - "# Respect: All words in the \"name\" of the root attack must be conjoined together. " + - "# Respect: There must be no more than forty characters in the \"name\" of the root attack. " + - "# Respect: For each word in the \"name\" of the root attack, its first letter must be capitalized. " + - "# Respect: Include what the root attack is and how it can be used to exploit the system " + - "specification in the same \"description\" key." + - "# Respect: All words in the \"description\" key must be separated with spaces."; - - private static final String KNOWLEDGE_ON_JSON_FOR_ATTACKS = "When you are asked to identify the attack nodes " + - "that an attacker needs to complete to successfully achieve the root attack, " + - "return them as a JSON specification formatted as follows: " + - "{\"attacknodes\": [{\"name\": \"NameOfAttackNode\", \"description\": \"" + - "The description of the attack node and how it brings an attacker closer to the root attack.\"} ...]} " + - "# Respect: All words in the \"name\" of each attack node must be conjoined together. " + - "# Respect: There must be no more than forty characters in the \"name\" of each attack node. " + - "# Respect: For each word in each attack node's \"name\", its first letter must be capitalized. " + - "# Respect: Include what the attack node is and how it is used by an attacker for getting closer " + - "to the root attack in the same \"description\" key. " + - "# Respect: All words in the \"description\" key must be separated with spaces."; - - private static final String KNOWLEDGE_ON_JSON_FOR_ATT_CONNS = "When you are asked to identify connections between the root " + - "attack and the attack nodes, return them as a JSON specification formatted as follows: " + - "{\"attackconnections\": [{\"parentattack\": \"NameOfRootAttack or NameOfAttackNode\", " + - "\"connectiontype\": \"the connection type\", " + - "\"childrenattacks\": [\"NameOfAttackNode\" ...]} ...]} " + - "# Respect: Each attack connection must have at least two children attack nodes. " + - "# Respect: The \"childrenattacks\" array must not contain the root attack. " + - "# Note: There are four types of connections: \"OR\", \"XOR\", \"AND\", and \"SEQUENCE\". " + - "# Note: An \"OR\" connection represents the scenario that among all of the children attacks, " + - "an attacker only needs one of the children to proceed to the parent attack. " + - "# Note: A \"XOR\" connection represents the scenario that among all of the children attacks, " + - "an attacker only needs one and only one of the children to proceed to the parent attack. " + - "# Note: An \"AND\" connection represents the scenario that among all of the children objects, " + - "an attacker needs all children simultaneously to proceed to the parent attack. " + - "# Note: A \"SEQUENCE\" connection represents the scenario that an attacker needs each of the children " + - "objects sequentially to proceed to the parent attack. The first indexed child of a \"SEQUENCE\" " + - "connection is the child that an attacker needs first while the last indexed child is the child that " + - "an attacker needs last. " + - "# Respect: The \"connectiontype\" must only be \"OR\", \"XOR\", \"AND\", or \"SEQUENCE\"."; - - private static final String KNOWLEDGE_ON_JSON_FOR_MITIGATIONS = "When you are asked to identify the mitigations " + - "that prevent an attacker from performing an attack node, " + - "return them as a JSON specification formatted as follows: " + - "{\"mitigations\": [{\"name\": \"NameOfMitigation\", \"description\": \"" + - "The description of the mitigation and how it prevents an attacker from completing " + - "its associated attack node.\"} ...]} " + - "# Respect: All words in the \"name\" of each mitigation must be conjoined together. " + - "# Respect: There must be no more than forty characters in the \"name\" of each mitigation. " + - "# Respect: For each word in each mitigation's \"name\", its first letter must be capitalized. " + - "# Respect: Include what the mitigation is and how it is used to prevent an attacker from " + - "completing its associated attack node in the same \"description\" key. " + - "# Respect: If there are no mitigations that can be applied to the provided attack nodes, return the " + - "following JSON: {\"mitigations\": \"No mitigations were able to be identified.\"} " + - "# Respect: All words in the \"description\" key must be separated with spaces. "; - - private static final String KNOWLEDGE_ON_JSON_FOR_MITI_PAIRS = "When you are asked to identify the " + - "mitigation that can be applied to an attack node to prevent " + - "an attacker from performing the attack node, " + - "return them as a JSON specification formatted as follows: " + - "{\"mitigationpairings\": [{\"mitigation\": \"NameOfMitigation\", \"attacknode\": \"NameOfAttackNode\"} ...]} " + - "# Respect: The value of \"mitigation\" should be only one of the names of the given mitigations. " + - "# Respect: The value of \"attacknode\" should be only one of the names of the given attack nodes. " + - "# Respect: The value of \"attacknode\" should not be the name of the root attack."; - - private static final String[] KNOWLEDGE_STAGES = { - KNOWLEDGE_ON_JSON_FOR_ROOT, - KNOWLEDGE_ON_JSON_FOR_ATTACKS, - KNOWLEDGE_ON_JSON_FOR_ATT_CONNS -// KNOWLEDGE_ON_JSON_FOR_MITIGATIONS, -// KNOWLEDGE_ON_JSON_FOR_MITI_PAIRS - }; - - private final String[] QUESTION_IDENTIFY_ATD = {"From the provided system specification and using the specified " + - "JSON format, identify a possible root attack that an attacker could potentially exploit on the provided " + - "system specification. Do respect the JSON format, and " + - "provide only JSON (no explanation before or after).\n", - - "From the provided system specification and root attack and " + - "using the specified JSON format, identify the attack nodes that an attacker needs to " + - "complete for achieving the root attack. Do respect the JSON format, and " + - "provide only JSON (no explanation before or after).\n", - - "From the provided system specification, root attack, and attack nodes and " + - "using the specified JSON format, identify the connections that illustrate in what order an " + - "attacker needs to complete the attack nodes to achieve the root attack. " + - "Do respect the JSON format, and provide only JSON (no explanation before or after).\n", - - "From the provided system specification and attack nodes " + - "and using the specified JSON format, " + - "identify possible mitigations, if there are any, that could prevent an attacker from completing " + - "an attack node. Do respect the JSON format, and provide only JSON (no explanation before or after).\n", - - "From the provided system specification, attack nodes, and mitigations " + - "and using the specified JSON format, " + - "identify what mitigation can be applied to which attack node such that the mitigation prevents " + - "an attacker from performing the attack node. " + - "Do respect the JSON format, and provide only JSON (no explanation before or after).\n" - }; - - private String rootAttackData; - private String attackNodeData; - private String attConnections; - private String mitigations; - private String mitiPairs; - private AttackTree at; - private ArrayList<AttackTree> atList; - - public AIAttackPatternTree1(AIChatData _chatData) { - super(_chatData); - atList = new ArrayList<>(); - } - - public AttackTree getATDiagram() { - return at; - } - - public void internalRequest() { - String previousRootAttacks = ""; - - for (int i = 0; i < 5; i++) { - at = new AttackTree("", null); - int stage = 0; - String lastQuestion = chatData.lastQuestion.trim(); - String json = ""; - String questionT = QUESTION_IDENTIFY_ATD[stage]; - - initKnowledge(); - makeKnowledge(stage, lastQuestion, previousRootAttacks); - - boolean done = false; - int cpt = 0; - - // actors, use cases and connections - while (!done && cpt < 40) { - cpt++; - boolean ok = makeQuestion(questionT); - - if (!ok) { - done = true; - TraceManager.addDev("Make question failed"); - } - - ArrayList<String> errors = null; - - try { - TraceManager.addDev("\n\nMaking specification from " + chatData.lastAnswer + "\n\n"); - json = extractJSON(); - - if (stage == 0) { - rootAttackData = ""; - - errors = new ArrayList<>(); - rootAttackData = checkRootAttack(json, errors); - TraceManager.addDev("Identified root attack - " + rootAttackData); - - if (rootAttackData.isEmpty()) { - errors.add("You must provide the root attack of " + - "the given attack pattern and how it can be used to " + - "exploit the provided system specification. Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } else if (stage == 1) { - attackNodeData = ""; - - errors = new ArrayList<>(); - attackNodeData = checkAttackNodes(json, errors); - TraceManager.addDev("Identified attack nodes: " + attackNodeData); - - if (attackNodeData.isEmpty()) { - errors.add("You must provide the attack nodes showing how an " + - "attacker uses these nodes to achieve the root attack. " + - "Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } else if (stage == 2) { - attConnections = ""; - - errors = new ArrayList<>(); - attConnections = checkAttackConns(json, errors); - TraceManager.addDev("Identified attack connections: " + attConnections); - - if (attConnections.isEmpty()) { - errors.add("You must provide the connections showing in what order " + - "an attacker needs to complete the attack nodes to achieve the " + - "root attack. Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } else if (stage == 3) { - mitigations = ""; - - errors = new ArrayList<>(); - mitigations = checkMitigations(json, errors); - - TraceManager.addDev("Identified mitigations: " + mitigations); - - if (mitigations.isEmpty()) { - errors.add("You must provide mitigations showing how they prevent " + - "an attacker from performing an attack node. " + - "Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } else if (stage == 4) { - mitiPairs = ""; - - errors = new ArrayList<>(); - mitiPairs = checkMitiPairs(json, errors); - - TraceManager.addDev("Identified mitigation pairings: " + mitiPairs); - - if (mitiPairs.isEmpty()) { - errors.add("You must associate the provided mitigations with a provided attack node " + - "such that the mitigation prevents an attacker from performing the attack node. " + - "Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } - } catch (org.json.JSONException e) { - TraceManager.addDev("Invalid JSON spec: " + extractJSON() + " because " + e.getMessage() + ": INJECTING ERROR"); - errors = new ArrayList<>(); - errors.add("There is an error in your JSON: " + e.getMessage() + ". Probably the JSON spec was incomplete. Do correct it. I need " + - "the full specification at once."); - } - - if ((errors != null) && (!errors.isEmpty())) { - questionT = "Your answer was as follows: " + json + "\n\nYet, it was not correct because of the following errors:"; - // Updating knowledge - for (String s : errors) { - questionT += "\n- " + s; - } - - initKnowledge(); - - if (stage == 0) { - makeKnowledge(stage, lastQuestion, previousRootAttacks); - } - else { - makeKnowledge(stage, lastQuestion, ""); - } - } else { - stage++; - - if (stage == KNOWLEDGE_STAGES.length) { - done = true; - } else { - initKnowledge(); - makeKnowledge(stage, lastQuestion, ""); - questionT = QUESTION_IDENTIFY_ATD[stage]; - - if (stage == 1) { - questionT += "\nThe root attack data is in the following JSON:\n" + rootAttackData.trim() + "\n"; - } else if (stage == 2) { - questionT += "\nThe root attack data is in the following JSON:\n" + rootAttackData.trim() + "\n"; - questionT += "\nThe data of the attack nodes are in the following JSON:\n" + - attackNodeData.trim() + "\n"; - } else if (stage == 3) { - questionT += "\nThe data of the attack nodes are in the following JSON:\n" + attackNodeData.trim(); - } else if (stage == 4) { - if (mitigations.toLowerCase().contains("no mitigations were able to be identified")) { - done = true; - } else { - questionT += "\nThe data of the attack nodes are in the following JSON:\n" + attackNodeData.trim(); - questionT += "\nThe data of the mitigations are in the following JSON:\n" + mitigations.trim(); - } - } - } - } - - waitIfConditionTrue(!done && cpt < 20); - cpt++; - } - - TraceManager.addDev("Reached end of AIAttackPatternTree internal request cpt=" + cpt); - atList.add(at); - previousRootAttacks = previousRootAttacks + rootAttackData + " "; - } - } - - public Object applyAnswer(Object input) { return atList; } - - private void initKnowledge() { - chatData.aiinterface.clearKnowledge(); - } - - private void makeKnowledge(int stage, String _spec, String previousRootAttacks) { - String [] know = KNOWLEDGE_STAGES[stage].split("#"); - for(String s: know) { - TraceManager.addDev("\nKnowledge added: " + s); - chatData.aiinterface.addKnowledge(s, "ok"); - } - - if (_spec != null) { - TraceManager.addDev("\nKnowledge added: " + _spec); - chatData.aiinterface.addKnowledge("The system specification is: " + _spec, "ok"); - } - - if (!previousRootAttacks.equals("")) { - TraceManager.addDev("\nKnowledge added: " + previousRootAttacks); - chatData.aiinterface.addKnowledge( - "Identify a root attack other than the root attacks in the following JSONs: " + previousRootAttacks, - "ok"); - - } - } - - private String checkRootAttack(String _spec, Collection<String> _errors) throws org.json.JSONException { - if (_spec == null) { - _errors.add("No \"rootattack\" object in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONObject rootAttackJSON = mainObject.getJSONObject("rootattack"); - - if (rootAttackJSON == null) { - TraceManager.addDev("No \"rootattack\" array in json"); - _errors.add("No \"rootattack\" array in json"); - - return ""; - } - else { - _errors.addAll(checkAttackSyntax(rootAttackJSON)); - } - - if (_errors.isEmpty()) { - Attack rootAttack = new Attack("", null); - rootAttack.setRoot(true); - rootAttack.setName(rootAttackJSON.getString("name")); - rootAttack.setDescription(rootAttackJSON.getString("description")); - at.addAttack(rootAttack); - - return _spec; - } - else { - return ""; - } - } - - private String checkAttackNodes(String _spec, Collection<String> _errors) throws org.json.JSONException { - if (_spec == null) { - _errors.add("No \"attacknodes\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONArray attackNodesJSON = mainObject.getJSONArray("attacknodes"); - - if (attackNodesJSON == null) { - TraceManager.addDev("No \"attacknodes\" array in json"); - _errors.add("No \"attacknodes\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < attackNodesJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkAttackSyntax(attackNodesJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { - for (int i = 0; i < attackNodesJSON.length(); i++) { - JSONObject attackNode = attackNodesJSON.getJSONObject(i); - - Attack attack = new Attack("", null); - attack.setName(attackNode.getString("name")); - attack.setDescription(attackNode.getString("description")); - at.addAttack(attack); - } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkAttackSyntax(JSONObject attack) { - ArrayList<String> errors = new ArrayList<>(); - - if (attack.length() != 2) { - errors.add("The attack object should only contain \"name\" and \"description\""); - - return errors; - } - - String name = attack.getString("name"); - String description = attack.getString("description"); - - if (name == null) { - errors.add("Attack has no name"); - } - else if (!name.matches("[a-zA-Z0-9]+")) { - errors.add(name + " must only contain alphanumeric characters"); - } - else if (name.length() > 40) { - errors.add(name + " must only contain forty characters max"); - } - else if (description == null) { - errors.add("Attack has no description"); - } - else if (!description.contains(" ")) { - errors.add("The words in \"description\" must be separated with spaces."); - } - - return errors; - } - - private String checkAttackConns(String _spec, Collection<String> _errors) throws org.json.JSONException { - if (_spec == null) { - _errors.add("No \"attackconnections\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONArray attackConnsJSON = mainObject.getJSONArray("attackconnections"); - - if (attackConnsJSON == null) { - TraceManager.addDev("No \"attackconnections\" array in json"); - _errors.add("No \"attackconnections\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < attackConnsJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkAttConnSyntax(attackConnsJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { - AttackNode connection = null; - - for (int i = 0; i < attackConnsJSON.length(); i++) { - JSONObject attackConn = attackConnsJSON.getJSONObject(i); - String parentAttackName = attackConn.getString("parentattack"); - String connectionType = attackConn.getString("connectiontype"); - JSONArray childrenAttackNames = attackConn.getJSONArray("childrenattacks"); - List<Attack> attackList = at.getAttacks(); - - switch(connectionType) { - case "OR": - connection = new ORNode("", null); - break; - case "XOR": - connection = new XORNode("", null); - break; - case "AND": - connection = new ANDNode("", null); - break; - default: - connection = new SequenceNode("", null); - break; - } - - Attack parentAttack = findAttack(parentAttackName, attackList, true); - connection.setResultingAttack(parentAttack); - int attackValue = 1; - - for (Object childAttackName : childrenAttackNames) { - String childAttackNameString = (String) childAttackName; - Attack childAttack = findAttack(childAttackNameString, attackList, false); - - connection.addInputAttack(childAttack, attackValue); - - attackValue++; - } - - at.addNode(connection); - } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkAttConnSyntax(JSONObject attackConn) { - ArrayList<String> errors = new ArrayList<>(); - - if (attackConn.length() != 3) { - errors.add("The attack connection object should only contain " + - "\"parentattack\", \"connectiontype\", and \"childrenattacks\"."); - - return errors; - } - - String parentAttack = attackConn.getString("parentattack"); - String connectionType = attackConn.getString("connectiontype"); - JSONArray childrenAttacks = attackConn.getJSONArray("childrenattacks"); - - if (parentAttack == null) { - errors.add("Connection has no parentattack"); - return errors; - } - - Attack foundParentAttack = findAttack(parentAttack, at.getAttacks(), true); - - if (foundParentAttack == null) { - errors.add(parentAttack + " is not the name of any provided root attack " + - "or attack node. Ensure that \"parentattack\" is the name of either the provided root" + - "attack or one of the attack nodes."); - } - - if (connectionType == null) { - errors.add("Connection has no connectiontype"); - } - else if (!connectionType.equals("OR") && - !connectionType.equals("XOR") && - !connectionType.equals("AND") && - !connectionType.equals("SEQUENCE")) { - errors.add("Ensure that connectiontype is one of the following types: \"OR\", \"XOR\", \"AND\", " + - "or \"SEQUENCE\""); - } - - if (childrenAttacks == null) { - errors.add("Connection has no childrenattacks"); - return errors; - } -// else if (childrenAttacks.length() <= 1) { -// errors.add("Connection has only one child attack. Please ensure that all connections have at least two " + -// "children attacks."); -// return errors; -// } - - for (Object childAttack : childrenAttacks) { - String childAttackString = (String) childAttack; - Attack foundChildAttack = findAttack(childAttackString, at.getAttacks(), false); - - if (foundChildAttack == null) { - errors.add(childAttackString + " is not the name of any provided attack node. " + - "Ensure that all values in the \"childrenattacks\" array are one of the names in " + - "the \"attacknodes\" JSON array."); - } - } - - return errors; - } - - private Attack findAttack(String attackNodeName, List<Attack> attackNodeList, boolean checkRoot) { - Attack attack = null; - int i; - - if (!checkRoot) { - i = 1; - } - else { - i = 0; - } - - while (i < attackNodeList.size()) { - attack = attackNodeList.get(i); - - if (attackNodeName.equals(attack.getName())) { - return attack; - } - - i++; - } - - return null; - } - - private String checkMitigations(String _spec, Collection<String> _errors) - throws org.json.JSONException { - String mitigationList = ""; - - if (_spec == null) { - _errors.add("No \"mitigations\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - Object mitigations = mainObject.get("mitigations"); - - JSONArray mitigationsJSON = (JSONArray) mitigations; - - if (mitigationsJSON == null) { - TraceManager.addDev("No \"mitigations\" array in json"); - _errors.add("No \"mitigations\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < mitigationsJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkMitigationSyntax(mitigationsJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { -// for (int i = 0; i < mitigationsJSON.length(); i++) { -// JSONObject attackStep = mitigationsJSON.getJSONObject(i); -// -// Attack attack = new Attack("", null); -// attack.setName(attackStep.getString("name")); -// attack.setDescription(attackStep.getString("description")); -// at.addAttack(attack); -// -// mitigations += attack.toString(); -// } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkMitigationSyntax(JSONObject mitigation) { - ArrayList<String> errors = new ArrayList<>(); - - if (mitigation.length() != 2) { - errors.add("The mitigation object should only contain \"name\" and \"description\""); - - return errors; - } - - String name = mitigation.getString("name"); - String description = mitigation.getString("description"); -// String attackNodeName = mitigation.getString("attacknode"); - - if (name == null) { - errors.add("Mitigation has no name"); - } - else if (!name.matches("[a-zA-Z0-9]+")) { - errors.add(name + " must only contain alphanumeric characters"); - } - else if (name.length() > 40) { - errors.add(name + " must only contain forty characters max"); - } - else if (description == null) { - errors.add("Mitigation has no description"); - } - else if (!description.contains(" ")) { - errors.add("The words in \"description\" must be separated with spaces."); - } -// else if (attackNodeName == null) { -// errors.add("Mitigation has no associated attack node"); -// } -// else if (attackNodeName.equals(at.getAttacks().get(0).getName())) { -// errors.add(attackNodeName + " is the name of the root attack. " + -// "Do not associate mitigations with the root attack"); -// } - -// ArrayList<Attack> attackNodeList = at.getAttacks(); -// ArrayList<String> differentAttackNames = getAllDiffAttackNodeNames(attackNodeName, attackNodeList); -// if (differentAttackNames.size() == at.getAttacks().size() - 1) { -// StringBuilder builder = new StringBuilder( -// attackNodeName + " in \"attacknode\" is not the name of any of the provided attack nodes. " + -// "The value of \"attacknode\" can only be one of the following names: "); -// -// for (int i = 0; i < differentAttackNames.size(); i++) { -// if (i != differentAttackNames.size() - 1) { -// builder.append("\"").append(differentAttackNames.get(i)).append("\"").append(", "); -// } -// else { -// builder.append("or ").append("\"").append(differentAttackNames.get(i)).append("\""); -// } -// } - -// errors.add(builder.toString()); -// } - - return errors; - } - - private String checkMitiPairs(String _spec, Collection<String> _errors) - throws org.json.JSONException { - String mitigationList = ""; - - if (_spec == null) { - _errors.add("No \"mitigationpairings\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONArray mitigationPairingsJSON = mainObject.getJSONArray("mitigationpairings"); - - if (mitigationPairingsJSON == null) { - TraceManager.addDev("No \"mitigationpairings\" array in json"); - _errors.add("No \"mitigationpairings\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < mitigationPairingsJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkMitiPairingSyntax(mitigationPairingsJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { -// for (int i = 0; i < mitigationsJSON.length(); i++) { -// JSONObject attackStep = mitigationsJSON.getJSONObject(i); -// -// Attack attack = new Attack("", null); -// attack.setName(attackStep.getString("name")); -// attack.setDescription(attackStep.getString("description")); -// at.addAttack(attack); -// -// mitigations += attack.toString(); -// } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkMitiPairingSyntax(JSONObject mitigation) { - ArrayList<String> errors = new ArrayList<>(); - - if (mitigation.length() != 2) { - errors.add("The mitigation pairing object should only contain \"mitigation\" and \"attacknode\""); - - return errors; - } - - String mitigationName = mitigation.getString("mitigation"); - String attackNodeName = mitigation.getString("attacknode"); - - if (mitigationName == null) { - errors.add("Pairing has no mitigation"); - } - else if (attackNodeName == null) { - errors.add("Pairing has no attack node"); - } - - Attack foundAttackNode = findAttack(attackNodeName, at.getAttacks(), false); - - if (foundAttackNode == null) { - errors.add(attackNodeName + " is not the name of any provided attack node. " + - "Ensure that the value of \"attacknode\" is the name of an attack node."); - } - - return errors; - } -} diff --git a/src/main/java/ai/AIAttackPatternTree2.java b/src/main/java/ai/AIAttackPatternTree2.java deleted file mode 100644 index 0d406ded1bd8d2ba294eae90ad371c71b02326be..0000000000000000000000000000000000000000 --- a/src/main/java/ai/AIAttackPatternTree2.java +++ /dev/null @@ -1,853 +0,0 @@ -package ai; - -import attacktrees.*; -import myutil.TraceManager; -import org.json.JSONArray; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Class AIUseCaseDiagram - * <p> - * Creation: 19/03/2024 - * - * @author Alan Birchler De Allende - */ -public class AIAttackPatternTree2 extends AIInteract { - private static final String KNOWLEDGE_ON_JSON_FOR_ROOT = "When you are asked to identify the root attack of " + - "an attack pattern and how it can be used to exploit a provided system specification, " + - "return the root attack formatted as JSON like so: " + - "{\"rootattack\": {\"name\": \"NameOfRootAttack\", \"description\": \"" + - "The description of the root attack and how it can be used to exploit the " + - "system specifications.\"}} " + - "# Respect: All words in the \"name\" of the root attack must be conjoined together. " + - "# Respect: There must be no more than forty characters in the \"name\" of the root attack. " + - "# Respect: For each word in the \"name\" of the root attack, its first letter must be capitalized. " + - "# Respect: Include what the root attack is and how it can be used to exploit the system " + - "specification in the same \"description\" key." + - "# Respect: All words in the \"description\" key must be separated with spaces."; - - private static final String KNOWLEDGE_ON_JSON_FOR_ATTACKS = "When you are asked to identify the attack nodes " + - "that an attacker needs to complete to successfully achieve the root attack, " + - "return them as a JSON specification formatted as follows: " + - "{\"attacknodes\": [{\"name\": \"NameOfAttackNode\", \"description\": \"" + - "The description of the attack node and how it brings an attacker closer to the root attack.\"} ...]} " + - "# Respect: All words in the \"name\" of each attack node must be conjoined together. " + - "# Respect: There must be no more than forty characters in the \"name\" of each attack node. " + - "# Respect: For each word in each attack node's \"name\", its first letter must be capitalized. " + - "# Respect: Include what the attack node is and how it is used by an attacker for getting closer " + - "to the root attack in the same \"description\" key. " + - "# Respect: All words in the \"description\" key must be separated with spaces."; - - private static final String KNOWLEDGE_ON_JSON_FOR_ATT_CONNS = "When you are asked to identify connections between the root " + - "attack and the attack nodes, return them as a JSON specification formatted as follows: " + - "{\"attackconnections\": [{\"parentattack\": \"NameOfRootAttack or NameOfAttackNode\", " + - "\"connectiontype\": \"the connection type\", " + - "\"childrenattacks\": [\"NameOfAttackNode\" ...]} ...]} " + - "# Respect: Each attack connection must have at least two children attack nodes. " + - "# Respect: The \"childrenattacks\" array must not contain the root attack. " + - "# Note: There are four types of connections: \"OR\", \"XOR\", \"AND\", and \"SEQUENCE\". " + - "# Note: An \"OR\" connection represents the scenario that among all of the children attacks, " + - "an attacker only needs one of the children to proceed to the parent attack. " + - "# Note: A \"XOR\" connection represents the scenario that among all of the children attacks, " + - "an attacker only needs one and only one of the children to proceed to the parent attack. " + - "# Note: An \"AND\" connection represents the scenario that among all of the children objects, " + - "an attacker needs all children simultaneously to proceed to the parent attack. " + - "# Note: A \"SEQUENCE\" connection represents the scenario that an attacker needs each of the children " + - "objects sequentially to proceed to the parent attack. The first indexed child of a \"SEQUENCE\" " + - "connection is the child that an attacker needs first while the last indexed child is the child that " + - "an attacker needs last. " + - "# Respect: The \"connectiontype\" must only be \"OR\", \"XOR\", \"AND\", or \"SEQUENCE\"."; - - private static final String KNOWLEDGE_ON_JSON_FOR_MITIGATIONS = "When you are asked to identify the mitigations " + - "that prevent an attacker from performing an attack node, " + - "return them as a JSON specification formatted as follows: " + - "{\"mitigations\": [{\"name\": \"NameOfMitigation\", \"description\": \"" + - "The description of the mitigation and how it prevents an attacker from completing " + - "its associated attack node.\"} ...]} " + - "# Respect: All words in the \"name\" of each mitigation must be conjoined together. " + - "# Respect: There must be no more than forty characters in the \"name\" of each mitigation. " + - "# Respect: For each word in each mitigation's \"name\", its first letter must be capitalized. " + - "# Respect: Include what the mitigation is and how it is used to prevent an attacker from " + - "completing its associated attack node in the same \"description\" key. " + - "# Respect: If there are no mitigations that can be applied to the provided attack nodes, return the " + - "following JSON: {\"mitigations\": \"No mitigations were able to be identified.\"} " + - "# Respect: All words in the \"description\" key must be separated with spaces. "; - - private static final String KNOWLEDGE_ON_JSON_FOR_MITI_PAIRS = "When you are asked to identify the " + - "mitigation that can be applied to an attack node to prevent " + - "an attacker from performing the attack node, " + - "return them as a JSON specification formatted as follows: " + - "{\"mitigationpairings\": [{\"mitigation\": \"NameOfMitigation\", \"attacknode\": \"NameOfAttackNode\"} ...]} " + - "# Respect: The value of \"mitigation\" should be only one of the names of the given mitigations. " + - "# Respect: The value of \"attacknode\" should be only one of the names of the given attack nodes. " + - "# Respect: The value of \"attacknode\" should not be the name of the root attack."; - - private static final String[] KNOWLEDGE_STAGES = { - KNOWLEDGE_ON_JSON_FOR_ROOT, - KNOWLEDGE_ON_JSON_FOR_ATTACKS, - KNOWLEDGE_ON_JSON_FOR_ATT_CONNS -// KNOWLEDGE_ON_JSON_FOR_MITIGATIONS, -// KNOWLEDGE_ON_JSON_FOR_MITI_PAIRS - }; - - private final String[] QUESTION_IDENTIFY_ATD = {"From the provided system specification and attack pattern " + - "and using the specified JSON format, identify what " + - "the root attack is of the attack pattern and how it can be used to exploit the system specification. " + - "Do respect the JSON format, and " + - "provide only JSON (no explanation before or after).\n", - - "From the provided system specification, attack pattern, and root attack and " + - "using the specified JSON format, identify the attack nodes that an attacker needs to " + - "complete for achieving the root attack. Do respect the JSON format, and " + - "provide only JSON (no explanation before or after).\n", - - "From the provided system specification, attack pattern, root attack, and attack nodes and " + - "using the specified JSON format, identify the connections that illustrate in what order an " + - "attacker needs to complete the attack nodes to achieve the root attack. " + - "Do respect the JSON format, and provide only JSON (no explanation before or after).\n", - - "From the provided system specification, attack pattern, and attack nodes " + - "and using the specified JSON format, " + - "identify possible mitigations, if there are any, that could prevent an attacker from completing " + - "an attack node. Do respect the JSON format, and provide only JSON (no explanation before or after).\n", - - "From the provided system specification, attack pattern, attack nodes, and mitigations " + - "and using the specified JSON format, " + - "identify what mitigation can be applied to which attack node such that the mitigation prevents " + - "an attacker from performing the attack node. " + - "Do respect the JSON format, and provide only JSON (no explanation before or after).\n" - }; - - private String rootAttackData; - private String attackNodeData; - private String attConnections; - private String mitigations; - private String mitiPairs; - private AttackTree at; - - public AIAttackPatternTree2(AIChatData _chatData) { - super(_chatData); - - at = new AttackTree("", null); - } - - public AttackTree getATDiagram() { - return at; - } - - public void internalRequest() { - at = new AttackTree("", null); - int stage = 0; - String lastQuestion = chatData.lastQuestion.trim(); - String[] data = lastQuestion.split("\n\n"); - String systemSpec = data[0]; - String attackPattern = data[1]; - boolean apContainsMitigations = attackPattern.contains("Mitigations:"); - - String json = ""; - - String questionT = QUESTION_IDENTIFY_ATD[stage]; - - initKnowledge(); - makeKnowledge(stage, systemSpec, attackPattern); - - boolean done = false; - int cpt = 0; - - // actors, use cases and connections - while (!done && cpt < 40) { - cpt++; - boolean ok = makeQuestion(questionT); - - if (!ok) { - done = true; - TraceManager.addDev("Make question failed"); - } - - ArrayList<String> errors = null; - - try { - TraceManager.addDev("\n\nMaking specification from " + chatData.lastAnswer + "\n\n"); - json = extractJSON(); - - if (stage == 0) { - rootAttackData = ""; - - errors = new ArrayList<>(); - rootAttackData = checkRootAttack(json, errors); - TraceManager.addDev("Identified root attack - " + rootAttackData); - - if (rootAttackData.isEmpty()) { - errors.add("You must provide the root attack of " + - "the given attack pattern and how it can be used to " + - "exploit the provided system specification. Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } - else if (stage == 1) { - attackNodeData = ""; - - errors = new ArrayList<>(); - attackNodeData = checkAttackNodes(json, errors); - TraceManager.addDev("Identified attack nodes: " + attackNodeData); - - if (attackNodeData.isEmpty()) { - errors.add("You must provide the attack nodes showing how an " + - "attacker uses these nodes to achieve the root attack. " + - "Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } - else if (stage == 2) { - attConnections = ""; - - errors = new ArrayList<>(); - attConnections = checkAttackConns(json, errors); - TraceManager.addDev("Identified attack connections: " + attConnections); - - if (attConnections.isEmpty()) { - errors.add("You must provide the connections showing in what order " + - "an attacker needs to complete the attack nodes to achieve the " + - "root attack. Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } - else if (stage == 3) { - mitigations = ""; - - errors = new ArrayList<>(); - mitigations = checkMitigations(json, errors, apContainsMitigations); - - TraceManager.addDev("Identified mitigations: " + mitigations); - - if (mitigations.isEmpty()) { - errors.add("You must provide mitigations showing how they prevent " + - "an attacker from performing an attack node. " + - "Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } - else if (stage == 4) { - mitiPairs = ""; - - errors = new ArrayList<>(); - mitiPairs = checkMitiPairs(json, errors); - - TraceManager.addDev("Identified mitigation pairings: " + mitiPairs); - - if (mitiPairs.isEmpty()) { - errors.add("You must associate the provided mitigations with a provided attack node " + - "such that the mitigation prevents an attacker from performing the attack node. " + - "Do respect the JSON format, and " + - "provide only JSON (no explanation before or after)."); - } - } - } catch (org.json.JSONException e) { - TraceManager.addDev("Invalid JSON spec: " + extractJSON() + " because " + e.getMessage() + ": INJECTING ERROR"); - errors = new ArrayList<>(); - errors.add("There is an error in your JSON: " + e.getMessage() + ". Probably the JSON spec was incomplete. Do correct it. I need " + - "the full specification at once."); - } - - if ((errors != null) && (!errors.isEmpty())) { - questionT = "Your answer was as follows: " + json + "\n\nYet, it was not correct because of the following errors:"; - // Updating knowledge - for (String s : errors) { - questionT += "\n- " + s; - } - - initKnowledge(); - makeKnowledge(stage, systemSpec, attackPattern); - } else { - stage++; - - if (stage == KNOWLEDGE_STAGES.length) { - done = true; - } else { - initKnowledge(); - makeKnowledge(stage, systemSpec, attackPattern); - questionT = QUESTION_IDENTIFY_ATD[stage]; - - if (stage == 1) { - questionT += "\nThe root attack data is in the following JSON:\n" + rootAttackData.trim() + "\n"; - } - else if (stage == 2) { - questionT += "\nThe root attack data is in the following JSON:\n" + rootAttackData.trim() + "\n"; - questionT += "\nThe data of the attack nodes are in the following JSON:\n" + - attackNodeData.trim() + "\n"; - } - else if (stage == 3) { - questionT += "\nThe data of the attack nodes are in the following JSON:\n" + attackNodeData.trim(); - } - else if (stage == 4) { - if (mitigations.toLowerCase().contains("no mitigations were able to be identified")) { - done = true; - } - else { - questionT += "\nThe data of the attack nodes are in the following JSON:\n" + attackNodeData.trim(); - questionT += "\nThe data of the mitigations are in the following JSON:\n" + mitigations.trim(); - } - } - } - } - - waitIfConditionTrue(!done && cpt < 20); - - cpt++; - } - - TraceManager.addDev("Reached end of AIAttackPatternTree internal request cpt=" + cpt); - } - - public Object applyAnswer(Object input) { - return at; - } - - private void initKnowledge() { - chatData.aiinterface.clearKnowledge(); - } - - private void makeKnowledge(int stage, String _spec, String _attackPattern) { - String [] know = KNOWLEDGE_STAGES[stage].split("#"); - for(String s: know) { - TraceManager.addDev("\nKnowledge added: " + s); - chatData.aiinterface.addKnowledge(s, "ok"); - } - - if (_spec != null) { - TraceManager.addDev("\nKnowledge added: " + _spec); - chatData.aiinterface.addKnowledge("The system specification is: " + _spec, "ok"); - } - - if (_attackPattern != null) { - TraceManager.addDev("\nKnowledge added: " + _attackPattern); - chatData.aiinterface.addKnowledge("The attack pattern is: " + _attackPattern, "ok"); - } - } - - private String checkRootAttack(String _spec, Collection<String> _errors) throws org.json.JSONException { - if (_spec == null) { - _errors.add("No \"rootattack\" object in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONObject rootAttackJSON = mainObject.getJSONObject("rootattack"); - - if (rootAttackJSON == null) { - TraceManager.addDev("No \"rootattack\" array in json"); - _errors.add("No \"rootattack\" array in json"); - - return ""; - } - else { - _errors.addAll(checkAttackSyntax(rootAttackJSON)); - } - - if (_errors.isEmpty()) { - Attack rootAttack = new Attack("", null); - rootAttack.setRoot(true); - rootAttack.setName(rootAttackJSON.getString("name")); - rootAttack.setDescription(rootAttackJSON.getString("description")); - at.addAttack(rootAttack); - - return _spec; - } - else { - return ""; - } - } - - private String checkAttackNodes(String _spec, Collection<String> _errors) throws org.json.JSONException { - if (_spec == null) { - _errors.add("No \"attacknodes\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONArray attackNodesJSON = mainObject.getJSONArray("attacknodes"); - - if (attackNodesJSON == null) { - TraceManager.addDev("No \"attacknodes\" array in json"); - _errors.add("No \"attacknodes\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < attackNodesJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkAttackSyntax(attackNodesJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { - for (int i = 0; i < attackNodesJSON.length(); i++) { - JSONObject attackNode = attackNodesJSON.getJSONObject(i); - - Attack attack = new Attack("", null); - attack.setName(attackNode.getString("name")); - attack.setDescription(attackNode.getString("description")); - at.addAttack(attack); - } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkAttackSyntax(JSONObject attack) { - ArrayList<String> errors = new ArrayList<>(); - - if (attack.length() != 2) { - errors.add("The attack object should only contain \"name\" and \"description\""); - - return errors; - } - - String name = attack.getString("name"); - String description = attack.getString("description"); - - if (name == null) { - errors.add("Attack has no name"); - } - else if (!name.matches("[a-zA-Z0-9]+")) { - errors.add(name + " must only contain alphanumeric characters"); - } - else if (name.length() > 40) { - errors.add(name + " must only contain forty characters max"); - } - else if (description == null) { - errors.add("Attack has no description"); - } - else if (!description.contains(" ")) { - errors.add("The words in \"description\" must be separated with spaces."); - } - - return errors; - } - - private String checkAttackConns(String _spec, Collection<String> _errors) throws org.json.JSONException { - if (_spec == null) { - _errors.add("No \"attackconnections\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONArray attackConnsJSON = mainObject.getJSONArray("attackconnections"); - - if (attackConnsJSON == null) { - TraceManager.addDev("No \"attackconnections\" array in json"); - _errors.add("No \"attackconnections\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < attackConnsJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkAttConnSyntax(attackConnsJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { - AttackNode connection = null; - - for (int i = 0; i < attackConnsJSON.length(); i++) { - JSONObject attackConn = attackConnsJSON.getJSONObject(i); - String parentAttackName = attackConn.getString("parentattack"); - String connectionType = attackConn.getString("connectiontype"); - JSONArray childrenAttackNames = attackConn.getJSONArray("childrenattacks"); - List<Attack> attackList = at.getAttacks(); - - switch(connectionType) { - case "OR": - connection = new ORNode("", null); - break; - case "XOR": - connection = new XORNode("", null); - break; - case "AND": - connection = new ANDNode("", null); - break; - default: - connection = new SequenceNode("", null); - break; - } - - Attack parentAttack = findAttack(parentAttackName, attackList, true); - connection.setResultingAttack(parentAttack); - int attackValue = 1; - - for (Object childAttackName : childrenAttackNames) { - String childAttackNameString = (String) childAttackName; - Attack childAttack = findAttack(childAttackNameString, attackList, false); - - connection.addInputAttack(childAttack, attackValue); - - attackValue++; - } - - at.addNode(connection); - } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkAttConnSyntax(JSONObject attackConn) { - ArrayList<String> errors = new ArrayList<>(); - - if (attackConn.length() != 3) { - errors.add("The attack connection object should only contain " + - "\"parentattack\", \"connectiontype\", and \"childrenattacks\"."); - - return errors; - } - - String parentAttack = attackConn.getString("parentattack"); - String connectionType = attackConn.getString("connectiontype"); - JSONArray childrenAttacks = attackConn.getJSONArray("childrenattacks"); - - if (parentAttack == null) { - errors.add("Connection has no parentattack"); - return errors; - } - - Attack foundParentAttack = findAttack(parentAttack, at.getAttacks(), true); - - if (foundParentAttack == null) { - errors.add(parentAttack + " is not the name of any provided root attack " + - "or attack node. Ensure that \"parentattack\" is the name of either the provided root" + - "attack or one of the attack nodes."); - } - - if (connectionType == null) { - errors.add("Connection has no connectiontype"); - } - else if (!connectionType.equals("OR") && - !connectionType.equals("XOR") && - !connectionType.equals("AND") && - !connectionType.equals("SEQUENCE")) { - errors.add("Ensure that connectiontype is one of the following types: \"OR\", \"XOR\", \"AND\", " + - "or \"SEQUENCE\""); - } - - if (childrenAttacks == null) { - errors.add("Connection has no childrenattacks"); - return errors; - } -// else if (childrenAttacks.length() <= 1) { -// errors.add("Connection has only one child attack. Please ensure that all connections have at least two " + -// "children attacks."); -// return errors; -// } - - for (Object childAttack : childrenAttacks) { - String childAttackString = (String) childAttack; - Attack foundChildAttack = findAttack(childAttackString, at.getAttacks(), false); - - if (foundChildAttack == null) { - errors.add(childAttackString + " is not the name of any provided attack node. " + - "Ensure that all values in the \"childrenattacks\" array are one of the names in " + - "the \"attacknodes\" JSON array."); - } - } - - return errors; - } - - private Attack findAttack(String attackNodeName, List<Attack> attackNodeList, boolean checkRoot) { - Attack attack = null; - int i; - - if (!checkRoot) { - i = 1; - } - else { - i = 0; - } - - while (i < attackNodeList.size()) { - attack = attackNodeList.get(i); - - if (attackNodeName.equals(attack.getName())) { - return attack; - } - - i++; - } - - return null; - } - - private String checkMitigations(String _spec, Collection<String> _errors, boolean containsMiti) - throws org.json.JSONException { - String mitigationList = ""; - - if (_spec == null) { - _errors.add("No \"mitigations\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - Object mitigations = mainObject.get("mitigations"); - - if (mitigations instanceof String && - mitigations.equals("No mitigations were able to be identified.")) { - - if (!containsMiti) { - return (String) mitigations; - } - else { - _errors.add("You specified that there were no mitigations able to be identified. Yet, the " + - "provided attack pattern contains a \"Mitigations:\" section."); - - return ""; - } - } - - JSONArray mitigationsJSON = (JSONArray) mitigations; - - if (mitigationsJSON == null) { - TraceManager.addDev("No \"mitigations\" array in json"); - _errors.add("No \"mitigations\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < mitigationsJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkMitigationSyntax(mitigationsJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { -// for (int i = 0; i < mitigationsJSON.length(); i++) { -// JSONObject attackStep = mitigationsJSON.getJSONObject(i); -// -// Attack attack = new Attack("", null); -// attack.setName(attackStep.getString("name")); -// attack.setDescription(attackStep.getString("description")); -// at.addAttack(attack); -// -// mitigations += attack.toString(); -// } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkMitigationSyntax(JSONObject mitigation) { - ArrayList<String> errors = new ArrayList<>(); - - if (mitigation.length() != 2) { - errors.add("The mitigation object should only contain \"name\" and \"description\""); - - return errors; - } - - String name = mitigation.getString("name"); - String description = mitigation.getString("description"); -// String attackNodeName = mitigation.getString("attacknode"); - - if (name == null) { - errors.add("Mitigation has no name"); - } - else if (!name.matches("[a-zA-Z0-9]+")) { - errors.add(name + " must only contain alphanumeric characters"); - } - else if (name.length() > 40) { - errors.add(name + " must only contain forty characters max"); - } - else if (description == null) { - errors.add("Mitigation has no description"); - } - else if (!description.contains(" ")) { - errors.add("The words in \"description\" must be separated with spaces."); - } -// else if (attackNodeName == null) { -// errors.add("Mitigation has no associated attack node"); -// } -// else if (attackNodeName.equals(at.getAttacks().get(0).getName())) { -// errors.add(attackNodeName + " is the name of the root attack. " + -// "Do not associate mitigations with the root attack"); -// } - -// ArrayList<Attack> attackNodeList = at.getAttacks(); -// ArrayList<String> differentAttackNames = getAllDiffAttackNodeNames(attackNodeName, attackNodeList); -// if (differentAttackNames.size() == at.getAttacks().size() - 1) { -// StringBuilder builder = new StringBuilder( -// attackNodeName + " in \"attacknode\" is not the name of any of the provided attack nodes. " + -// "The value of \"attacknode\" can only be one of the following names: "); -// -// for (int i = 0; i < differentAttackNames.size(); i++) { -// if (i != differentAttackNames.size() - 1) { -// builder.append("\"").append(differentAttackNames.get(i)).append("\"").append(", "); -// } -// else { -// builder.append("or ").append("\"").append(differentAttackNames.get(i)).append("\""); -// } -// } - -// errors.add(builder.toString()); -// } - - return errors; - } - - private String checkMitiPairs(String _spec, Collection<String> _errors) - throws org.json.JSONException { - String mitigationList = ""; - - if (_spec == null) { - _errors.add("No \"mitigationpairings\" array in json"); - - return ""; - } - - int indexStart = _spec.indexOf('{'); - int indexStop = _spec.lastIndexOf('}'); - - if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { - _errors.add("Invalid JSON object (start or stop)"); - - return ""; - } - - String json = _spec.substring(indexStart, indexStop + 1); - - JSONObject mainObject = new JSONObject(json); - JSONArray mitigationPairingsJSON = mainObject.getJSONArray("mitigationpairings"); - - if (mitigationPairingsJSON == null) { - TraceManager.addDev("No \"mitigationpairings\" array in json"); - _errors.add("No \"mitigationpairings\" array in json"); - - return ""; - } - else { - int i = 0; - - while (i < mitigationPairingsJSON.length() && _errors.isEmpty()) - { - _errors.addAll(checkMitiPairingSyntax(mitigationPairingsJSON.getJSONObject(i))); - i++; - } - } - - if (_errors.isEmpty()) { -// for (int i = 0; i < mitigationsJSON.length(); i++) { -// JSONObject attackStep = mitigationsJSON.getJSONObject(i); -// -// Attack attack = new Attack("", null); -// attack.setName(attackStep.getString("name")); -// attack.setDescription(attackStep.getString("description")); -// at.addAttack(attack); -// -// mitigations += attack.toString(); -// } - - return _spec; - } - else { - return ""; - } - } - - private ArrayList<String> checkMitiPairingSyntax(JSONObject mitigation) { - ArrayList<String> errors = new ArrayList<>(); - - if (mitigation.length() != 2) { - errors.add("The mitigation pairing object should only contain \"mitigation\" and \"attacknode\""); - - return errors; - } - - String mitigationName = mitigation.getString("mitigation"); - String attackNodeName = mitigation.getString("attacknode"); - - if (mitigationName == null) { - errors.add("Pairing has no mitigation"); - } - else if (attackNodeName == null) { - errors.add("Pairing has no attack node"); - } - - Attack foundAttackNode = findAttack(attackNodeName, at.getAttacks(), false); - - if (foundAttackNode == null) { - errors.add(attackNodeName + " is not the name of any provided attack node. " + - "Ensure that the value of \"attacknode\" is the name of an attack node."); - } - - return errors; - } -} diff --git a/src/main/java/ai/APTAuditor.java b/src/main/java/ai/APTAuditor.java index d6e347e821ee74d7d9d4992c318ab23fcac5c79b..a9cad0f2dfae43d8e3368aa75f05b0097f6ec2ff 100644 --- a/src/main/java/ai/APTAuditor.java +++ b/src/main/java/ai/APTAuditor.java @@ -318,6 +318,65 @@ public class APTAuditor { } } + public JSONObject checkMitigations(AttackTree at, String _spec, String[][] attackStepNames, Collection<String> _errors) + throws org.json.JSONException { + if (_spec == null) { + _errors.add("No \"mitigations\" object in json"); + + return new JSONObject(); + } + + int indexStart = _spec.indexOf('{'); + int indexStop = _spec.lastIndexOf('}'); + + if ((indexStart == -1) || (indexStop == -1) || (indexStart > indexStop)) { + _errors.add("Invalid JSON object (start or stop)"); + + return new JSONObject(); + } + + String json = _spec.substring(indexStart, indexStop + 1); + + JSONObject mainObject = new JSONObject(json); + + if (mainObject.length() != 1) { + TraceManager.addDev("Extra key/value mappings added in outer json object"); + _errors.add("\"mitigations\" should be the only key/value mapping in " + + "the outer json object"); + + return new JSONObject(); + } + + JSONArray mitigationsJSON = mainObject.getJSONArray("mitigations"); + + if (mitigationsJSON == null) { + TraceManager.addDev("No \"mitigations\" array in json"); + _errors.add("No \"mitigations\" array in json"); + + return new JSONObject(); + } + else if (mitigationsJSON.isEmpty()) { + return new JSONObject(); + } + + int i = 0; + + while (i < mitigationsJSON.length() && _errors.isEmpty()) + { + _errors.addAll(checkMitiSyntax(mitigationsJSON.getJSONObject(i), attackStepNames)); + i++; + } + + if (_errors.isEmpty()) { + createMitigations(at, mitigationsJSON); + + return mainObject; + } + else { + return new JSONObject(); + } + } + private ArrayList<String> checkAttackScenSyntax(AttackTree at, JSONObject attackScen) { ArrayList<String> errors = new ArrayList<>(); @@ -596,6 +655,54 @@ public class APTAuditor { return errors; } + private ArrayList<String> checkMitiSyntax(JSONObject mitigation, String[][] attackStepNames) { + ArrayList<String> errors = new ArrayList<>(); + + if (mitigation.length() != 3) { + errors.add("The \"mitigation\" object should only contain \"name\", \"description\", " + + "and \"attacksteps\"."); + + return errors; + } + + String name = mitigation.getString("name"); + String description = mitigation.getString("description"); + JSONArray relatedAttSteps = mitigation.getJSONArray("attacksteps"); + + if (name == null) { + errors.add("Mitigation has no name"); + return errors; + } + else if (!name.matches("[a-zA-Z0-9]+")) { + errors.add(name + " must only contain alphanumeric characters"); + return errors; + } + else if (name.length() > 40) { + errors.add(name + " must only contain forty characters max"); + return errors; + } + else if (description == null) { + errors.add("Mitigation has no description"); + return errors; + } + else if (!description.contains(" ")) { + errors.add("The words in \"description\" must be separated with spaces"); + return errors; + } + + for (Object object : relatedAttSteps) { + String relatedAttStep = (String) object; + + if (!foundNameInList(relatedAttStep, attackStepNames)) { + errors.add(relatedAttStep + " is not the name of a provided attack step. Please only associate " + + "mitigations with attack steps from the provided list."); + return errors; + } + } + + return errors; + } + private void createAttackSteps(AttackTree _at, JSONArray attackSteps, String parentAttackName) { AttackNode seqConnection = new SequenceNode("", null); Attack parentAttack = findAttack(_at, parentAttackName); @@ -613,6 +720,25 @@ public class APTAuditor { } } + private void createMitigations(AttackTree _at, JSONArray mitigations) { + for (int i = 0; i < mitigations.length(); i++) { + JSONObject mitigation = mitigations.getJSONObject(i); + JSONArray relations = mitigation.getJSONArray("attacksteps"); + + Defense defense = new Defense("", null); + defense.setName(mitigation.getString("name")); + defense.setDescription(mitigation.getString("description")); + + for (int j = 0; j < relations.length(); j++) { + String attackNodeName = relations.getString(j); + Attack attack = findAttack(_at, attackNodeName); + defense.addRelatedAttack(attack); + } + + _at.addDefense(defense); + } + } + private Attack findAttack(AttackTree at, String attackNodeName) { String attackNNLower = attackNodeName.toLowerCase(); ArrayList<Attack> attackNodeList = at.getAttacks(); @@ -632,6 +758,16 @@ public class APTAuditor { return null; } + private boolean foundNameInList(String target, String[][] list) { + for (String[] s : list) { + if (s[0].equals(target)) { + return true; + } + } + + return false; + } + private static class DuplicateEntryException extends Exception { public DuplicateEntryException(String message) { super(message); diff --git a/src/main/java/ai/CAPECTracer.java b/src/main/java/ai/CAPECTracer.java index 1f3a39064b5b2c46c0e592a76885a2c9b4a6f4d4..c9fdedbbcc9ae10d3797e7266271200e44fb1b71 100644 --- a/src/main/java/ai/CAPECTracer.java +++ b/src/main/java/ai/CAPECTracer.java @@ -1,7 +1,6 @@ package ai; import common.ConfigurationTTool; -import launcher.LauncherException; import launcher.RshClient; import launcher.RshClientReader; import myutil.TraceManager; @@ -19,17 +18,17 @@ import java.nio.file.Paths; * @author Alan Birchler De Allende */ public class CAPECTracer extends AIInteract { - public static String QUESTIONTRACECAPECS = "From the provided system specifications, identify all of the possible " + + private final String QUESTIONTRACECAPECS = "From the provided system specifications, identify all of the possible " + "attack patterns that an attacker could use to exploit the system."; + private final String QUESTIONMITIGATIONS = "From the provided attack, identify possible " + + "countermeasures that could help offset an attack."; - private String command; - private Path projectPath; + private final Path projectPath; private String results; public CAPECTracer(AIChatData _chatData) { super(_chatData); projectPath = Paths.get("../capectracer").normalize().toAbsolutePath(); - command = getCapecCommand(projectPath.toString()); results = ""; } @@ -39,17 +38,32 @@ public class CAPECTracer extends AIInteract { @Override public void internalRequest() { + String command = getCommand(projectPath.toString(), false); + chatData.feedback.addToChat(QUESTIONTRACECAPECS, true); String systemSpec = chatData.lastQuestion.trim(); writeToFile(projectPath + "/system_specs.txt", systemSpec); - String results = runCapecTracer(projectPath.toString()); + String results = runCapecTracer(projectPath.toString(), command); + chatData.feedback.addToChat(results, false); + this.results = results; + } + + public void getMitigations(String attack) { + String command = getCommand(projectPath.toString(), true); + String question = QUESTIONMITIGATIONS + "\nThe attack is the following: " + attack + "\n"; + + chatData.feedback.addToChat(question, true); + + writeToFile(projectPath + "/attack.txt", attack); + + String results = runCapecTracer(projectPath.toString(), command); chatData.feedback.addToChat(results, false); this.results = results; } - public String getInfo() { + public String getInfo(String command) { return "Running command: " + command; } @@ -72,16 +86,26 @@ public class CAPECTracer extends AIInteract { } } - private String getCapecCommand(String capecTracerFolder) { + private String getCommand(String capecTracerFolder, boolean getMitigations) { String python = "python3"; - if (ConfigurationTTool.PythonPathForCapec.length() > 0) { + String command; + + if (!ConfigurationTTool.PythonPathForCapec.isEmpty()) { python = ConfigurationTTool.PythonPathForCapec; } - return python + " " + capecTracerFolder + "/capec_tracer.py"; + + command = python + " " + capecTracerFolder + "/capec_tracer.py"; + + if (getMitigations) { + command += " --mitigations"; + } + + return command; } - private String runCapecTracer(String capecTracerFolder) { + private String runCapecTracer(String capecTracerFolder, String command) { String traces = ""; + String output = ""; try { RshClient rshc = new RshClient("localhost"); @@ -89,27 +113,29 @@ public class CAPECTracer extends AIInteract { rshc.sendExecuteCommandRequest(); RshClientReader data = rshc.getDataReaderFromProcess(); int characterInt = data.read(); - StringBuilder output = new StringBuilder(); + StringBuilder outputBuilder = new StringBuilder(); while (characterInt != -1) { - output.append((char) characterInt); + outputBuilder.append((char) characterInt); characterInt = data.read(); } - if (!output.toString().contains("Error") && - !output.toString().contains("Failed to download the list of CAPECs from MITRE.")) { - byte[] bytes = Files.readAllBytes(Path.of(capecTracerFolder + "/traced_capecs.txt")); - traces = new String(bytes); - } - } catch (IOException | LauncherException e) { + output = outputBuilder.toString(); + TraceManager.addDev(output); + + byte[] bytes = Files.readAllBytes(Path.of(capecTracerFolder + "/traces.txt")); + traces = new String(bytes); + } catch (Exception e) { TraceManager.addDev(e.getMessage()); } - if (traces.isEmpty()) { - return "The tracer failed to run successfully."; + if (!traces.contains("Error") && + !output.contains("Error") && + !traces.contains("Failed to download the list of CAPECs from MITRE.")) { + return traces; } else { - return traces; + return "The tracer failed to run successfully."; } } diff --git a/src/main/java/attacktrees/Attack.java b/src/main/java/attacktrees/Attack.java index 0a06bb4fc7e7dcfd1030d18ab2a5e95e72553f14..b2d530cdaf545d9c006da38e32783e2c51b804e5 100644 --- a/src/main/java/attacktrees/Attack.java +++ b/src/main/java/attacktrees/Attack.java @@ -124,10 +124,7 @@ public class Attack extends AttackElement { return (originNode == null); } - public boolean isFinal() { - return destinationNodes.size() == 0; - - } + public boolean isFinal() { return destinationNodes.isEmpty(); } public boolean canPerformAttack(int _resource, int _expertise) { diff --git a/src/main/java/attacktrees/AttackNode.java b/src/main/java/attacktrees/AttackNode.java index 07437a98e7b33c55fb1f280dd3df14f419d03539..7aa7a2bd0bd49a6bfe9aeeb5645aab6e3b2d4e53 100644 --- a/src/main/java/attacktrees/AttackNode.java +++ b/src/main/java/attacktrees/AttackNode.java @@ -90,6 +90,7 @@ public abstract class AttackNode extends AttackElement { public ArrayList<Attack> getInputAttacks() { return inputAttacks; } + public ArrayList<Integer> getInputValues() { return inputValues; } diff --git a/src/main/java/ui/AttackTreePanelTranslator.java b/src/main/java/ui/AttackTreePanelTranslator.java index 44d9cb4e35ac36d387a1af054dc918e7b6abc766..4692cadc249c9ec381f4547745ce75cc10ac2b65 100644 --- a/src/main/java/ui/AttackTreePanelTranslator.java +++ b/src/main/java/ui/AttackTreePanelTranslator.java @@ -41,10 +41,11 @@ package ui; import attacktrees.*; import avatartranslator.*; -import myutil.TraceManager; import translator.CheckingError; import ui.atd.*; +import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -111,31 +112,28 @@ public class AttackTreePanelTranslator { } public AttackTree translateToAttackTreeDataStructure() { - - at = new AttackTree("AttackTree", atp); if (panel == null) { panel = (AttackTreeDiagramPanel)(atp.panels.get(index)); } - at = new AttackTree("AttackTree", panel); if (panel != null) { - at = new AttackTree("AttackTree", atp); + at = new AttackTree("AttackTree", panel); translate(panel); boolean b = at.checkSyntax(); + if (!b) { UICheckingError ce = new UICheckingError(CheckingError.STRUCTURE_ERROR, at.errorOfFaultyElement); ce.setTGComponent((TGComponent) (at.faultyElement.getReferenceObject())); ce.setTDiagramPanel(panel); addCheckingError(ce); } - } + fixOrdering(); + } - //TraceManager.addDev("AT=" + at.toString()); return at; - } public void translate(AttackTreeDiagramPanel atdp) { @@ -373,9 +371,22 @@ public class AttackTreePanelTranslator { } } } - } + private void fixOrdering() { + for (AttackNode operator : at.getAttackNodes()) { + ArrayList<Attack> attacks = operator.getInputAttacks(); + ArrayList<Integer> inputValues = operator.getInputValues(); + + if (attacks != null && !attacks.isEmpty()) { + Collections.reverse(attacks); + } + + if (inputValues != null && !inputValues.isEmpty()) { + Collections.reverse(inputValues); + } + } + } public AvatarSpecification generateAvatarSpec() { AvatarSpecification as = new AvatarSpecification("spec from attack trees", atp); diff --git a/src/main/java/ui/atd/ATDCountermeasure.java b/src/main/java/ui/atd/ATDCountermeasure.java index 205239e7e4e694df19edca83c77d98e941ac4636..d08e1bf5013b8269a7c60e241c8f20148ded798a 100644 --- a/src/main/java/ui/atd/ATDCountermeasure.java +++ b/src/main/java/ui/atd/ATDCountermeasure.java @@ -324,6 +324,8 @@ public class ATDCountermeasure extends TGCScalableWithInternalComponent implemen return value; } + public void setCountermeasureName(String value) { this.value = value; } + public String getAttributes() { String s = "Description = " + description + "\n"; s += "Id=" + getId(); @@ -336,12 +338,7 @@ public class ATDCountermeasure extends TGCScalableWithInternalComponent implemen NodeList nli; Node n1, n2; Element elt; - String oldtext = description; - description = ""; - String s; - - // - // + String sdescription = null; for (int i = 0; i < nl.getLength(); i++) { n1 = nl.item(i); @@ -351,22 +348,18 @@ public class ATDCountermeasure extends TGCScalableWithInternalComponent implemen n2 = nli.item(j); if (n2.getNodeType() == Node.ELEMENT_NODE) { elt = (Element) n2; - if (elt.getTagName().equals("textline")) { + if (elt.getTagName().equals("info")) { // - s = elt.getAttribute("data"); - if (s.equals("null")) { - s = ""; - } - description += GTURTLEModeling.decodeString(s) + "\n"; + sdescription = elt.getAttribute("description"); + } + + if (sdescription != null) { + description = sdescription; } - // } } } } - if (description.length() == 0) { - description = oldtext; - } } catch (Exception e) { TraceManager.addError("Failed when loading countermeasure"); throw new MalformedModelingException(); @@ -378,13 +371,15 @@ public class ATDCountermeasure extends TGCScalableWithInternalComponent implemen protected String translateExtraParam() { StringBuffer sb = new StringBuffer("<extraparam>\n"); - if (descriptions != null) { - for (int i = 0; i < descriptions.length; i++) { - sb.append("<textline data=\""); - sb.append(GTURTLEModeling.transformString(descriptions[i])); - sb.append("\" />\n"); - } - } +// if (descriptions != null) { +// for (int i = 0; i < descriptions.length; i++) { +// sb.append("<textline data=\""); +// sb.append(GTURTLEModeling.transformString(descriptions[i])); +// sb.append("\" />\n"); +// } +// } + sb.append("<info description=\"" + description); + sb.append("\" />\n"); sb.append("</extraparam>\n"); return new String(sb); } diff --git a/src/main/java/ui/atd/AttackTreeDiagramPanel.java b/src/main/java/ui/atd/AttackTreeDiagramPanel.java index c0eab5aa4e5d1a6dc83e1501c772fda177c2548e..4ff293462c10ca9d7da00655eec5227c72098330 100644 --- a/src/main/java/ui/atd/AttackTreeDiagramPanel.java +++ b/src/main/java/ui/atd/AttackTreeDiagramPanel.java @@ -212,29 +212,12 @@ public class AttackTreeDiagramPanel extends TDiagramPanel implements TDPWithAttr String ret = drawAttackFromAttackTreeModel(mapOfComponents, _at, root, null, -1, 600, 100, 1200); drawConnectionlessAttacks(_at); + drawCountermeasures(mapOfComponents, _at, -1); return ret; } - public void drawConnectionlessAttacks(AttackTree _at) { - int x = 0; - - for(Attack att: _at.getAttacks()) { - ATDAttack attack = alreadyDrawnAttack(att); - - if (attack == null) { - attack = new ATDAttack(x, 0, getMinX(), getMaxX(), getMinY(), getMaxY(), true, null, this); - attack.setValue(att.getName()); - attack.setRootAttack(att.isRoot()); - attack.setDescription(att.getDescription()); - addBuiltComponent(attack); - - x = x + attack.getWidth(); - } - } - } - public String drawAttackFromAttackTreeModel(HashMap<AttackElement, TGComponent> _mapOfComponents, AttackTree _at, Attack _att, ATDConstraint _const, int _valueConst, int _x, int _y, int _length) { ATDAttack attack = alreadyDrawnAttack(_att); @@ -335,32 +318,60 @@ public class AttackTreeDiagramPanel extends TDiagramPanel implements TDPWithAttr } } + return null; + } + + private ATDAttack alreadyDrawnAttack(Attack attack) { + List<TGComponent> componentList = getComponentList(); + + for (TGComponent component : componentList) { + if (component instanceof ATDAttack) { + ATDAttack attackComponent = (ATDAttack) component; + + if (attackComponent.getValue().equals(attack.getName()) && + attackComponent.getDescription().equals(attack.getDescription()) && + attackComponent.isRootAttack() == attack.isRoot()) { + return attackComponent; + } + } + } + + return null; + } + + public void drawConnectionlessAttacks(AttackTree _at) { + int x = 0; + + for(Attack att: _at.getAttacks()) { + ATDAttack attack = alreadyDrawnAttack(att); + + if (attack == null) { + attack = new ATDAttack(x, 0, getMinX(), getMaxX(), getMinY(), getMaxY(), true, null, this); + attack.setValue(att.getName()); + attack.setRootAttack(att.isRoot()); + attack.setDescription(att.getDescription()); + addBuiltComponent(attack); + + x = x + attack.getWidth(); + } + } + } + + public void drawCountermeasures(HashMap<AttackElement, TGComponent> _mapOfComponents, AttackTree _at, + int _valueConst) { // Handling countermeasures int xNoRel = 100; int yNoRel = 100; - int newX, newY; + for(Defense def : _at.getDefenses()) { - if (def.hasRelatedAttacks()) { - Attack firstAttack = def.getRelatedAttacks().get(0); - TGComponent refToAtt = _mapOfComponents.get(firstAttack); - if (refToAtt != null) { - newX = refToAtt.getX(); - newY = refToAtt.getY() + 100; - } else { - newX = xNoRel; - newY = yNoRel; - yNoRel += 100; - } - } else { - newX = xNoRel; - newY = yNoRel; - yNoRel += 100; - } - ATDCountermeasure atdc = new ATDCountermeasure(newX, newY, getMinX(), getMaxX(), getMinY(), getMaxY(), true, null, this); + ATDCountermeasure atdc = new ATDCountermeasure(xNoRel, yNoRel, getMinX(), getMaxX(), getMinY(), getMaxY(), true, null, this); + atdc.setCountermeasureName(def.getName()); atdc.setDescription(def.getDescription()); - atdc.setEnabled(def.isEnabled()); + atdc.setEnabled(true); addBuiltComponent(atdc); + yNoRel += 100; + // Connections for(Attack relatedAtt: def.getRelatedAttacks()) { TGComponent refToAtt = _mapOfComponents.get(relatedAtt); @@ -369,7 +380,7 @@ public class AttackTreeDiagramPanel extends TDiagramPanel implements TDPWithAttr TGConnectingPoint p2 = refToAtt.closerFreeTGConnectingPoint(atdc.getX(), atdc.getY(), true, false); if ((p1 != null) && (p2 != null)) { - + TGConnector conn = null; Vector<Point> points = new Vector<Point>(); conn = new ATDCountermeasureConnector(0, 0, 0, 0, 0, 0, true, null, @@ -387,26 +398,6 @@ public class AttackTreeDiagramPanel extends TDiagramPanel implements TDPWithAttr } } } - - return null; - } - - private ATDAttack alreadyDrawnAttack(Attack attack) { - List<TGComponent> componentList = getComponentList(); - - for (TGComponent component : componentList) { - if (component instanceof ATDAttack) { - ATDAttack attackComponent = (ATDAttack) component; - - if (attackComponent.getValue().equals(attack.getName()) && - attackComponent.getDescription().equals(attack.getDescription()) && - attackComponent.isRootAttack() == attack.isRoot()) { - return attackComponent; - } - } - } - - return null; } public int getTotalNumberOfAttacks() { diff --git a/src/main/java/ui/window/JFrameAI.java b/src/main/java/ui/window/JFrameAI.java index c68aaef29b1c97adc45936424c098fe4cb7ecbf9..c827e54a01de85bf2808d36fef64b2906b395174 100644 --- a/src/main/java/ui/window/JFrameAI.java +++ b/src/main/java/ui/window/JFrameAI.java @@ -50,6 +50,7 @@ import myutil.AIInterface; import myutil.GraphicLib; import myutil.TraceManager; import ui.*; +import ui.atd.AttackTreeDiagramPanel; import ui.avatarbd.AvatarBDPanel; import ui.avatarrd.AvatarRDPanel; import ui.avatarrd.AvatarRDRequirement; @@ -73,8 +74,6 @@ import java.util.HashMap; * @author Ludovic APVRILLE */ public class JFrameAI extends JFrame implements ActionListener { - - private static String[] POSSIBLE_ACTIONS = { "Chat - Chat on any topic you like, or help the AI give a better answer on a previous question", "Identify requirements - Provide a system specification", @@ -90,14 +89,11 @@ public class JFrameAI extends JFrame implements ActionListener { "Model mutation - A(I)MULET - Select a block diagram first", "Diagram coherency", "Diagram coherency with formal rules", - "Capec tracer - Identify the possible attack patterns that an attacker could use to exploit your system " + + "CAPEC tracer - Identify the possible attack patterns that an attacker could use to exploit your system " + "specifications.", - "Attack Tree Generator, Pipeline 1 - Creates five Attack Tree diagrams that model five different " + - "possible exploitations on a provided system specification.", - "Attack Tree Generator, Pipeline 2 - Creates an Attack Tree diagram that models a possible exploitation " + - "on a provided system specification using a provided attack pattern.", - "Attack Tree Generator, Pipeline 3 - Creates an Attack Tree diagram that models a possible exploitation " + - "on a provided system specification using a provided attack pattern." + "Attack tree generator - Creates a specified amount of Attack Tree diagrams that model a possible exploitation " + + "on a provided system specification using a set of provided attack patterns from the CAPEC tracer.", + "Mitigations generator - Generates a list of mitigations for each provided attack step." }; private static String[] AIInteractClass = { @@ -106,7 +102,7 @@ public class JFrameAI extends JFrame implements ActionListener { "AIBlockConnAttrib", "AIBlockConnAttribWithSlicing", "AISoftwareBlock", "AIStateMachine", "AIStateMachinesAndAttributes", "AIAmulet", "AIDiagramCoherency", "AIDiagramCoherencyWithFormalRules", - "CAPECTracer", "AIAttackPatternTree1", "AIAttackPatternTree2", "AIAttackPatternTree3" + "CAPECTracer", "AIAttackPatternTree", "AIAPTMitigations" }; private static String[] INFOS = { @@ -129,12 +125,13 @@ public class JFrameAI extends JFrame implements ActionListener { "Identify the possible attack patterns that an attacker could use to exploit your system specifications. " + "Each identified attack pattern will have a confidence score of TTool's estimation on how " + "related an attack pattern is to the provided system specifications.", - "Using a provided system specification, create five Attack Tree diagrams that model the steps that an attacker " + - "would need to take to exploit five different identified root attacks on the given system specification.", - "Using a provided system specification and an attack pattern, create an Attack Tree diagram that " + + "Using a provided system specification, create Attack Tree diagrams that " + "models the steps that an attacker would need to take to exploit the given attack pattern " + - "on the given system specifications.", - "Test pipeline" + "on the given system specifications. This pipeline also uses the CAPEC tracer pipeline to " + + "extract relevant attack patterns to assist with the creation of the attack trees.", + "Using a set of provided attack steps, output possible mitigations for each attack step that prevent " + + "an attacker from preforming the step. Uses the CAPEC tracer pipeline to generate a list " + + "of possible counters to help with identifying the mitigations." }; protected JComboBox<String> listOfPossibleActions; @@ -272,34 +269,6 @@ public class JFrameAI extends JFrame implements ActionListener { panelTop.add(modelSelectionPanel, BorderLayout.SOUTH); framePanel.add(panelTop, BorderLayout.NORTH); - listOfPossibleActions.addActionListener(e -> { - String selectedAction = (String) listOfPossibleActions.getSelectedItem(); - assert selectedAction != null; - - if (selectedAction.equals(POSSIBLE_ACTIONS[14])) { - modelSelectionPanel.setVisible(false); - labelAtsSpinner.setVisible(false); - numAtsSpinner.setVisible(false); - numLvlsSpinner.setVisible(false); - labelnumLvlsSpinner.setVisible(false); - } - else if (selectedAction.equals(POSSIBLE_ACTIONS[17])) { - modelSelectionPanel.setVisible(true); - labelAtsSpinner.setVisible(true); - numAtsSpinner.setVisible(true); - numLvlsSpinner.setVisible(true); - labelnumLvlsSpinner.setVisible(true); - } - else { - modelSelectionPanel.setVisible(true); - labelAtsSpinner.setVisible(false); - numAtsSpinner.setVisible(false); - numLvlsSpinner.setVisible(false); - labelnumLvlsSpinner.setVisible(false); - } - }); - - // Middle panel /*jta.setEditable(true); @@ -400,9 +369,45 @@ public class JFrameAI extends JFrame implements ActionListener { framePanel.add(lowPart, BorderLayout.SOUTH); enableDisableActions(); + pack(); + listOfPossibleActions.addActionListener(e -> { + String selectedAction = (String) listOfPossibleActions.getSelectedItem(); + assert selectedAction != null; - pack(); + if (selectedAction.equals(POSSIBLE_ACTIONS[14])) { + modelSelectionPanel.setVisible(false); + labelAtsSpinner.setVisible(false); + numAtsSpinner.setVisible(false); + numLvlsSpinner.setVisible(false); + labelnumLvlsSpinner.setVisible(false); +// questionPanel.setVisible(true); + } + else if (selectedAction.equals(POSSIBLE_ACTIONS[15])) { + modelSelectionPanel.setVisible(true); + labelAtsSpinner.setVisible(true); + numAtsSpinner.setVisible(true); + numLvlsSpinner.setVisible(true); + labelnumLvlsSpinner.setVisible(true); +// questionPanel.setVisible(true); + } + else if (selectedAction.equals(POSSIBLE_ACTIONS[16])) { + modelSelectionPanel.setVisible(true); + labelAtsSpinner.setVisible(false); + numAtsSpinner.setVisible(false); + numLvlsSpinner.setVisible(false); + labelnumLvlsSpinner.setVisible(false); +// questionPanel.setVisible(false); + } + else { + modelSelectionPanel.setVisible(true); + labelAtsSpinner.setVisible(false); + numAtsSpinner.setVisible(false); + numLvlsSpinner.setVisible(false); + labelnumLvlsSpinner.setVisible(false); +// questionPanel.setVisible(true); + } + }); } private void addChat(String nameOfChat) { @@ -536,13 +541,23 @@ public class JFrameAI extends JFrame implements ActionListener { } } - if (selected.aiInteract instanceof AIAttackPatternTree3) { - TraceManager.addDev("****** AIAttackPatternTree3 identified *****"); - AIAttackPatternTree3 aiAPT = (AIAttackPatternTree3) selected.aiInteract; + if (selected.aiInteract instanceof AIAttackPatternTree) { + TraceManager.addDev("****** AIAttackPatternTree identified *****"); + AIAttackPatternTree aiAPT = (AIAttackPatternTree) selected.aiInteract; aiAPT.setMaxLevels((Integer) numLvlsModel.getNumber()); aiAPT.setMaxNumAts((Integer) numAtsModel.getNumber()); } + if (selected.aiInteract instanceof AIAPTMitigations) { + TraceManager.addDev("****** AIAPTMitigations identified *****"); + TDiagramPanel tdp = mgui.getCurrentTDiagramPanel(); + AttackTreePanelTranslator atpt = new AttackTreePanelTranslator((AttackTreeDiagramPanel) tdp); + AttackTree at = atpt.translateToAttackTreeDataStructure(); + + AIAPTMitigations aiMiti = (AIAPTMitigations) selected.aiInteract; + aiMiti.setReferenceAT(at); + } + selected.aiInteract.makeRequest(question.getText()); String info = selected.aiInteract.getInfo(); @@ -611,14 +626,13 @@ public class JFrameAI extends JFrame implements ActionListener { applyIdentifyStateMachines(selectedChat.aiInteract.applyAnswer(null)); } else if (selectedChat.aiInteract instanceof ai.AIAmulet) { applyMutations(); - } else if (selectedChat.aiInteract instanceof ai.AIAttackPatternTree1) { - applyAttackTrees(selectedChat.aiInteract.applyAnswer(null)); - } else if (selectedChat.aiInteract instanceof ai.AIAttackPatternTree2) { - applyAttackTree(selectedChat.aiInteract.applyAnswer(null)); } - else if (selectedChat.aiInteract instanceof ai.AIAttackPatternTree3) { + else if (selectedChat.aiInteract instanceof AIAttackPatternTree) { applyAttackTrees(selectedChat.aiInteract.applyAnswer(null)); } + else if (selectedChat.aiInteract instanceof AIAPTMitigations) { + applyAttackTree(selectedChat.aiInteract.applyAnswer(null)); + } break; case 1: applyRequirementIdentification();