def test_parse_line(): t = threatmodel.ThreatModel() p = parser.SourceFileParser(t) lines = [{ "line": "somecode.action(parameter) // @mitigates Path:To:Component against Threat with Control", "next_line": "other.action(parameter) // @mitigates Path:To:Component2 against Threat2 with Control2", "action": "mitigates", "threat": "Threat", "component": "Path:To:Component", "control": "Control", "annotation": "@mitigates Path:To:Component against Threat with Control", "code": "somecode.action(parameter)", "filename": "afile.js", "line_no": 66 }] for line in lines: (data, source) = p.parse_line(line["line"], line["next_line"], line["filename"], line["line_no"]) assert data == { "action": "mitigates", "threat": "Threat", "component": "Path:To:Component", "control": "Control" } assert source == { "annotation": line["annotation"], "code": line["code"], "filename": line["filename"], "line": line["line_no"] }
def test_yaml_paser(): t = threatmodel.ThreatModel() t.threat_library = threatmodel.ThreatLibrary() p = parser.YamlFileParser(t) yaml_string = """ key1: key11: x-threatspec: "@threat A string threat" key12: x-threatspec: - "@threat Array threat 1" - "@threat Array threat 2" key2: key21: key211: "x-threatspec": "@threat Extended threat 1": description: Extended description 1 impact: high """ data = yaml.load(yaml_string, Loader=yaml.SafeLoader) p.parse_data(data, {}, "path/to/file") assert len(t.threat_library.threats) == 4 assert "#a_string_threat" in t.threat_library.threats assert t.threat_library.threats[ "#a_string_threat"].name == "A string threat" assert "#extended_threat_1" in t.threat_library.threats assert t.threat_library.threats["#extended_threat_1"].custom[ "impact"] == "high"
def test_parse_comment_line(): t = threatmodel.ThreatModel() p = parser.SourceFileParser(t) lines = [{ "line": "// A normal comment", "annotation": "A normal comment", "code": "" }, { "line": "somecode.action(parameter) // An inline comment", "annotation": "An inline comment", "code": "somecode.action(parameter)" }, { "line": "# A normal comment", "annotation": "A normal comment", "code": "" }, { "line": "somecode.action(parameter) # An inline comment", "annotation": "An inline comment", "code": "somecode.action(parameter)" }] for line in lines: (annotation, code) = p.parse_comment_line(line["line"]) assert line["code"] == code assert line["annotation"] == annotation
def test_parser_parse_annotation(): t = threatmodel.ThreatModel() p = parser.Parser(t) annotations = [{ "annotation": "@mitigates Path:To:Component against a multi word threat with a multi word control", "action": "mitigates", "component": "Path:To:Component", "threat": "a multi word threat", "control": "a multi word control" }, { "annotation": "@accepts a multi word threat to Path:To:Component with why it has been accepted", "action": "accepts", "threat": "a multi word threat", "component": "Path:To:Component", "details": "why it has been accepted" }, { "annotation": "@transfers a multi word threat from Path:To:Source to Path:To:Destination with why it has been transfered", "action": "transfers", "threat": "a multi word threat", "source_component": "Path:To:Source", "destination_component": "Path:To:Destination", "details": "why it has been transfered" }, { "annotation": "@exposes Path:To:Component to a multi word threat with how it is exposed", "action": "exposes", "threat": "a multi word threat", "component": "Path:To:Component", "details": "how it is exposed" }, { "annotation": "@connects Path:To:Source with Path:To:Destination with details about connection", "action": "connects", "source_component": "Path:To:Source", "destination_component": "Path:To:Destination", "direction": "with", "details": "details about connection" }, { "annotation": "@review Path:To:Component something worth noting", "action": "review", "component": "Path:To:Component", "details": "something worth noting" }, { "annotation": "@tests a multi word control for Path:To:Component", "action": "tests", "control": "a multi word control", "component": "Path:To:Component" }] for annotation in annotations: data = p.parse_annotation(annotation.pop("annotation")) assert data == annotation
def test_strip(): t = threatmodel.ThreatModel() t.threat_library = threatmodel.ThreatLibrary() p = parser.CommentParser(t) assert p.strip("* test ") == "test" assert p.strip(" * test ") == "test" assert p.strip(" * test ") == "test" assert p.strip("test ") == "test" assert p.strip(" test") == "test" assert p.strip(" * test\n attr: 42 ") == "test\n attr: 42"
def test_parse_control_extended_comment(): t = threatmodel.ThreatModel() p = parser.SourceFileParser(t) comment_text = """ @control A Control (#controlid): description: | A multiline description cost: high """ annotations = p.parse_comment(comment_text) assert len(annotations) == 1 assert annotations[0]["control"] == "A Control (#controlid)" assert annotations[0]["description"] == "A multiline\ndescription\n" assert annotations[0]["cost"] == "high"
def test_parse_threat_extended_comment(): t = threatmodel.ThreatModel() p = parser.SourceFileParser(t) comment_text = """ @threat A Threat (#threatid): description: | A multiline description impact: high """ annotations = p.parse_comment(comment_text) assert len(annotations) == 1 assert annotations[0]["threat"] == "A Threat (#threatid)" assert annotations[0]["description"] == "A multiline\ndescription\n" assert annotations[0]["impact"] == "high"
def test_parse_component_extended_comment_with_leading_stars(): t = threatmodel.ThreatModel() p = parser.SourceFileParser(t) comment_text = """ * @component Path:To:Component (#componentid): * description: | * A multiline * description * value: high """ annotations = p.parse_comment(comment_text) assert len(annotations) == 1 assert annotations[0]["component"] == "Path:To:Component (#componentid)" assert annotations[0]["description"] == "A multiline\ndescription\n" assert annotations[0]["value"] == "high"
def test_parse_review_extended_comment_with_leading_stars(): t = threatmodel.ThreatModel() p = parser.SourceFileParser(t) comment_text = """ * @review Path:To:Component Check something: * description: | * A multiline * description * urgent: yes """ annotations = p.parse_comment(comment_text) assert len(annotations) == 1 assert annotations[0]["component"] == "Path:To:Component" assert annotations[0]["details"] == "Check something" assert annotations[0]["description"] == "A multiline\ndescription\n" assert annotations[0]["urgent"] == True
def __init__(self): self.threat_library = threatmodel.ThreatLibrary() self.control_library = threatmodel.ControlLibrary() self.component_library = threatmodel.ComponentLibrary() self.threatmodel = threatmodel.ThreatModel() self.threatmodel.threat_library = self.threat_library self.threatmodel.control_library = self.control_library self.threatmodel.component_library = self.component_library self.threatmodel.run_id = uuid.uuid4().hex logger.debug("Setting run id to {}".format(self.threatmodel.run_id)) self.config = config.Config() self.parser = None self.reporter = None self.loaded_source_paths = {} self.loaded_library_paths = {}
def test_source_file_parser_parse_comments(): t = threatmodel.ThreatModel() p = parser.SourceFileParser(t) comments = [{ "test": "@mitigates Path:To:Component against a multi word threat with a multi word control", "result": { "action": "mitigate", "component": "Path:To:Component", "threat": "a multi word threat", "control": "a multi word control" } }, { "test": "@accepts a multi word threat to Path:To:Component with why it has been accepted", "result": { "action": "accept", "threat": "a multi word threat", "component": "Path:To:Component", "details": "why it has been accepted" } }, { "test": "@transfers a multi word threat from Path:To:Source to Path:To:Destination with why it has been transfered", "result": { "action": "transfer", "threat": "a multi word threat", "source_component": "Path:To:Source", "destination_component": "Path:To:Destination", "details": "why it has been transfered" } }, { "test": "@exposes Path:To:Component to a multi word threat with how it is exposed", "result": { "action": "expose", "threat": "a multi word threat", "component": "Path:To:Component", "details": "how it is exposed" } }, { "test": "@connects Path:To:Source with Path:To:Destination with details about connection", "result": { "action": "connect", "source_component": "Path:To:Source", "destination_component": "Path:To:Destination", "direction": "with", "details": "details about connection" } }, { "test": "@review Path:To:Component something worth noting", "result": { "action": "review", "component": "Path:To:Component", "details": "something worth noting" } }, { "test": "@tests a multi word control for Path:To:Component", "result": { "action": "test", "control": "a multi word control", "component": "Path:To:Component" } }] for comment in comments: data = p.parse_comment(comment["test"]) assert len(data) == 1 data[0].pop("annotation") data[0].pop("line") assert data[0] == comment["result"]