From 27ada1ef22ba6a391405e7b41689af0605697ea8 Mon Sep 17 00:00:00 2001 From: xansec Date: Tue, 17 Jun 2025 23:48:17 -0400 Subject: [PATCH 1/9] rebase --- .../parsers/file/mayhem.md | 19 + dojo/tools/mayhem/__init__.py | 0 dojo/tools/mayhem/parser.py | 628 ++++++ .../scans/mayhem/mayhem_api_many_vulns.sarif | 1705 +++++++++++++++++ .../scans/mayhem/mayhem_api_no_vulns.sarif | 17 + .../scans/mayhem/mayhem_api_one_vuln.sarif | 62 + .../scans/mayhem/mayhem_code_many_vulns.sarif | 394 ++++ .../scans/mayhem/mayhem_code_no_vulns.sarif | 27 + .../scans/mayhem/mayhem_code_one_vuln.sarif | 87 + unittests/scans/test_mayhem_parser.py | 71 + unittests/tools/test_mayhem_parser.py | 71 + 11 files changed, 3081 insertions(+) create mode 100644 docs/content/en/connecting_your_tools/parsers/file/mayhem.md create mode 100644 dojo/tools/mayhem/__init__.py create mode 100644 dojo/tools/mayhem/parser.py create mode 100644 unittests/scans/mayhem/mayhem_api_many_vulns.sarif create mode 100644 unittests/scans/mayhem/mayhem_api_no_vulns.sarif create mode 100644 unittests/scans/mayhem/mayhem_api_one_vuln.sarif create mode 100755 unittests/scans/mayhem/mayhem_code_many_vulns.sarif create mode 100755 unittests/scans/mayhem/mayhem_code_no_vulns.sarif create mode 100755 unittests/scans/mayhem/mayhem_code_one_vuln.sarif create mode 100644 unittests/scans/test_mayhem_parser.py create mode 100644 unittests/tools/test_mayhem_parser.py diff --git a/docs/content/en/connecting_your_tools/parsers/file/mayhem.md b/docs/content/en/connecting_your_tools/parsers/file/mayhem.md new file mode 100644 index 00000000000..0c55dcaea2e --- /dev/null +++ b/docs/content/en/connecting_your_tools/parsers/file/mayhem.md @@ -0,0 +1,19 @@ +--- +title: "Mayhem SARIF Reports" +toc_hide: true +--- +Import for Mayhem generated SARIF reports. In general, the exiting +SARIF report consumer should work, and for general cases does. However, +since Mayhem is A. DAST and B. includes fuzzed data in the content of +the report, a Mayhem-specific SARIF consumer is added. +See more below: +[Mayhem SARIF Report (API)](https://docs.mayhem.security/api-testing/tutorials/identifying-api-issues/bug-reporting/#sarif-reports). +[Mayhem SARIF Report (CI)](https://docs.mayhem.security/integrations/ci-integrations/github/#analyzing-sarif-reports). + + +#### Parity with Existing SARIF Consumer + +The current implementation is mostly lifted from the existing SARIF parser support. As such, it will also aggregate all the findings in the SARIF file in one single report, and it also supports fingerprint deduplication. + +### Sample Scan Data +Sample Mayhem SARIF reports can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/mayhem). \ No newline at end of file diff --git a/dojo/tools/mayhem/__init__.py b/dojo/tools/mayhem/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dojo/tools/mayhem/parser.py b/dojo/tools/mayhem/parser.py new file mode 100644 index 00000000000..0e4e2c634c3 --- /dev/null +++ b/dojo/tools/mayhem/parser.py @@ -0,0 +1,628 @@ +import json +import logging +import re +import textwrap + +import dateutil.parser +from django.utils.translation import gettext as _ + +from dojo.models import Finding +from dojo.tools.parser_test import ParserTest + +logger = logging.getLogger(__name__) + +CWE_REGEX = r"cwe-\d+" + + +class MayhemParser: + + """ + Mayhem SARIF Parser + This is largely lifted from the existing SARIF parser, but with some minor + modifications to better support the structure of Mayhem SARIF reports. + """ + + def get_fields(self) -> list[str]: + """ + Return the list of fields used in the Sarif Parser + + Fields: + - title: Made using rule and id from Sarif scanner. + - severity: Set to severity from Sarif Scanner converted to Defect Dojo format. + - description: Made by combining message, location, and rule from Sarif Scanner. + - static_finding: Set to true. + - dynamic_finding: Set to false. + - false_p: Set to true or false based on suppression status from Sarif scanner. + - active: Set to true or false based on suppression status from Sarif scanner. + - file_path: Set to physical location from Sarif scanner. + - line: Set to start line from Sarif scanner. + - vuln_id_from_tool: Set to rule id from Sarif scanner. + - cwe: Set to the cwe values outputted from Sarif Scanner. + - cvssv3: Set to properties and securitiy-severity from Sarif scanner if available. + - cvssv3_score: Set to properties and securitiy-severity from Sarif scanner if available. + - mitigation: Set to altered version of finding's description. + - date: Set to the date outputted from Sarif Scanner converted to datetime. + - tags: Set to tags from Sarif scanner. + - unique_id_from_tool: Set to the hash fingerpring value outputted from Sarif Scanner. + + NOTE: This parser supports tags. + """ + return [ + "title", + "severity", + "description", + "static_finding", + "dynamic_finding", + "false_p", + "active", + "file_path", + "line", + "vuln_id_from_tool", + "cwe", + "cvssv3", + "cvssv3_score", + "mitigation", + "date", + "tags", + "unique_id_from_tool", + ] + + def get_dedupe_fields(self) -> list[str]: + """ + Return the list of dedupe fields used in the Sarif Parser + + Fields: + - title: Made using rule and id from Sarif scanner. + - cwe: Set to the cwe values outputted from Sarif Scanner. + - line: Set to start line from Sarif scanner. + - file_path: Set to physical location from Sarif scanner. + - description: Made by combining message, location, and rule from Sarif Scanner. + + NOTE: uses legacy dedupe: ['title', 'cwe', 'line', 'file_path', 'description'] + """ + return [ + "title", + "cwe", + "line", + "file_path", + "description", + ] + + def get_scan_types(self): + return ["Mayhem SARIF Report"] + + def get_label_for_scan_types(self, scan_type): + return scan_type + + def get_description_for_scan_types(self, scan_type): + return "Mayhem SARIF reports from code or API runs." + + def get_findings(self, filehandle, test): + """For simple interface of parser contract we just aggregate everything""" + tree = json.load(filehandle) + items = [] + # for each runs we just aggregate everything + for run in tree.get("runs", []): + items.extend(self.__get_items_from_run(run)) + return items + + def get_tests(self, scan_type, handle): + tree = json.load(handle) + tests = [] + for run in tree.get("runs", []): + test = ParserTest( + name=run["tool"]["driver"]["name"], + parser_type=run["tool"]["driver"]["name"], + version=run["tool"]["driver"].get("version"), + ) + test.findings = self.__get_items_from_run(run) + tests.append(test) + return tests + + def __get_items_from_run(self, run): + items = [] + # load rules + rules = get_rules(run) + artifacts = get_artifacts(run) + # get the timestamp of the run if possible + run_date = self.__get_last_invocation_date(run) + for result in run.get("results", []): + result_items = get_items_from_result(result, rules, artifacts, run_date) + if result_items: + items.extend(result_items) + return items + + def __get_last_invocation_date(self, data): + invocations = data.get("invocations", []) + if len(invocations) == 0: + return None + # try to get the last 'endTimeUtc' + raw_date = invocations[-1].get("endTimeUtc") + if raw_date is None: + return None + # if the data is here we try to convert it to datetime + return dateutil.parser.isoparse(raw_date) + + +def get_rules(run): + rules = {} + rules_array = run["tool"]["driver"].get("rules", []) + if len(rules_array) == 0 and run["tool"].get("extensions") is not None: + rules_array = run["tool"]["extensions"][0].get("rules", []) + for item in rules_array: + rules[item["id"]] = item + return rules + + +# Rules and results have de sames scheme for tags +def get_properties_tags(value): + if not value: + return [] + return value.get("properties", {}).get("tags", []) + + +def search_cwe(value, cwes): + matches = re.search(CWE_REGEX, value, re.IGNORECASE) + if matches: + cwes.append(int(matches[0].split("-")[1])) + + +def get_rule_cwes(rule): + cwes = [] + # data of the specification + if "relationships" in rule and isinstance(rule["relationships"], list): + for relationship in rule["relationships"]: + value = relationship["target"]["id"] + search_cwe(value, cwes) + return cwes + + for tag in get_properties_tags(rule): + search_cwe(tag, cwes) + return cwes + + +def get_result_cwes_properties(result): + """Some tools like njsscan store the CWE in the properties of the result""" + cwes = [] + if "properties" in result and "cwe" in result["properties"]: + value = result["properties"]["cwe"] + search_cwe(value, cwes) + return cwes + +def get_result_cwes_mcode(result): + """Mayhem SARIF reports include CWE property under taxa.toolComponent.name and number under taxa.id""" + cwes = [] + if "taxa" in result: + for taxon in result["taxa"]: + if taxon.get("toolComponent", {}).get("name") == "CWE": + value = taxon.get("id") + if value: + cwes.append(int(value)) + return cwes + + +def get_artifacts(run): + artifacts = {} + for custom_index, tree_artifact in enumerate(run.get("artifacts", [])): + artifacts[tree_artifact.get("index", custom_index)] = tree_artifact + return artifacts + +def clean_mayhem_title_text(text): + """ + Clean the title text for Mayhem SARIF reports. + """ + if not text: + return "" + + # Remove links (and add limit to avoid catastrophic backtracking) + link_regex = r"\[[^\]]{1,100}?\]\([^)]{1,200}?\)" + text = re.sub(link_regex, "", text) + + # Remove URL encoded characters + url_encoding_regex = r"&#x\d+;" + text = re.sub(url_encoding_regex, "", text) + + # Remove single or double quotes + quotes_regex = r"[\"']" + text = re.sub(quotes_regex, "", text) + + # Remove TDID + tdid_regex = r"TDID-\d+\s*-\s*|TDID-\d+-" + text = re.sub(tdid_regex, "", text) + + return text.strip() + + +def get_message_from_multiformatMessageString(data, rule, content_type="text"): + """ + Get a message from multimessage struct + + See here for the specification: https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317468 + """ + if content_type not in ["text", "markdown"]: + raise ValueError(f"Unexpected content type: {content_type}") + if content_type == "markdown" and "markdown" in data: + # handle markdown content + markdown = data.get("markdown") + # strip "headings" or anything that changes text size + heading_regex = r"^#+\s*" + markdown = re.sub(heading_regex, "", markdown, flags=re.MULTILINE) + # replace non-unicode characters with "?" + non_unicode_regex = r'[^\x09\x0A\x0D\x20-\x7E]' + markdown = re.sub(non_unicode_regex, '?', markdown) + return markdown.strip() + elif content_type == "text" and "text" in data: + # handle text content + text = data.get("text") + if rule is not None and "id" in data: + text = rule["messageStrings"][data["id"]].get("text") + arguments = data.get("arguments", []) + # argument substitution + for i in range(6): # the specification limit to 6 + substitution_str = "{" + str(i) + "}" + if substitution_str in text and i < len(arguments): + text = text.replace(substitution_str, arguments[i]) + return text + else: + return "" + +def cve_try(val): + # Match only the first CVE! + cveSearch = re.search(r"(CVE-[0-9]+-[0-9]+)", val, re.IGNORECASE) + if cveSearch: + return cveSearch.group(1).upper() + return None + + +def get_title(result, rule): + title = None + if "message" in result: + title = get_message_from_multiformatMessageString( + result["message"], rule, + ) + if title is None and rule is not None: + if "shortDescription" in rule: + title = get_message_from_multiformatMessageString( + rule["shortDescription"], rule, + ) + elif "fullDescription" in rule: + title = get_message_from_multiformatMessageString( + rule["fullDescription"], rule, + ) + elif "name" in rule: + title = rule["name"] + elif "id" in rule: + title = rule["id"] + + if title is None: + msg = "No information found to create a title" + raise ValueError(msg) + + # Clean the title text for Mayhem SARIF reports + title = clean_mayhem_title_text(title) + + return textwrap.shorten(title, 150) + + +def get_snippet(location): + + snippet = None + + if location and "physicalLocation" in location: + if "region" in location["physicalLocation"]: + if "snippet" in location["physicalLocation"]["region"]: + if ( + "text" + in location["physicalLocation"]["region"]["snippet"] + ): + snippet = location["physicalLocation"]["region"][ + "snippet" + ]["text"] + if ( + snippet is None + and "contextRegion" in location["physicalLocation"] + ): + if "snippet" in location["physicalLocation"]["contextRegion"]: + if ( + "text" + in location["physicalLocation"]["contextRegion"][ + "snippet" + ] + ): + snippet = location["physicalLocation"][ + "contextRegion" + ]["snippet"]["text"] + + return snippet + + +def get_codeFlowsDescription(code_flows): + description = "" + for codeFlow in code_flows: + for threadFlow in codeFlow.get("threadFlows", []): + if "locations" not in threadFlow: + continue + + description = f"**{_('Code flow')}:**\n" + + for line, location in enumerate(threadFlow.get("locations", [])): + physicalLocation = location.get("location", {}).get("physicalLocation", {}) + region = physicalLocation.get("region", {}) + uri = physicalLocation.get("artifactLocation").get("uri") + + start_line = "" + start_column = "" + snippet = "" + + if "startLine" in region: + start_line = f":L{region.get('startLine')}" + + if "startColumn" in region: + start_column = f":C{region.get('startColumn')}" + + if "snippet" in region: + snippet = f"\t-\t{region.get('snippet').get('text')}" + + description += f"{line + 1}. {uri}{start_line}{start_column}{snippet}\n" + + if "message" in location.get("location", {}): + message_field = location.get("location", {}).get("message", {}) + if "markdown" in message_field: + message = message_field.get("markdown", "") + else: + message = message_field.get("text", "") + + description += f"\t{message}\n" + + return description + +def get_description(result, rule, location): + description = "" + message = "" + if "message" in result: + message = get_message_from_multiformatMessageString( + result["message"], rule, + ) + description += f"**Result message:** {message}\n" + if get_snippet(location) is not None: + description += f"**Snippet:**\n```\n{get_snippet(location)}\n```\n" + if rule is not None: + if "name" in rule: + description += f"**{_('Rule name')}:** {rule.get('name')}\n" + shortDescription = "" + if "shortDescription" in rule: + shortDescription = get_message_from_multiformatMessageString( + rule["shortDescription"], rule, + ) + if shortDescription != message: + description += f"**{_('Rule short description')}:** {shortDescription}\n" + if "fullDescription" in rule: + fullDescription = get_message_from_multiformatMessageString( + rule["fullDescription"], rule, + ) + if ( + fullDescription != message + and fullDescription != shortDescription + ): + description += f"**{_('Rule full description')}:** {fullDescription}\n" + if "markdown" in result["message"]: + markdown = get_message_from_multiformatMessageString( + result["message"], rule, content_type="markdown", + ) + # Replace "Details" with "Link" in the markdown + markdown = markdown.replace("Details", "Link") + description += f"**{_('Additional Details')}:**\n{markdown}\n" + description += f"_(Unprintable characters are replaced with '?'; please see Mayhem for full reproducer.)_" + if len(result.get("codeFlows", [])) > 0: + description += get_codeFlowsDescription(result["codeFlows"]) + + return description.removesuffix("\n") + + +def get_references(rule): + reference = None + if rule is not None: + if "helpUri" in rule: + reference = rule["helpUri"] + elif "help" in rule: + helpText = get_message_from_multiformatMessageString( + rule["help"], rule, + ) + if helpText.startswith("http"): + reference = helpText + + return reference + + +def cvss_to_severity(cvss): + severity_mapping = { + 1: "Info", + 2: "Low", + 3: "Medium", + 4: "High", + 5: "Critical", + } + + if cvss >= 9: + return severity_mapping.get(5) + if cvss >= 7: + return severity_mapping.get(4) + if cvss >= 4: + return severity_mapping.get(3) + if cvss > 0: + return severity_mapping.get(2) + return severity_mapping.get(1) + + +def get_severity(result, rule): + severity = result.get("level") + if severity is None and rule is not None: + # get the severity from the rule + if "defaultConfiguration" in rule: + severity = rule["defaultConfiguration"].get("level") + + if severity == "note": + return "Info" + if severity == "warning": + return "Medium" + if severity == "error": + return "High" + return "Medium" + + +def get_items_from_result(result, rules, artifacts, run_date): + # see + # https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html + # / 3.27.9 + kind = result.get("kind", "fail") + if kind != "fail": + return None + + # if finding is suppressed, mark it as False Positive + # Note: see + # https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/sarif-v2.0-csprd02.html#_Toc10127852 + suppressed = False + if result.get("suppressions"): + suppressed = True + + # if there is a location get all files into files list + + files = [] + + if "locations" in result: + for location in result["locations"]: + + file_path = None + line = None + + if "physicalLocation" in location: + file_path = location["physicalLocation"]["artifactLocation"]["uri"] + + # 'region' attribute is optionnal + if "region" in location["physicalLocation"]: + # https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/sarif-v2.0-csprd02.html / 3.30.1 + # need to check whether it is byteOffset + if "byteOffset" in location["physicalLocation"]["region"]: + pass + else: + line = location["physicalLocation"]["region"]["startLine"] + + files.append((file_path, line, location)) + + if not files: + files.append((None, None, None)) + + result_items = [] + + for file_path, line, location in files: + + # test rule link + rule = rules.get(result.get("ruleId")) + + finding = Finding( + title=get_title(result, rule), + severity=get_severity(result, rule), + description=get_description(result, rule, location), + static_finding=False, # by definition + dynamic_finding=True, # by definition + false_p=suppressed, + active=not suppressed, + file_path=file_path, + line=line, + references=get_references(rule), + ) + + if "ruleId" in result: + finding.vuln_id_from_tool = result["ruleId"] + # for now we only support when the id of the rule is a CVE + if cve_try(result["ruleId"]): + finding.unsaved_vulnerability_ids = [cve_try(result["ruleId"])] + # some time the rule id is here but the tool doesn't define it + if rule is not None: + cwes_extracted = get_rule_cwes(rule) + # Find CWEs in Mayhem SARIF reports + cwes_extracted.extend(get_result_cwes_mcode(result)) + if len(cwes_extracted) > 0: + finding.cwe = cwes_extracted[-1] + + # Some tools such as GitHub or Grype return the severity in properties + # instead + if "properties" in rule and "security-severity" in rule["properties"]: + try: + cvss = float(rule["properties"]["security-severity"]) + severity = cvss_to_severity(cvss) + finding.cvssv3_score = cvss + finding.severity = severity + except ValueError: + if rule["properties"]["security-severity"].lower().capitalize() in {"Info", "Low", "Medium", "High", "Critical"}: + finding.severity = rule["properties"]["security-severity"].lower().capitalize() + else: + finding.severity = "Info" + + # manage the case that some tools produce CWE as properties of the result + cwes_properties_extracted = get_result_cwes_properties(result) + if len(cwes_properties_extracted) > 0: + finding.cwe = cwes_properties_extracted[-1] + + # manage fixes provided in the report + if "fixes" in result: + finding.mitigation = "\n".join( + [fix.get("description", {}).get("text") for fix in result["fixes"]], + ) + + if run_date: + finding.date = run_date + + # manage tags provided in the report and rule and remove duplicated + tags = list(set(get_properties_tags(rule) + get_properties_tags(result))) + tags = [s.removeprefix("external/cwe/") for s in tags] + finding.tags = tags + + # manage fingerprints + # fingerprinting in SARIF is more complete than in current implementation + # SARIF standard make it possible to have multiple version in the same report + # for now we just take the first one and keep the format to be able to + # compare it + if result.get("fingerprints"): + hashes = get_fingerprints_hashes(result["fingerprints"]) + first_item = next(iter(hashes.items())) + finding.unique_id_from_tool = first_item[1]["value"] + elif result.get("partialFingerprints"): + # for this one we keep an order to have id that could be compared + hashes = get_fingerprints_hashes(result["partialFingerprints"]) + sorted_hashes = sorted(hashes.keys()) + finding.unique_id_from_tool = "|".join( + [f'{key}:{hashes[key]["value"]}' for key in sorted_hashes], + ) + + result_items.append(finding) + + return result_items + + +def get_fingerprints_hashes(values): + """ + Method that generate a `unique_id_from_tool` data from the `fingerprints` attribute. + - for now, we take the value of the last version of the first hash method. + """ + fingerprints = {} + for key in values: + if "/" in key: + key_method = key.split("/")[-2] + key_method_version = int(key.split("/")[-1].replace("v", "")) + else: + key_method = key + key_method_version = 0 + value = values[key] + if fingerprints.get(key_method): + if fingerprints[key_method]["version"] < key_method_version: + fingerprints[key_method] = { + "version": key_method_version, + "value": value, + } + else: + fingerprints[key_method] = { + "version": key_method_version, + "value": value, + } + return fingerprints diff --git a/unittests/scans/mayhem/mayhem_api_many_vulns.sarif b/unittests/scans/mayhem/mayhem_api_many_vulns.sarif new file mode 100644 index 00000000000..2b5596a0533 --- /dev/null +++ b/unittests/scans/mayhem/mayhem_api_many_vulns.sarif @@ -0,0 +1,1705 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "semanticVersion": "2.31.2", + "name": "mAPI", + "fullName": "Mayhem for API", + "rules": [ + { + "id": "internal-server-error (com.fasterxml.jackson.databind.JsonMappingException)", + "shortDescription": { + "text": "Internal Server Error (com.fasterxml.jackson.databind.JsonMappingException)" + }, + "fullDescription": { + "text": "The API returned an Internal Server Error, indicating an unhandled exception." + }, + "help": { + "text": "Add additional error handling mechanisms for the errors encountered. Ideally, \nthe error would return a 4xx status like '400 Bad Request', and provide \nmeaningful details to end-users.\n\nAlso, ensure that in production those error messages are not providing\ninformation useful to an attacker, like a stacktrace.\n" + }, + "defaultConfiguration": { + "level": "error" + }, + "properties": { + "tags": [ + "CWE-20", + "CWE-391", + "CWE-550" + ] + } + }, + { + "id": "internal-server-error (io.swagger.oas.inflector.utils.ApiException)", + "shortDescription": { + "text": "Internal Server Error (io.swagger.oas.inflector.utils.ApiException)" + }, + "fullDescription": { + "text": "The API returned an Internal Server Error, indicating an unhandled exception." + }, + "help": { + "text": "Add additional error handling mechanisms for the errors encountered. Ideally, \nthe error would return a 4xx status like '400 Bad Request', and provide \nmeaningful details to end-users.\n\nAlso, ensure that in production those error messages are not providing\ninformation useful to an attacker, like a stacktrace.\n" + }, + "defaultConfiguration": { + "level": "error" + }, + "properties": { + "tags": [ + "CWE-20", + "CWE-391", + "CWE-550" + ] + } + }, + { + "id": "invalid-request-spec ($.complete: expected boolean)", + "shortDescription": { + "text": "Invalid Request Spec ($.complete: expected boolean)" + }, + "fullDescription": { + "text": "The server accepted a payload which wasn't valid according to the provided spec." + }, + "help": { + "text": "\nMake extra sure that unexpectedly successful payloads don't represent more\nserious defects, security or otherwise. Once you're satisfied, adjust the\naffected endoint's \"requestBody\" field to be in sync with what the code actually\nallows.\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "Security", + "CWE-573", + "CWE-20", + "OWASP-API6" + ] + } + }, + { + "id": "invalid-request-spec ($.petId: expected integer)", + "shortDescription": { + "text": "Invalid Request Spec ($.petId: expected integer)" + }, + "fullDescription": { + "text": "The server accepted a payload which wasn't valid according to the provided spec." + }, + "help": { + "text": "\nMake extra sure that unexpectedly successful payloads don't represent more\nserious defects, security or otherwise. Once you're satisfied, adjust the\naffected endoint's \"requestBody\" field to be in sync with what the code actually\nallows.\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "Security", + "CWE-573", + "CWE-20", + "OWASP-API6" + ] + } + }, + { + "id": "invalid-request-spec ($.shipDate: expected string)", + "shortDescription": { + "text": "Invalid Request Spec ($.shipDate: expected string)" + }, + "fullDescription": { + "text": "The server accepted a payload which wasn't valid according to the provided spec." + }, + "help": { + "text": "\nMake extra sure that unexpectedly successful payloads don't represent more\nserious defects, security or otherwise. Once you're satisfied, adjust the\naffected endoint's \"requestBody\" field to be in sync with what the code actually\nallows.\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "Security", + "CWE-573", + "CWE-20", + "OWASP-API6" + ] + } + }, + { + "id": "invalid-response-spec ($: expected object)", + "shortDescription": { + "text": "Invalid Response ($: expected object)" + }, + "fullDescription": { + "text": "The response from the API was not described in the provided spec." + }, + "help": { + "text": "\nMake sure that each specified endpoint's \"responses\" field is in sync with\nwhat the code actually produces:\n * HTTP status codes that the endpoint returns are specified\n * Content-Type that the endpoint returns are specified\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "OWASP-API3", + "CWE-573", + "Security" + ] + } + }, + { + "id": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "shortDescription": { + "text": "Invalid Response (got a non-empty response when an empty response was specified)" + }, + "fullDescription": { + "text": "The response from the API was not described in the provided spec." + }, + "help": { + "text": "\nMake sure that each specified endpoint's \"responses\" field is in sync with\nwhat the code actually produces:\n * HTTP status codes that the endpoint returns are specified\n * Content-Type that the endpoint returns are specified\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "OWASP-API3", + "CWE-573", + "Security" + ] + } + }, + { + "id": "invalid-response-spec (unable to parse body as json)", + "shortDescription": { + "text": "Invalid Response (unable to parse body as json)" + }, + "fullDescription": { + "text": "The response from the API was not described in the provided spec." + }, + "help": { + "text": "\nMake sure that each specified endpoint's \"responses\" field is in sync with\nwhat the code actually produces:\n * HTTP status codes that the endpoint returns are specified\n * Content-Type that the endpoint returns are specified\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "OWASP-API3", + "CWE-573", + "Security" + ] + } + }, + { + "id": "invalid-response-spec (unspecified status code: 200)", + "shortDescription": { + "text": "Invalid Response (unspecified status code: 200)" + }, + "fullDescription": { + "text": "The response from the API was not described in the provided spec." + }, + "help": { + "text": "\nMake sure that each specified endpoint's \"responses\" field is in sync with\nwhat the code actually produces:\n * HTTP status codes that the endpoint returns are specified\n * Content-Type that the endpoint returns are specified\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "OWASP-API3", + "CWE-573", + "Security" + ] + } + }, + { + "id": "invalid-response-spec (unspecified status code: 400)", + "shortDescription": { + "text": "Invalid Response (unspecified status code: 400)" + }, + "fullDescription": { + "text": "The response from the API was not described in the provided spec." + }, + "help": { + "text": "\nMake sure that each specified endpoint's \"responses\" field is in sync with\nwhat the code actually produces:\n * HTTP status codes that the endpoint returns are specified\n * Content-Type that the endpoint returns are specified\n" + }, + "defaultConfiguration": { + "level": "warning" + }, + "properties": { + "tags": [ + "OWASP-API3", + "CWE-573", + "Security" + ] + } + } + ] + } + }, + "results": [ + { + "ruleId": "internal-server-error (io.swagger.oas.inflector.utils.ApiException)", + "message": { + "text": "Internal Server Error in 'PUT /pet'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2633478)", + "markdown": "Internal Server Error (io.swagger.oas.inflector.utils.ApiException) in 'PUT /pet'.\n\n# Sample Request\n\n```http\nPUT /api/v3/pet HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: 338a637b-7eb6-41fd-9fb5-b205c8fcd119\n\n{\"name\":\"http://127.127.127.127:39711\",\"photoUrls\":[]}\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 500 Internal Server Error\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 2de2d8fb127fbe8694a2dd06d540df0a\ndate: Mon, 16 Jun 2025 22:46:47 GMT\nserver: Google Frontend\ncontent-length: 6327\n\n{\n \"code\": 500,\n \"message\": \"There was an error processing your request. It has been logged (ID: 73145dfa268dcf99)\",\n \"stacktrace\": \"io.swagger.oas.inflector.utils.ApiException: There was an error processing your request. It has been logged (ID: 73145dfa268dcf99)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:497)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:79)\\n\\tat sun.reflect.GeneratedMethodAccessor123.invoke(Unknown Source)\\n\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.lang.reflect.Method.invoke(Method.java:498)\\n\\tat org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)\\n\\tat org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)\\n\\tat org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:316)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:298)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:268)\\n\\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)\\n\\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)\\n\\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)\\n\\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)\\n\\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)\\n\\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:864)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)\\n\\tat io.swagger.oas.inflector.utils.CORSFilter.doFilter(CORSFilter.java:36)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:219)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:531)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:319)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:175)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:133)\\n\\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)\\n\\tat java.lang.Thread.run(Thread.java:748)\\nCaused by: java.lang.reflect.InvocationTargetException\\n\\tat sun.reflect.GeneratedMethodAccessor131.invoke(Unknown Source)\\n\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.lang.reflect.Method.invoke(Method.java:498)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:426)\\n\\t... 60 more\\nCaused by: java.lang.NullPointerException\\n\\tat io.swagger.petstore.model.Pet.getId(Pet.java:38)\\n\\tat io.swagger.petstore.controller.PetController.updatePet(PetController.java:184)\\n\\t... 64 more\\n\"\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2633478)\n" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "io/swagger/oas/inflector/controllers/OpenAPIOperationController.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 497, + "endLine": 497 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "io/swagger/oas/inflector/controllers/OpenAPIOperationController.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 79, + "endLine": 79 + } + } + } + } + ] + } + ] + } + ], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "io/swagger/oas/inflector/controllers/OpenAPIOperationController.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 497, + "endLine": 497 + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "message": { + "text": "Invalid Response in 'GET /user/logout'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2556731)", + "markdown": "Invalid Response (got a non-empty response when an empty response was specified) in 'GET /user/logout'.\n\n# Sample Request\n\n```http\nGET /api/v3/user/logout HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: d0769da1-c1a8-4f48-b735-c52affc3b81d\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: abe56e75d013d4f46e0117543acd51fd\ndate: Mon, 16 Jun 2025 22:46:46 GMT\nserver: Google Frontend\ncontent-length: 15\n\nUser logged out\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2556731)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "internal-server-error (com.fasterxml.jackson.databind.JsonMappingException)", + "message": { + "text": "Internal Server Error in 'GET /store/inventory'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2556732)", + "markdown": "Internal Server Error (com.fasterxml.jackson.databind.JsonMappingException) in 'GET /store/inventory'.\n\n# Sample Request\n\n```http\nGET /api/v3/store/inventory HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: e1d1554c-e37d-4175-907f-39615509de79\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 500 Internal Server Error\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: e2a2058aa189c234433c495d4adad7fe\ndate: Mon, 16 Jun 2025 22:46:47 GMT\nserver: Google Frontend\ncontent-length: 6883\n\n{\n \"code\": 500,\n \"message\": \"There was an error processing your request. It has been logged (ID: 9b0d850598d2684f)\",\n \"stacktrace\": \"com.fasterxml.jackson.databind.JsonMappingException: Null key for a Map not allowed in JSON (use a converting NullKeySerializer?) (through reference chain: java.util.HashMap[\\\"null\\\"])\\n\\tat com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:288)\\n\\tat com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1337)\\n\\tat com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1231)\\n\\tat com.fasterxml.jackson.databind.ser.impl.FailingSerializer.serialize(FailingSerializer.java:35)\\n\\tat com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeOptionalFields(MapSerializer.java:785)\\n\\tat com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:677)\\n\\tat com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:637)\\n\\tat com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)\\n\\tat com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)\\n\\tat com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)\\n\\tat com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1513)\\n\\tat com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1005)\\n\\tat com.fasterxml.jackson.jaxrs.base.ProviderBase.writeTo(ProviderBase.java:626)\\n\\tat org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:266)\\n\\tat org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:251)\\n\\tat org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163)\\n\\tat org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:109)\\n\\tat org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163)\\n\\tat org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:85)\\n\\tat org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163)\\n\\tat org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1135)\\n\\tat org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:662)\\n\\tat org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:395)\\n\\tat org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:385)\\n\\tat org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:280)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:316)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:298)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:268)\\n\\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)\\n\\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)\\n\\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)\\n\\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)\\n\\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)\\n\\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:864)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)\\n\\tat io.swagger.oas.inflector.utils.CORSFilter.doFilter(CORSFilter.java:36)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:219)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:531)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:319)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:175)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:133)\\n\\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)\\n\\tat java.lang.Thread.run(Thread.java:748)\\n\"\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2556732)\n" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/JsonMappingException.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 288, + "endLine": 288 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/SerializerProvider.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1337, + "endLine": 1337 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/SerializerProvider.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1231, + "endLine": 1231 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 35, + "endLine": 35 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ser/std/MapSerializer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 785, + "endLine": 785 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ser/std/MapSerializer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 677, + "endLine": 677 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ser/std/MapSerializer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 637, + "endLine": 637 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ser/std/MapSerializer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 33, + "endLine": 33 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 480, + "endLine": 480 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 319, + "endLine": 319 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ObjectWriter.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1513, + "endLine": 1513 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/ObjectWriter.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1005, + "endLine": 1005 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/jaxrs/base/ProviderBase.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 626, + "endLine": 626 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 266, + "endLine": 266 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 251, + "endLine": 251 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 163, + "endLine": 163 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/internal/JsonWithPaddingInterceptor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 109, + "endLine": 109 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 163, + "endLine": 163 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/internal/MappableExceptionWrapperInterceptor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 85, + "endLine": 85 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 163, + "endLine": 163 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/message/internal/MessageBodyFactory.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1135, + "endLine": 1135 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/ServerRuntime.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 662, + "endLine": 662 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/ServerRuntime.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 395, + "endLine": 395 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/ServerRuntime.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 385, + "endLine": 385 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/ServerRuntime.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 280, + "endLine": 280 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/internal/Errors.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 272, + "endLine": 272 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/internal/Errors.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 268, + "endLine": 268 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/internal/Errors.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 316, + "endLine": 316 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/internal/Errors.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 298, + "endLine": 298 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/internal/Errors.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 268, + "endLine": 268 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/process/internal/RequestScope.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 289, + "endLine": 289 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/ServerRuntime.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 256, + "endLine": 256 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/server/ApplicationHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 703, + "endLine": 703 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/servlet/WebComponent.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 416, + "endLine": 416 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/servlet/WebComponent.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 370, + "endLine": 370 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/servlet/ServletContainer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 389, + "endLine": 389 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/servlet/ServletContainer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 342, + "endLine": 342 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/glassfish/jersey/servlet/ServletContainer.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 229, + "endLine": 229 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/servlet/ServletHolder.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 864, + "endLine": 864 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/servlet/ServletHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1655, + "endLine": 1655 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "io/swagger/oas/inflector/utils/CORSFilter.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 36, + "endLine": 36 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/servlet/ServletHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1634, + "endLine": 1634 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/servlet/ServletHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 533, + "endLine": 533 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ScopedHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 146, + "endLine": 146 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/security/SecurityHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 548, + "endLine": 548 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/HandlerWrapper.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 132, + "endLine": 132 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ScopedHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 257, + "endLine": 257 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/session/SessionHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1595, + "endLine": 1595 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ScopedHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 255, + "endLine": 255 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ContextHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1253, + "endLine": 1253 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ScopedHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 203, + "endLine": 203 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/servlet/ServletHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 473, + "endLine": 473 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/session/SessionHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1564, + "endLine": 1564 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ScopedHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 201, + "endLine": 201 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ContextHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 1155, + "endLine": 1155 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ScopedHandler.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 144, + "endLine": 144 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/ContextHandlerCollection.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 219, + "endLine": 219 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/HandlerCollection.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 126, + "endLine": 126 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/handler/HandlerWrapper.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 132, + "endLine": 132 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/Server.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 531, + "endLine": 531 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/HttpChannel.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 352, + "endLine": 352 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/server/HttpConnection.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 260, + "endLine": 260 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/io/AbstractConnection.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 281, + "endLine": 281 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/io/FillInterest.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 102, + "endLine": 102 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/io/ChannelEndPoint.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 118, + "endLine": 118 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/util/thread/strategy/EatWhatYouKill.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 319, + "endLine": 319 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/util/thread/strategy/EatWhatYouKill.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 175, + "endLine": 175 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/util/thread/strategy/EatWhatYouKill.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 133, + "endLine": 133 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/util/thread/ReservedThreadExecutor.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 366, + "endLine": 366 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/util/thread/QueuedThreadPool.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 754, + "endLine": 754 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "org/eclipse/jetty/util/thread/QueuedThreadPool.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 672, + "endLine": 672 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "java/lang/Thread.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 748, + "endLine": 748 + } + } + } + } + ] + } + ] + } + ], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "com/fasterxml/jackson/databind/JsonMappingException.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 288, + "endLine": 288 + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (unspecified status code: 200)", + "message": { + "text": "Invalid Response in 'DELETE /pet/{petId}'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244959)", + "markdown": "Invalid Response (unspecified status code: 200) in 'DELETE /pet/{petId}'.\n\n# Sample Request\n\n```http\nDELETE /api/v3/pet/-7 HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: 6e886ad5-83b2-4e1f-a8d5-392fe1e1b5c8\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 1a93d7210a896b7e568cee6ce4fa06e4\ndate: Mon, 16 Jun 2025 22:46:47 GMT\nserver: Google Frontend\ncontent-length: 11\n\nPet deleted\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244959)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "message": { + "text": "Invalid Response in 'GET /pet/findByStatus'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244983)", + "markdown": "Invalid Response (got a non-empty response when an empty response was specified) in 'GET /pet/findByStatus'.\n\n# Sample Request\n\n```http\nGET /api/v3/pet/findByStatus?status=a}3CLnJ6@d& HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: 69212dd4-0829-4747-9089-2ec78be5be34\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 400 Bad Request\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 21e4e01855b793dcb80c42387b1cf742\ndate: Mon, 16 Jun 2025 22:46:48 GMT\nserver: Google Frontend\ncontent-length: 5790\n\n{\n \"code\": 400,\n \"message\": \"Input error: query parameter `status value `a}3CLnJ6@d` is not in the allowable values `[available, pending, sold]`\",\n \"stacktrace\": \"io.swagger.oas.inflector.utils.ApiException: Input error: query parameter `status value `a}3CLnJ6@d` is not in the allowable values `[available, pending, sold]`\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:419)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:79)\\n\\tat sun.reflect.GeneratedMethodAccessor123.invoke(Unknown Source)\\n\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.lang.reflect.Method.invoke(Method.java:498)\\n\\tat org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)\\n\\tat org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)\\n\\tat org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:316)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:298)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:268)\\n\\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)\\n\\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)\\n\\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)\\n\\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)\\n\\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)\\n\\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:864)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)\\n\\tat io.swagger.oas.inflector.utils.CORSFilter.doFilter(CORSFilter.java:36)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:219)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:531)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:319)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:175)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:133)\\n\\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)\\n\\tat java.lang.Thread.run(Thread.java:748)\\n\"\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244983)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "message": { + "text": "Invalid Response in 'POST /store/order'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244973)", + "markdown": "Invalid Response (got a non-empty response when an empty response was specified) in 'POST /store/order'.\n\n# Sample Request\n\n```http\nPOST /api/v3/store/order HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: a6cb18ac-6643-4404-83c0-3a3e5b57e2a2\n\n{\"id\":10,\"petId\":198772,\"shipDate\":\"'&&exec 3<>/dev/tcp/localhost/45573; echo 7ce019ee-3fec-42bb-83ce-11208f7f6198 >&3\\u0000\"}\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 400 Bad Request\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 1fa66920f02f11a68a1834871b8d9593\ndate: Mon, 16 Jun 2025 22:46:48 GMT\nserver: Google Frontend\ncontent-length: 5702\n\n{\n \"code\": 400,\n \"message\": \"Input error: unable to convert input to io.swagger.petstore.model.Order\",\n \"stacktrace\": \"io.swagger.oas.inflector.utils.ApiException: Input error: unable to convert input to io.swagger.petstore.model.Order\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:419)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:79)\\n\\tat sun.reflect.GeneratedMethodAccessor123.invoke(Unknown Source)\\n\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.lang.reflect.Method.invoke(Method.java:498)\\n\\tat org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)\\n\\tat org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)\\n\\tat org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:316)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:298)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:268)\\n\\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)\\n\\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)\\n\\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)\\n\\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)\\n\\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)\\n\\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:864)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)\\n\\tat io.swagger.oas.inflector.utils.CORSFilter.doFilter(CORSFilter.java:36)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:219)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:531)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:319)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:175)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:133)\\n\\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)\\n\\tat java.lang.Thread.run(Thread.java:748)\\n\"\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244973)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (unspecified status code: 200)", + "message": { + "text": "Invalid Response in 'DELETE /user/{username}'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244991)", + "markdown": "Invalid Response (unspecified status code: 200) in 'DELETE /user/{username}'.\n\n# Sample Request\n\n```http\nDELETE /api/v3/user/Dr.%20%D8%B1%D9%8A%D9%85%D8%A7%D9%86%20%D8%A2%D9%84%20%D8%B3%D8%B9%D9%88%D8%AF HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: db468dd7-4d2f-41ca-b042-e51d301c8b80\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\nx-cloud-trace-context: 23a7d2cbd69cbb5ccb0802388fc038ea\ndate: Mon, 16 Jun 2025 22:46:49 GMT\ncontent-type: text/html\nserver: Google Frontend\ncontent-length: 0\n\nEmpty response\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244991)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "internal-server-error (io.swagger.oas.inflector.utils.ApiException)", + "message": { + "text": "Internal Server Error in 'POST /pet'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2633484)", + "markdown": "Internal Server Error (io.swagger.oas.inflector.utils.ApiException) in 'POST /pet'.\n\n# Sample Request\n\n```http\nPOST /api/v3/pet HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: 25d89b51-8b2e-40bf-9945-11ee3bab3b85\n\n{\"name\":\"doggie\",\"photoUrls\":[\"X$H-834~L8Q\\\\\\\"6k\",\"򞢑񂐟򖘨󙘼쐐򠟟\",\"du4o;L+\\\"k7#(}E5H\",\"𭀝󊕭򨷦񽃏񁇌𻂊򝕨\",\"񔢺򠈧񈯁񖿂􌔄񱅨󵉈\",\"\",\"Hg@FY~xHnX\",\"󒐶񽌨򐅶􋑖򃠡񅖈򺠧\",\"ZQ\",\"%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\%25%32%65%25%32%65\\\\\\\\etc/passwd\"],\"category\":{}}\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 500 Internal Server Error\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 2633c7c7f7767a9f9641249756b97b3c\ndate: Mon, 16 Jun 2025 22:46:50 GMT\nserver: Google Frontend\ncontent-length: 6388\n\n{\n \"code\": 500,\n \"message\": \"There was an error processing your request. It has been logged (ID: 4974fcbf91d3b22f)\",\n \"stacktrace\": \"io.swagger.oas.inflector.utils.ApiException: There was an error processing your request. It has been logged (ID: 4974fcbf91d3b22f)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:497)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:79)\\n\\tat sun.reflect.GeneratedMethodAccessor123.invoke(Unknown Source)\\n\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.lang.reflect.Method.invoke(Method.java:498)\\n\\tat org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)\\n\\tat org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)\\n\\tat org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:316)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:298)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:268)\\n\\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)\\n\\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)\\n\\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)\\n\\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)\\n\\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)\\n\\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:864)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)\\n\\tat io.swagger.oas.inflector.utils.CORSFilter.doFilter(CORSFilter.java:36)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:219)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:531)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:319)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:175)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:133)\\n\\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)\\n\\tat java.lang.Thread.run(Thread.java:748)\\nCaused by: java.lang.reflect.InvocationTargetException\\n\\tat sun.reflect.GeneratedMethodAccessor182.invoke(Unknown Source)\\n\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.lang.reflect.Method.invoke(Method.java:498)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:426)\\n\\t... 60 more\\nCaused by: java.lang.NullPointerException\\n\\tat io.swagger.petstore.model.Pet.getId(Pet.java:38)\\n\\tat io.swagger.petstore.data.PetData.addPet(PetData.java:104)\\n\\tat io.swagger.petstore.controller.PetController.addPet(PetController.java:164)\\n\\t... 64 more\\n\"\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2633484)\n" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "io/swagger/oas/inflector/controllers/OpenAPIOperationController.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 497, + "endLine": 497 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "io/swagger/oas/inflector/controllers/OpenAPIOperationController.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 79, + "endLine": 79 + } + } + } + } + ] + } + ] + } + ], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "io/swagger/oas/inflector/controllers/OpenAPIOperationController.java", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": 497, + "endLine": 497 + } + } + } + ] + }, + { + "ruleId": "invalid-request-spec ($.shipDate: expected string)", + "message": { + "text": "Invalid Request Spec in 'POST /store/order'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1284267)", + "markdown": "Invalid Request Spec ($.shipDate: expected string) in 'POST /store/order'.\n\n# Sample Request\n\n```http\nPOST /api/v3/store/order HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: 683ffb52-d478-486f-8b3a-bc503f1de1a2\n\n{\"petId\":198772,\"id\":10,\"shipDate\":null,\"status\":\"laceQ\"}\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 3c7959148c25b67ba3e158221cc1a773\ndate: Mon, 16 Jun 2025 22:46:50 GMT\nserver: Google Frontend\ncontent-length: 71\n\n{\n \"id\": 10,\n \"petId\": 198772,\n \"quantity\": 0,\n \"status\": \"laceQ\",\n \"complete\": false\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1284267)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (unspecified status code: 200)", + "message": { + "text": "Invalid Response in 'DELETE /store/order/{orderId}'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1357961)", + "markdown": "Invalid Response (unspecified status code: 200) in 'DELETE /store/order/{orderId}'.\n\n# Sample Request\n\n```http\nDELETE /api/v3/store/order/10 HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: 1d772090-fba7-4101-9617-0e16dae85548\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\nx-cloud-trace-context: 21964b4bcd417884a8ae9933fbcdfeb5\ndate: Mon, 16 Jun 2025 22:46:49 GMT\ncontent-type: text/html\nserver: Google Frontend\ncontent-length: 0\n\nEmpty response\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1357961)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec ($: expected object)", + "message": { + "text": "Invalid Response in 'POST /user/createWithList'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2634466)", + "markdown": "Invalid Response ($: expected object) in 'POST /user/createWithList'.\n\n# Sample Request\n\n```http\nPOST /api/v3/user/createWithList HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: f8975815-0dd0-4f6f-bf35-14cfcc09edfb\n\n[{\"firstName\":\"str\",\"password\":\"12345\"},{\"firstName\":\"str\",\"userStatus\":1},{\"password\":\"str\"},{\"password\":\"str\"}]\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 3fbe37401a8079e01bb6391d45bec8f5\ndate: Mon, 16 Jun 2025 22:46:51 GMT\nserver: Google Frontend\ncontent-length: 186\n\n[\n {\n \"id\": 0,\n \"firstName\": \"str\",\n \"password\": \"12345\",\n \"userStatus\": 0\n },\n {\n \"id\": 0,\n \"firstName\": \"str\",\n \"userStatus\": 1\n },\n {\n \"id\": 0,\n \"password\": \"str\",\n \"userStatus\": 0\n },\n {\n \"id\": 0,\n \"password\": \"str\",\n \"userStatus\": 0\n }\n]\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2634466)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-request-spec ($.complete: expected boolean)", + "message": { + "text": "Invalid Request Spec in 'POST /store/order'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1320926)", + "markdown": "Invalid Request Spec ($.complete: expected boolean) in 'POST /store/order'.\n\n# Sample Request\n\n```http\nPOST /api/v3/store/order HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: 199f604a-3693-4ccb-8e12-f59b05e4c86c\n\n{\"complete\":null,\"quantity\":-6}\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 7f05980a6e20992917ad2159f73ddc15\ndate: Mon, 16 Jun 2025 22:46:52 GMT\nserver: Google Frontend\ncontent-length: 49\n\n{\n \"id\": 0,\n \"petId\": 0,\n \"quantity\": -6,\n \"complete\": false\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1320926)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "message": { + "text": "Invalid Response in 'GET /pet/findByTags'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1245004)", + "markdown": "Invalid Response (got a non-empty response when an empty response was specified) in 'GET /pet/findByTags'.\n\n# Sample Request\n\n```http\nGET /api/v3/pet/findByTags HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: 6639c45e-2306-42b5-a19f-d7a1d1154b1e\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 400 Bad Request\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 18eb5a816717ffed8c3b9bdb12463691\ndate: Mon, 16 Jun 2025 22:46:54 GMT\nserver: Google Frontend\ncontent-length: 28\n\nNo tags provided. Try again?\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1245004)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-request-spec ($.petId: expected integer)", + "message": { + "text": "Invalid Request Spec in 'POST /store/order'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1320909)", + "markdown": "Invalid Request Spec ($.petId: expected integer) in 'POST /store/order'.\n\n# Sample Request\n\n```http\nPOST /api/v3/store/order HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: f6b40945-a8a2-48cf-a574-0d4d18768d47\n\n{\"petId\":null,\"id\":10,\"shipDate\":\"2008-06-20T02:12:30.213927950+00:00\"}\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 58ec3e724a49e54f3a5a0711f495c453\ndate: Mon, 16 Jun 2025 22:46:54 GMT\nserver: Google Frontend\ncontent-length: 92\n\n{\n \"id\": 10,\n \"petId\": 0,\n \"quantity\": 0,\n \"shipDate\": \"2008-06-20T02:12:30.213+00:00\",\n \"complete\": false\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1320909)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (unable to parse body as json)", + "message": { + "text": "Invalid Response in 'GET /user/login'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2556735)", + "markdown": "Invalid Response (unable to parse body as json) in 'GET /user/login'.\n\n# Sample Request\n\n```http\nGET /api/v3/user/login?username=Willa Herzog& HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: da52b8b9-e937-48be-a370-750369364353\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\nx-rate-limit: 5000\nx-expires-after: Mon Jun 16 23:46:54 GMT 2025\ncontent-type: application/json\nx-cloud-trace-context: 627920cfa1ef35f1303251aa0816ece3\ndate: Mon, 16 Jun 2025 22:46:54 GMT\nserver: Google Frontend\ncontent-length: 43\n\nLogged in user session: 9055842670202532864\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2556735)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (unspecified status code: 200)", + "message": { + "text": "Invalid Response in 'POST /pet/{petId}'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244968)", + "markdown": "Invalid Response (unspecified status code: 200) in 'POST /pet/{petId}'.\n\n# Sample Request\n\n```http\nPOST /api/v3/pet/6?name=9%VwIo|!o\"9{ia1w%&status=\";exec 3<>/dev/tcp/localhost/45573; echo 539bf285-c152-446a-91cf-0acd0d9f8b6e >&3\u0000& HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: 5091312c-f3da-4044-b935-a6a0b5b346fc\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 5d6439523cd49996d8696f0629bdbb3d\ndate: Mon, 16 Jun 2025 22:46:57 GMT\nserver: Google Frontend\ncontent-length: 314\n\n{\n \"id\": 6,\n \"category\": {\n \"id\": 0\n },\n \"name\": \"9%VwIo|!o\\\"9{ia1w%\",\n \"photoUrls\": [\n \"J#Ziia:m5WuL\\\\$\",\n \"'9JAslL'3YwUv\",\n \"c~BebqgWxFm\",\n \"}qQ\",\n \"WMf3>k;x\",\n \"str\",\n \"^F_>>>lw(J{z-h!N]6\",\n \"http://[0:0:0:0:0:ffff:127.0.0.1]:34213\"\n ],\n \"tags\": [],\n \"status\": \"\\\";exec 3<>/dev/tcp/localhost/45573; echo 539bf285-c152-446a-91cf-0acd0d9f8b6e >&3\\u0000\"\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1244968)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (unspecified status code: 400)", + "message": { + "text": "Invalid Response in 'POST /pet/{petId}'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1512637)", + "markdown": "Invalid Response (unspecified status code: 400) in 'POST /pet/{petId}'.\n\n# Sample Request\n\n```http\nPOST /api/v3/pet/198772?status=񜚏񌧛󭮍󓔓񛂮𴛈􇸄& HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: d9d18fe3-1077-43f6-a46f-8164dd39c2dc\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 400 Bad Request\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 084d65046e4e959386457488ea618ef3\ndate: Mon, 16 Jun 2025 22:46:58 GMT\nserver: Google Frontend\ncontent-length: 28\n\nNo Name provided. Try again?\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1512637)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "message": { + "text": "Invalid Response in 'DELETE /user/{username}'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2633489)", + "markdown": "Invalid Response (got a non-empty response when an empty response was specified) in 'DELETE /user/{username}'.\n\n# Sample Request\n\n```http\nDELETE /api/v3/user/Dr.%20Judson%20GvslaZ1(fl9$j'n0%22fon7J%7DT8H]cY1Yv5%3C'I@K%!Q@m:pecy[5hv%fDoV7Z* HTTP/1.1\ncontent-length: 0\napi_key: \nx-mapi-program-uuid: ab96b290-172c-4b11-9996-ed910810ed42\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 400 Bad Request\ncontent-type: text/html;charset=iso-8859-1\nx-cloud-trace-context: 116ced021a0083a14479bdc4b1e5f343\ndate: Mon, 16 Jun 2025 22:47:04 GMT\nserver: Google Frontend\ncontent-length: 54\n\n

Bad Message 400

reason: Bad Request
\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/2633489)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "message": { + "text": "Invalid Response in 'POST /user/createWithList'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1245015)", + "markdown": "Invalid Response (got a non-empty response when an empty response was specified) in 'POST /user/createWithList'.\n\n# Sample Request\n\n```http\nPOST /api/v3/user/createWithList HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: cd4cf8f2-66fe-41b3-a96a-2597315dcfb2\n\n[{\"email\":\"str\",\"password\":\"str\",\"phone\":\"str\"},{\"id\":3,\"username\":\"Cat 3\",\"lastName\":\"tag4\"},{\"username\":\"Cats\",\"phone\":\"#jiKV%P%JPLes=(j\"},{\"firstName\":\"Cats\"},{\"email\":\"󭓧╏𼍥򯬃𐛬򐭤䡗\",\"userStatus\":\"pending\"},{\"username\":\"Cat 3\",\"id\":3,\"phone\":\"VY&SH1~4pdNx|U^\"}]\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 400 Bad Request\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 55e93e450fd8443267807b55d4b209e0\ndate: Mon, 16 Jun 2025 22:47:05 GMT\nserver: Google Frontend\ncontent-length: 5704\n\n{\n \"code\": 400,\n \"message\": \"Input error: unable to convert input to io.swagger.petstore.model.User[]\",\n \"stacktrace\": \"io.swagger.oas.inflector.utils.ApiException: Input error: unable to convert input to io.swagger.petstore.model.User[]\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:419)\\n\\tat io.swagger.oas.inflector.controllers.OpenAPIOperationController.apply(OpenAPIOperationController.java:79)\\n\\tat sun.reflect.GeneratedMethodAccessor123.invoke(Unknown Source)\\n\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n\\tat java.lang.reflect.Method.invoke(Method.java:498)\\n\\tat org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)\\n\\tat org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)\\n\\tat org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)\\n\\tat org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)\\n\\tat org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)\\n\\tat org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:316)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:298)\\n\\tat org.glassfish.jersey.internal.Errors.process(Errors.java:268)\\n\\tat org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)\\n\\tat org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)\\n\\tat org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)\\n\\tat org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)\\n\\tat org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)\\n\\tat org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)\\n\\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:864)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)\\n\\tat io.swagger.oas.inflector.utils.CORSFilter.doFilter(CORSFilter.java:36)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:219)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:531)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:319)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:175)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:133)\\n\\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)\\n\\tat java.lang.Thread.run(Thread.java:748)\\n\"\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1245015)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + }, + { + "ruleId": "invalid-response-spec (got a non-empty response when an empty response was specified)", + "message": { + "text": "Invalid Response in 'PUT /user/{username}'. [Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1284204)", + "markdown": "Invalid Response (got a non-empty response when an empty response was specified) in 'PUT /user/{username}'.\n\n# Sample Request\n\n```http\nPUT /api/v3/user/str HTTP/1.1\ncontent-type: application/json\napi_key: \nx-mapi-program-uuid: 8eac8fed-4f50-47fc-a072-79a14a0182c8\n\n{\"email\":\"john@email.com\",\"firstName\":\"John\"}\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\naccess-control-allow-methods: GET, POST, DELETE, PUT\naccess-control-allow-headers: Content-Type, api_key, Authorization\naccess-control-expose-headers: Content-Disposition\ncontent-type: application/json\nx-cloud-trace-context: 3001eedd2c4adafa7ecec5055d94df0e\ndate: Mon, 16 Jun 2025 22:47:09 GMT\nserver: Google Frontend\ncontent-length: 67\n\n{\n \"id\": 0,\n \"firstName\": \"John\",\n \"email\": \"john@email.com\",\n \"userStatus\": 0\n}\n```\n\n[Details](https://app.mayhem.security/training/petstore/petstore/-/defects/1284204)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/unittests/scans/mayhem/mayhem_api_no_vulns.sarif b/unittests/scans/mayhem/mayhem_api_no_vulns.sarif new file mode 100644 index 00000000000..a7d9ef04385 --- /dev/null +++ b/unittests/scans/mayhem/mayhem_api_no_vulns.sarif @@ -0,0 +1,17 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "semanticVersion": "2.31.2", + "name": "mAPI", + "fullName": "Mayhem for API", + "rules": [] + } + }, + "results": [] + } + ] +} \ No newline at end of file diff --git a/unittests/scans/mayhem/mayhem_api_one_vuln.sarif b/unittests/scans/mayhem/mayhem_api_one_vuln.sarif new file mode 100644 index 00000000000..2d9abccaced --- /dev/null +++ b/unittests/scans/mayhem/mayhem_api_one_vuln.sarif @@ -0,0 +1,62 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "semanticVersion": "2.31.2", + "name": "mAPI", + "fullName": "Mayhem for API", + "rules": [ + { + "id": "default-credentials", + "shortDescription": { + "text": "Default Credentials Used" + }, + "fullDescription": { + "text": "A protected API returned a successful response using well-known default credentials." + }, + "help": { + "text": "Review the endpoint code to ensure that the required authentication code is\nrunning in all cases. If the endpoint does not require authentication, update\nthe specification to indicate as such.\n\nIf the authentication checks are performed on this endpoint, review the code\nchecking the provided credentials to ensure that default credentials are not\naccepted as valid. Ensure that the user-provided data is validated extensively\nbefore any authentication checks are performed. The sample response provided by\nMayhem for API as part of the bug report may point you in the right direction\nwhen trying to debug the issue. The issue may be in the underlying library, if\nany, you are using to perform validation checks.\n" + }, + "defaultConfiguration": { + "level": "error" + }, + "properties": { + "tags": [ + "Security", + "CWE-287", + "CWE-1390", + "CWE-1391", + "CWE-1392", + "OWASP-API2" + ] + } + } + ] + } + }, + "results": [ + { + "ruleId": "default-credentials", + "message": { + "text": "Default Credentials Used in 'GET /info'. [Details](https://app.mayhem.security/platform-demo/mayhem-demo/api/-/defects/2477915)", + "markdown": "Default Credentials Used in 'GET /info'.\n\n# Sample Request\n\n```http\nGET /info HTTP/1.1\ncontent-length: 0\nauthorization: Basic TFItSVNETjpMUi1JU0RO\nx-mapi-program-uuid: eb3b952d-2088-4711-a86b-7995c83b8579\n\n```\n\n# Sample Response\n\n```http\nHTTP/1.1 200 OK\ndate: Wed, 18 Jun 2025 02:24:15 GMT\nserver: uvicorn\ncontent-length: 35\ncontent-type: application/json\n\n[\n \"version: 1.0.0\\n\",\n \"config: fast\"\n]\n```\n\n[Details](https://app.mayhem.security/platform-demo/mayhem-demo/api/-/defects/2477915)\n" + }, + "codeFlows": [], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "unknown-file", + "uriBaseId": "%SRCROOT%" + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/unittests/scans/mayhem/mayhem_code_many_vulns.sarif b/unittests/scans/mayhem/mayhem_code_many_vulns.sarif new file mode 100755 index 00000000000..174cdd78b70 --- /dev/null +++ b/unittests/scans/mayhem/mayhem_code_many_vulns.sarif @@ -0,0 +1,394 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "Mayhem Code Security", + "rules": [ + { + "id": "MI110", + "name": "UndefinedBehavior", + "defaultConfiguration": { + "level": "error" + }, + "helpUri": "https://www.mayhem.security/", + "shortDescription": { + "text": "The software exhibits behavior that is undefined or unpredictable, often leading to a crash." + } + }, + { + "id": "MI103", + "name": "ImproperResourceManagement", + "defaultConfiguration": { + "level": "error" + }, + "helpUri": "https://www.mayhem.security/", + "shortDescription": { + "text": "The software does not properly restrict reading from or writing to dynamically-managed code resources such as variables, objects, classes, attributes, functions, or executable instructions or statements." + } + }, + { + "id": "MI102", + "name": "ImproperMemoryManagement", + "defaultConfiguration": { + "level": "error" + }, + "helpUri": "https://www.mayhem.security/", + "shortDescription": { + "text": "The software does not sufficiently track and release allocated memory after it has been used, which slowly consumes remaining memory." + } + }, + { + "id": "MI107", + "name": "OutOfBoundsAccess", + "defaultConfiguration": { + "level": "error" + }, + "helpUri": "https://www.mayhem.security/", + "shortDescription": { + "text": "The software accesses data past the end, or before the beginning, of the intended buffer." + } + } + ], + "version": "2.12.0", + "informationUri": "https://www.mayhem.security/" + } + }, + "results": [ + { + "message": { + "markdown": "#0 0x5595b43dea57 in parse_lat_lon src/gps_uploader.c:97\n#1 0x5595b43dee40 in main src/gps_uploader.c:145\n#2 0x7fc924cdfd67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58\n#3 0x7fc924cdfe24 in __libc_start_main_impl ../csu/libc-start.c:360\n#4 0x5595b43de180 in _start (/app/gps_uploader+0x2180) (BuildId: ef91952468b4d0b95713614a2a77d345bbc745ca)\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2307883)", + "text": "TDID-2307883 - Integer Overflow or Wraparound" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 1, + "endLine": 97, + "startColumn": 1, + "startLine": 97 + }, + "artifactLocation": { + "uri": "src/gps_uploader.c" + } + } + } + ], + "taxa": [ + { + "id": "190", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI110" + }, + { + "message": { + "markdown": "#0 0x7ffff7848e5c in __vfscanf_internal at ./stdio-common/vfscanf-internal.c:2320:18\n#1 0x7ffff77f4c82 in __GI_raise at ./signal/../sysdeps/posix/raise.c:27:6\n#2 0x7ffff77dd4f0 in __GI_abort at ./stdlib/abort.c:81:7\n#3 0x7ffff77de32d in _IO_acquire_lock_fct at ./libio/libioP.h:979:6 (inlined by) _IO_peekc_locked at ./libio/peekc.c:37:3\n#4 0x7ffff78529e5 in __vfwscanf_internal at ./stdio-common/vfscanf-internal.c:1480:13\n#5 0x7ffff7854e59 in __vfwscanf_internal at ./stdio-common/vfscanf-internal.c:783:8\n#6 0x7ffff78574ff in __vfwscanf_internal at ./stdio-common/vfscanf-internal.c:2936:7\n#7 0x555555556560 in double_free at /app/src/gps_uploader.c:46:1\n#8 0x55555555660d in vulnerable_c at /app/src/gps_uploader.c:62:5\n#9 0x555555556e61 in main at /app/src/gps_uploader.c:148:6\n#10 0x7ffff77ded68 in __libc_start_call_main at ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3\n#11 0x7ffff77dee25 in call_init at ./csu/../csu/libc-start.c:128:20 (inlined by) __libc_start_main_impl at ./csu/../csu/libc-start.c:347:5\n#12 0x555555556181 in _start+0x21 at ??:0:0\n\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2305663)", + "text": "TDID-2305663 - Double Free" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 18, + "endLine": 2320, + "startColumn": 18, + "startLine": 2320 + }, + "artifactLocation": { + "uri": "./stdio-common/vfscanf-internal.c" + } + } + } + ], + "taxa": [ + { + "id": "415", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI103" + }, + { + "message": { + "markdown": "#0 0x55ba7a2e2316 in integer_overflow_negative src/gps_uploader.c:19\n#1 0x55ba7a2e25df in vulnerable_c src/gps_uploader.c:58\n#2 0x55ba7a2e2e60 in main src/gps_uploader.c:146\n#3 0x7f1b1ef74d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58\n#4 0x7f1b1ef74e24 in __libc_start_main_impl ../csu/libc-start.c:360\n#5 0x55ba7a2e2180 in _start (/app/gps_uploader+0x2180) (BuildId: ef91952468b4d0b95713614a2a77d345bbc745ca)\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2305660)", + "text": "TDID-2305660 - Integer Overflow or Wraparound" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 1, + "endLine": 19, + "startColumn": 1, + "startLine": 19 + }, + "artifactLocation": { + "uri": "src/gps_uploader.c" + } + } + } + ], + "taxa": [ + { + "id": "190", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI110" + }, + { + "message": { + "markdown": "#0 0x55555555656e in stack_exhaustion at /app/src/gps_uploader.c:48:41\n#1 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#2 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#3 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#4 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#5 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#6 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#7 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#8 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#9 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#10 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#11 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#12 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#13 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#14 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#15 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#16 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#17 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#18 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#19 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#20 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#21 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#22 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#23 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#24 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#25 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#26 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#27 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#28 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#29 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#30 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#31 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#32 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#33 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#34 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#35 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#36 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#37 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#38 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#39 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#40 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#41 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#42 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#43 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#44 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#45 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#46 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#47 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#48 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#49 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#50 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#51 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#52 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#53 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#54 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#55 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#56 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#57 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#58 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#59 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#60 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#61 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#62 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#63 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#64 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#65 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#66 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#67 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#68 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#69 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#70 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#71 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#72 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#73 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#74 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#75 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#76 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#77 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#78 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#79 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#80 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#81 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#82 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#83 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#84 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#85 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#86 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#87 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#88 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#89 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#90 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#91 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#92 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#93 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#94 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#95 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#96 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#97 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#98 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#99 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#100 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#101 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#102 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#103 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#104 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#105 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#106 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#107 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#108 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#109 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#110 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#111 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#112 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#113 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#114 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#115 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#116 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#117 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#118 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#119 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#120 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#121 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#122 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#123 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#124 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#125 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#126 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n#127 0x5555555565a4 in stack_exhaustion at /app/src/gps_uploader.c:52:1\n\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2305324)", + "text": "TDID-2305324 - Uncontrolled Resource Consumption" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 41, + "endLine": 48, + "startColumn": 41, + "startLine": 48 + }, + "artifactLocation": { + "uri": "app/src/gps_uploader.c" + } + } + } + ], + "taxa": [ + { + "id": "400", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI102" + }, + { + "message": { + "markdown": "#0 0x55648cb75495 in oob_write src/gps_uploader.c:37\n#1 0x55648cb755fd in vulnerable_c src/gps_uploader.c:60\n#2 0x55648cb75e60 in main src/gps_uploader.c:146\n#3 0x7ffb323f8d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58\n#4 0x7ffb323f8e24 in __libc_start_main_impl ../csu/libc-start.c:360\n#5 0x55648cb75180 in _start (/app/gps_uploader+0x2180) (BuildId: ef91952468b4d0b95713614a2a77d345bbc745ca)\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2305323)", + "text": "TDID-2305323 - Improper Validation of Array Index" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 1, + "endLine": 37, + "startColumn": 1, + "startLine": 37 + }, + "artifactLocation": { + "uri": "src/gps_uploader.c" + } + } + } + ], + "taxa": [ + { + "id": "129", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI107" + }, + { + "message": { + "markdown": "#0 0x559a03964396 in oob_read src/gps_uploader.c:28\n#1 0x559a039645ee in vulnerable_c src/gps_uploader.c:59\n#2 0x559a03964e60 in main src/gps_uploader.c:146\n#3 0x7f0994757d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58\n#4 0x7f0994757e24 in __libc_start_main_impl ../csu/libc-start.c:360\n#5 0x559a03964180 in _start (/app/gps_uploader+0x2180) (BuildId: ef91952468b4d0b95713614a2a77d345bbc745ca)\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2305322)", + "text": "TDID-2305322 - Improper Validation of Array Index" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 1, + "endLine": 28, + "startColumn": 1, + "startLine": 28 + }, + "artifactLocation": { + "uri": "src/gps_uploader.c" + } + } + } + ], + "taxa": [ + { + "id": "129", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI107" + }, + { + "message": { + "markdown": "#0 0x55bd9f855a9c in parse_lat_lon src/gps_uploader.c:98\n#1 0x55bd9f855e40 in main src/gps_uploader.c:145\n#2 0x7fc35fcb5d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58\n#3 0x7fc35fcb5e24 in __libc_start_main_impl ../csu/libc-start.c:360\n#4 0x55bd9f855180 in _start (/app/gps_uploader+0x2180) (BuildId: ef91952468b4d0b95713614a2a77d345bbc745ca)\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2175978)", + "text": "TDID-2175978 - Integer Overflow or Wraparound" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 1, + "endLine": 98, + "startColumn": 1, + "startLine": 98 + }, + "artifactLocation": { + "uri": "src/gps_uploader.c" + } + } + } + ], + "taxa": [ + { + "id": "190", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI110" + }, + { + "message": { + "markdown": "#0 0x55d6929d62a0 in divide_by_zero src/gps_uploader.c:11\n#1 0x55d6929d65d0 in vulnerable_c src/gps_uploader.c:57\n#2 0x55d6929d6e60 in main src/gps_uploader.c:146\n#3 0x7f6b72682d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58\n#4 0x7f6b72682e24 in __libc_start_main_impl ../csu/libc-start.c:360\n#5 0x55d6929d6180 in _start (/app/gps_uploader+0x2180) (BuildId: ef91952468b4d0b95713614a2a77d345bbc745ca)\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/mayhem-demo/car/77?defect=2175973)", + "text": "TDID-2175973 - Divide By Zero" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 1, + "endLine": 11, + "startColumn": 1, + "startLine": 11 + }, + "artifactLocation": { + "uri": "src/gps_uploader.c" + } + } + } + ], + "taxa": [ + { + "id": "369", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI110" + } + ], + "taxonomies": [ + { + "name": "CWE", + "organization": "MITRE", + "taxa": [ + { + "id": "190", + "help": { + "text": "The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control." + }, + "name": "Integer Overflow or Wraparound", + "defaultConfiguration": { + "level": "error" + }, + "fullDescription": { + "text": "The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control." + }, + "shortDescription": { + "text": "The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control." + } + }, + { + "id": "415", + "help": { + "text": "The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations." + }, + "name": "Double Free", + "defaultConfiguration": { + "level": "error" + }, + "fullDescription": { + "text": "The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations." + }, + "shortDescription": { + "text": "The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations." + } + }, + { + "id": "400", + "help": { + "text": "The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources." + }, + "name": "Uncontrolled Resource Consumption", + "defaultConfiguration": { + "level": "error" + }, + "fullDescription": { + "text": "The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources." + }, + "shortDescription": { + "text": "The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources." + } + }, + { + "id": "129", + "help": { + "text": "The product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array." + }, + "name": "Improper Validation of Array Index", + "defaultConfiguration": { + "level": "error" + }, + "fullDescription": { + "text": "The product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array." + }, + "shortDescription": { + "text": "The product uses untrusted input when calculating or using an array index, but the product does not validate or incorrectly validates the index to ensure the index references a valid position within the array." + } + }, + { + "id": "369", + "help": { + "text": "The product divides a value by zero." + }, + "name": "Divide By Zero", + "defaultConfiguration": { + "level": "error" + }, + "fullDescription": { + "text": "The product divides a value by zero." + }, + "shortDescription": { + "text": "The product divides a value by zero." + } + } + ], + "shortDescription": { + "text": "The MITRE Common Weakness Enumeration" + } + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Documents/CommitteeSpecifications/2.1.0/sarif-schema-2.1.0.json" +} \ No newline at end of file diff --git a/unittests/scans/mayhem/mayhem_code_no_vulns.sarif b/unittests/scans/mayhem/mayhem_code_no_vulns.sarif new file mode 100755 index 00000000000..88051cb774d --- /dev/null +++ b/unittests/scans/mayhem/mayhem_code_no_vulns.sarif @@ -0,0 +1,27 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "Mayhem Code Security", + "rules": [], + "version": "2.12.0", + "informationUri": "https://www.mayhem.security/" + } + }, + "results": [], + "taxonomies": [ + { + "name": "CWE", + "organization": "MITRE", + "taxa": [], + "shortDescription": { + "text": "The MITRE Common Weakness Enumeration" + } + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Documents/CommitteeSpecifications/2.1.0/sarif-schema-2.1.0.json" +} \ No newline at end of file diff --git a/unittests/scans/mayhem/mayhem_code_one_vuln.sarif b/unittests/scans/mayhem/mayhem_code_one_vuln.sarif new file mode 100755 index 00000000000..4de0de4250d --- /dev/null +++ b/unittests/scans/mayhem/mayhem_code_one_vuln.sarif @@ -0,0 +1,87 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "Mayhem Code Security", + "rules": [ + { + "id": "MI101", + "name": "ImproperInputValidation", + "defaultConfiguration": { + "level": "error" + }, + "helpUri": "https://www.mayhem.security/", + "shortDescription": { + "text": "The software receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly." + } + } + ], + "version": "2.12.0", + "informationUri": "https://www.mayhem.security/" + } + }, + "results": [ + { + "message": { + "markdown": "#0 0x7ffff7e47b1c in vfwprintf+0x23ec at ??:0:0\n#1 0x7ffff7dee26e in raise+0x1e at ??:0:0\n#2 0x7ffff7dd18ff in abort+0xdf at ??:0:0\n#3 0x5555555559a9 in ?? at ??:0:0\n#4 0x555555555ab8 in ?? at ??:0:0\n#5 0x7ffff7dd31ca in __libc_init_first+0x8a at ??:0:0\n#6 0x7ffff7dd328b in __libc_start_main+0x8b at ??:0:0\n#7 0x555555555875 in ?? at ??:0:0\n\n\n[Details](https://app.mayhem.security:443/forallsecure-demo/bazel-rules/mayhemit/10?defect=2211925)", + "text": "TDID-2211925 - Improper Input Validation" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "endColumn": 1, + "endLine": 1, + "startColumn": 1, + "startLine": 1 + }, + "artifactLocation": { + "uri": "unknown_file" + } + } + } + ], + "taxa": [ + { + "id": "20", + "toolComponent": { + "name": "CWE" + } + } + ], + "ruleId": "MI101" + } + ], + "taxonomies": [ + { + "name": "CWE", + "organization": "MITRE", + "taxa": [ + { + "id": "20", + "help": { + "text": "The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly." + }, + "name": "Improper Input Validation", + "defaultConfiguration": { + "level": "error" + }, + "fullDescription": { + "text": "The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly." + }, + "shortDescription": { + "text": "The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly." + } + } + ], + "shortDescription": { + "text": "The MITRE Common Weakness Enumeration" + } + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Documents/CommitteeSpecifications/2.1.0/sarif-schema-2.1.0.json" +} \ No newline at end of file diff --git a/unittests/scans/test_mayhem_parser.py b/unittests/scans/test_mayhem_parser.py new file mode 100644 index 00000000000..1052a348f91 --- /dev/null +++ b/unittests/scans/test_mayhem_parser.py @@ -0,0 +1,71 @@ +from dojo.models import Finding, Test +from dojo.tools.mayhem.parser import MayhemParser +from unittests.dojo_test_case import DojoTestCase, get_unit_tests_scans_path + + +class TestMayhemParser(DojoTestCase): + def common_checks(self, finding): + self.assertLessEqual(len(finding.title), 250) + self.assertIn(finding.severity, Finding.SEVERITIES) + if finding.cwe: + self.assertIsInstance(finding.cwe, int) + self.assertEqual(False, finding.static_finding) # Mayhem is DAST! + self.assertEqual(True, finding.dynamic_finding) # Mayhem is DAST! + + def test_mcode_many_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_code_many_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(8, len(findings)) + for finding in findings: + self.common_checks(finding) + + def test_mapi_many_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_api_many_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(20, len(findings)) + for finding in findings: + self.common_checks(finding) + + def test_mcode_one_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_code_one_vuln.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(1, len(findings)) + finding = findings[0] + self.common_checks(finding) + self.assertEqual(20, finding.cwe) + + def test_mapi_one_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_api_one_vuln.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(1, len(findings)) + finding = findings[0] + self.common_checks(finding) + self.assertEqual(1392, finding.cwe) + + def test_mcode_no_vulns_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_code_no_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(0, len(findings)) + + def test_mapi_no_vulns_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_api_no_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(0, len(findings)) \ No newline at end of file diff --git a/unittests/tools/test_mayhem_parser.py b/unittests/tools/test_mayhem_parser.py new file mode 100644 index 00000000000..1052a348f91 --- /dev/null +++ b/unittests/tools/test_mayhem_parser.py @@ -0,0 +1,71 @@ +from dojo.models import Finding, Test +from dojo.tools.mayhem.parser import MayhemParser +from unittests.dojo_test_case import DojoTestCase, get_unit_tests_scans_path + + +class TestMayhemParser(DojoTestCase): + def common_checks(self, finding): + self.assertLessEqual(len(finding.title), 250) + self.assertIn(finding.severity, Finding.SEVERITIES) + if finding.cwe: + self.assertIsInstance(finding.cwe, int) + self.assertEqual(False, finding.static_finding) # Mayhem is DAST! + self.assertEqual(True, finding.dynamic_finding) # Mayhem is DAST! + + def test_mcode_many_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_code_many_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(8, len(findings)) + for finding in findings: + self.common_checks(finding) + + def test_mapi_many_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_api_many_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(20, len(findings)) + for finding in findings: + self.common_checks(finding) + + def test_mcode_one_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_code_one_vuln.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(1, len(findings)) + finding = findings[0] + self.common_checks(finding) + self.assertEqual(20, finding.cwe) + + def test_mapi_one_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_api_one_vuln.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(1, len(findings)) + finding = findings[0] + self.common_checks(finding) + self.assertEqual(1392, finding.cwe) + + def test_mcode_no_vulns_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_code_no_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(0, len(findings)) + + def test_mapi_no_vulns_report(self): + with ( + get_unit_tests_scans_path("mayhem") / "mayhem_api_no_vulns.sarif" + ).open(encoding="utf-8") as testfile: + parser = MayhemParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(0, len(findings)) \ No newline at end of file From dd75e9e9b39e1a3d93be7c55264f38b593b0a0f0 Mon Sep 17 00:00:00 2001 From: xansec Date: Wed, 18 Jun 2025 00:13:11 -0400 Subject: [PATCH 2/9] linter checks --- dojo/tools/mayhem/parser.py | 39 +++++++++++++-------------- unittests/tools/test_mayhem_parser.py | 4 +-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/dojo/tools/mayhem/parser.py b/dojo/tools/mayhem/parser.py index 0e4e2c634c3..5cf23c16dd9 100644 --- a/dojo/tools/mayhem/parser.py +++ b/dojo/tools/mayhem/parser.py @@ -92,7 +92,7 @@ def get_scan_types(self): return ["Mayhem SARIF Report"] def get_label_for_scan_types(self, scan_type): - return scan_type + return scan_type def get_description_for_scan_types(self, scan_type): return "Mayhem SARIF reports from code or API runs." @@ -189,6 +189,7 @@ def get_result_cwes_properties(result): search_cwe(value, cwes) return cwes + def get_result_cwes_mcode(result): """Mayhem SARIF reports include CWE property under taxa.toolComponent.name and number under taxa.id""" cwes = [] @@ -207,21 +208,20 @@ def get_artifacts(run): artifacts[tree_artifact.get("index", custom_index)] = tree_artifact return artifacts + def clean_mayhem_title_text(text): - """ - Clean the title text for Mayhem SARIF reports. - """ + """Clean the title text for Mayhem SARIF reports.""" if not text: return "" - + # Remove links (and add limit to avoid catastrophic backtracking) link_regex = r"\[[^\]]{1,100}?\]\([^)]{1,200}?\)" text = re.sub(link_regex, "", text) - + # Remove URL encoded characters url_encoding_regex = r"&#x\d+;" text = re.sub(url_encoding_regex, "", text) - + # Remove single or double quotes quotes_regex = r"[\"']" text = re.sub(quotes_regex, "", text) @@ -229,7 +229,7 @@ def clean_mayhem_title_text(text): # Remove TDID tdid_regex = r"TDID-\d+\s*-\s*|TDID-\d+-" text = re.sub(tdid_regex, "", text) - + return text.strip() @@ -239,8 +239,8 @@ def get_message_from_multiformatMessageString(data, rule, content_type="text"): See here for the specification: https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317468 """ - if content_type not in ["text", "markdown"]: - raise ValueError(f"Unexpected content type: {content_type}") + if content_type not in {"text", "markdown"}: + raise ValueError("Unexpected message content; expected 'text' or 'markdown'.") if content_type == "markdown" and "markdown" in data: # handle markdown content markdown = data.get("markdown") @@ -248,10 +248,10 @@ def get_message_from_multiformatMessageString(data, rule, content_type="text"): heading_regex = r"^#+\s*" markdown = re.sub(heading_regex, "", markdown, flags=re.MULTILINE) # replace non-unicode characters with "?" - non_unicode_regex = r'[^\x09\x0A\x0D\x20-\x7E]' - markdown = re.sub(non_unicode_regex, '?', markdown) + non_unicode_regex = r"[^\x09\x0A\x0D\x20-\x7E]" + markdown = re.sub(non_unicode_regex, "?", markdown) return markdown.strip() - elif content_type == "text" and "text" in data: + if content_type == "text" and "text" in data: # handle text content text = data.get("text") if rule is not None and "id" in data: @@ -266,6 +266,7 @@ def get_message_from_multiformatMessageString(data, rule, content_type="text"): else: return "" + def cve_try(val): # Match only the first CVE! cveSearch = re.search(r"(CVE-[0-9]+-[0-9]+)", val, re.IGNORECASE) @@ -297,7 +298,7 @@ def get_title(result, rule): if title is None: msg = "No information found to create a title" raise ValueError(msg) - + # Clean the title text for Mayhem SARIF reports title = clean_mayhem_title_text(title) @@ -374,7 +375,8 @@ def get_codeFlowsDescription(code_flows): description += f"\t{message}\n" - return description + return description + def get_description(result, rule, location): description = "" @@ -400,10 +402,7 @@ def get_description(result, rule, location): fullDescription = get_message_from_multiformatMessageString( rule["fullDescription"], rule, ) - if ( - fullDescription != message - and fullDescription != shortDescription - ): + if (fullDescription != message) and (fullDescription != shortDescription): description += f"**{_('Rule full description')}:** {fullDescription}\n" if "markdown" in result["message"]: markdown = get_message_from_multiformatMessageString( @@ -412,7 +411,7 @@ def get_description(result, rule, location): # Replace "Details" with "Link" in the markdown markdown = markdown.replace("Details", "Link") description += f"**{_('Additional Details')}:**\n{markdown}\n" - description += f"_(Unprintable characters are replaced with '?'; please see Mayhem for full reproducer.)_" + description += "_(Unprintable characters are replaced with '?'; please see Mayhem for full reproducer.)_" if len(result.get("codeFlows", [])) > 0: description += get_codeFlowsDescription(result["codeFlows"]) diff --git a/unittests/tools/test_mayhem_parser.py b/unittests/tools/test_mayhem_parser.py index 1052a348f91..da381004a75 100644 --- a/unittests/tools/test_mayhem_parser.py +++ b/unittests/tools/test_mayhem_parser.py @@ -21,7 +21,7 @@ def test_mcode_many_report(self): self.assertEqual(8, len(findings)) for finding in findings: self.common_checks(finding) - + def test_mapi_many_report(self): with ( get_unit_tests_scans_path("mayhem") / "mayhem_api_many_vulns.sarif" @@ -68,4 +68,4 @@ def test_mapi_no_vulns_report(self): ).open(encoding="utf-8") as testfile: parser = MayhemParser() findings = parser.get_findings(testfile, Test()) - self.assertEqual(0, len(findings)) \ No newline at end of file + self.assertEqual(0, len(findings)) From 2af68e21e13fe063a68d7c90895987d6144a243e Mon Sep 17 00:00:00 2001 From: xansec Date: Wed, 18 Jun 2025 00:20:59 -0400 Subject: [PATCH 3/9] more linter checks --- dojo/tools/mayhem/parser.py | 19 +++---- unittests/scans/test_mayhem_parser.py | 71 --------------------------- 2 files changed, 8 insertions(+), 82 deletions(-) delete mode 100644 unittests/scans/test_mayhem_parser.py diff --git a/dojo/tools/mayhem/parser.py b/dojo/tools/mayhem/parser.py index 5cf23c16dd9..ac39dbda4ab 100644 --- a/dojo/tools/mayhem/parser.py +++ b/dojo/tools/mayhem/parser.py @@ -239,8 +239,6 @@ def get_message_from_multiformatMessageString(data, rule, content_type="text"): See here for the specification: https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317468 """ - if content_type not in {"text", "markdown"}: - raise ValueError("Unexpected message content; expected 'text' or 'markdown'.") if content_type == "markdown" and "markdown" in data: # handle markdown content markdown = data.get("markdown") @@ -263,8 +261,7 @@ def get_message_from_multiformatMessageString(data, rule, content_type="text"): if substitution_str in text and i < len(arguments): text = text.replace(substitution_str, arguments[i]) return text - else: - return "" + return "" def cve_try(val): @@ -405,13 +402,13 @@ def get_description(result, rule, location): if (fullDescription != message) and (fullDescription != shortDescription): description += f"**{_('Rule full description')}:** {fullDescription}\n" if "markdown" in result["message"]: - markdown = get_message_from_multiformatMessageString( - result["message"], rule, content_type="markdown", - ) - # Replace "Details" with "Link" in the markdown - markdown = markdown.replace("Details", "Link") - description += f"**{_('Additional Details')}:**\n{markdown}\n" - description += "_(Unprintable characters are replaced with '?'; please see Mayhem for full reproducer.)_" + markdown = get_message_from_multiformatMessageString( + result["message"], rule, content_type="markdown", + ) + # Replace "Details" with "Link" in the markdown + markdown = markdown.replace("Details", "Link") + description += f"**{_('Additional Details')}:**\n{markdown}\n" + description += "_(Unprintable characters are replaced with '?'; please see Mayhem for full reproducer.)_" if len(result.get("codeFlows", [])) > 0: description += get_codeFlowsDescription(result["codeFlows"]) diff --git a/unittests/scans/test_mayhem_parser.py b/unittests/scans/test_mayhem_parser.py deleted file mode 100644 index 1052a348f91..00000000000 --- a/unittests/scans/test_mayhem_parser.py +++ /dev/null @@ -1,71 +0,0 @@ -from dojo.models import Finding, Test -from dojo.tools.mayhem.parser import MayhemParser -from unittests.dojo_test_case import DojoTestCase, get_unit_tests_scans_path - - -class TestMayhemParser(DojoTestCase): - def common_checks(self, finding): - self.assertLessEqual(len(finding.title), 250) - self.assertIn(finding.severity, Finding.SEVERITIES) - if finding.cwe: - self.assertIsInstance(finding.cwe, int) - self.assertEqual(False, finding.static_finding) # Mayhem is DAST! - self.assertEqual(True, finding.dynamic_finding) # Mayhem is DAST! - - def test_mcode_many_report(self): - with ( - get_unit_tests_scans_path("mayhem") / "mayhem_code_many_vulns.sarif" - ).open(encoding="utf-8") as testfile: - parser = MayhemParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(8, len(findings)) - for finding in findings: - self.common_checks(finding) - - def test_mapi_many_report(self): - with ( - get_unit_tests_scans_path("mayhem") / "mayhem_api_many_vulns.sarif" - ).open(encoding="utf-8") as testfile: - parser = MayhemParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(20, len(findings)) - for finding in findings: - self.common_checks(finding) - - def test_mcode_one_report(self): - with ( - get_unit_tests_scans_path("mayhem") / "mayhem_code_one_vuln.sarif" - ).open(encoding="utf-8") as testfile: - parser = MayhemParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(1, len(findings)) - finding = findings[0] - self.common_checks(finding) - self.assertEqual(20, finding.cwe) - - def test_mapi_one_report(self): - with ( - get_unit_tests_scans_path("mayhem") / "mayhem_api_one_vuln.sarif" - ).open(encoding="utf-8") as testfile: - parser = MayhemParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(1, len(findings)) - finding = findings[0] - self.common_checks(finding) - self.assertEqual(1392, finding.cwe) - - def test_mcode_no_vulns_report(self): - with ( - get_unit_tests_scans_path("mayhem") / "mayhem_code_no_vulns.sarif" - ).open(encoding="utf-8") as testfile: - parser = MayhemParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(0, len(findings)) - - def test_mapi_no_vulns_report(self): - with ( - get_unit_tests_scans_path("mayhem") / "mayhem_api_no_vulns.sarif" - ).open(encoding="utf-8") as testfile: - parser = MayhemParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(0, len(findings)) \ No newline at end of file From 57ffb0662bc98ebfa2c7292aee53ca27930980eb Mon Sep 17 00:00:00 2001 From: xansec Date: Wed, 18 Jun 2025 18:36:12 -0400 Subject: [PATCH 4/9] extend SarifParser instead of copying code --- dojo/tools/mayhem/parser.py | 358 ++++-------------------------------- 1 file changed, 33 insertions(+), 325 deletions(-) diff --git a/dojo/tools/mayhem/parser.py b/dojo/tools/mayhem/parser.py index ac39dbda4ab..da2ae0cff90 100644 --- a/dojo/tools/mayhem/parser.py +++ b/dojo/tools/mayhem/parser.py @@ -1,7 +1,6 @@ import json import logging import re -import textwrap import dateutil.parser from django.utils.translation import gettext as _ @@ -9,94 +8,44 @@ from dojo.models import Finding from dojo.tools.parser_test import ParserTest +from dojo.tools.sarif.parser import SarifParser +from dojo.tools.sarif.parser import ( + get_codeFlowsDescription, + get_snippet, + get_title, + get_severity, + get_references, + cve_try, + get_rule_cwes, + get_result_cwes_properties, + cvss_to_severity, + get_properties_tags, + get_fingerprints_hashes, + get_rules + ) + + logger = logging.getLogger(__name__) CWE_REGEX = r"cwe-\d+" -class MayhemParser: - +class MayhemParser(SarifParser): """ Mayhem SARIF Parser - This is largely lifted from the existing SARIF parser, but with some minor + This class extends the existing SARIF parser, but with some minor modifications to better support the structure of Mayhem SARIF reports. """ - def get_fields(self) -> list[str]: - """ - Return the list of fields used in the Sarif Parser - - Fields: - - title: Made using rule and id from Sarif scanner. - - severity: Set to severity from Sarif Scanner converted to Defect Dojo format. - - description: Made by combining message, location, and rule from Sarif Scanner. - - static_finding: Set to true. - - dynamic_finding: Set to false. - - false_p: Set to true or false based on suppression status from Sarif scanner. - - active: Set to true or false based on suppression status from Sarif scanner. - - file_path: Set to physical location from Sarif scanner. - - line: Set to start line from Sarif scanner. - - vuln_id_from_tool: Set to rule id from Sarif scanner. - - cwe: Set to the cwe values outputted from Sarif Scanner. - - cvssv3: Set to properties and securitiy-severity from Sarif scanner if available. - - cvssv3_score: Set to properties and securitiy-severity from Sarif scanner if available. - - mitigation: Set to altered version of finding's description. - - date: Set to the date outputted from Sarif Scanner converted to datetime. - - tags: Set to tags from Sarif scanner. - - unique_id_from_tool: Set to the hash fingerpring value outputted from Sarif Scanner. - - NOTE: This parser supports tags. - """ - return [ - "title", - "severity", - "description", - "static_finding", - "dynamic_finding", - "false_p", - "active", - "file_path", - "line", - "vuln_id_from_tool", - "cwe", - "cvssv3", - "cvssv3_score", - "mitigation", - "date", - "tags", - "unique_id_from_tool", - ] - - def get_dedupe_fields(self) -> list[str]: - """ - Return the list of dedupe fields used in the Sarif Parser - - Fields: - - title: Made using rule and id from Sarif scanner. - - cwe: Set to the cwe values outputted from Sarif Scanner. - - line: Set to start line from Sarif scanner. - - file_path: Set to physical location from Sarif scanner. - - description: Made by combining message, location, and rule from Sarif Scanner. - - NOTE: uses legacy dedupe: ['title', 'cwe', 'line', 'file_path', 'description'] - """ - return [ - "title", - "cwe", - "line", - "file_path", - "description", - ] - def get_scan_types(self): return ["Mayhem SARIF Report"] - def get_label_for_scan_types(self, scan_type): - return scan_type - - def get_description_for_scan_types(self, scan_type): + def get_description_for_scan_types(self): return "Mayhem SARIF reports from code or API runs." + # Due to mixing of class methods and package functions, we need to override some of these methods + # without changing their behavior. __get_items_from_run is name mangled in the parent class, + # so inherited methods cannot access the version here in MayhemParser. def get_findings(self, filehandle, test): """For simple interface of parser contract we just aggregate everything""" tree = json.load(filehandle) @@ -123,15 +72,16 @@ def __get_items_from_run(self, run): items = [] # load rules rules = get_rules(run) - artifacts = get_artifacts(run) + # Artifacts do not appear to be used anywhere + # artifacts = get_artifacts(run) # get the timestamp of the run if possible run_date = self.__get_last_invocation_date(run) for result in run.get("results", []): - result_items = get_items_from_result(result, rules, artifacts, run_date) + result_items = get_items_from_result(result, rules, run_date) if result_items: items.extend(result_items) return items - + def __get_last_invocation_date(self, data): invocations = data.get("invocations", []) if len(invocations) == 0: @@ -144,52 +94,6 @@ def __get_last_invocation_date(self, data): return dateutil.parser.isoparse(raw_date) -def get_rules(run): - rules = {} - rules_array = run["tool"]["driver"].get("rules", []) - if len(rules_array) == 0 and run["tool"].get("extensions") is not None: - rules_array = run["tool"]["extensions"][0].get("rules", []) - for item in rules_array: - rules[item["id"]] = item - return rules - - -# Rules and results have de sames scheme for tags -def get_properties_tags(value): - if not value: - return [] - return value.get("properties", {}).get("tags", []) - - -def search_cwe(value, cwes): - matches = re.search(CWE_REGEX, value, re.IGNORECASE) - if matches: - cwes.append(int(matches[0].split("-")[1])) - - -def get_rule_cwes(rule): - cwes = [] - # data of the specification - if "relationships" in rule and isinstance(rule["relationships"], list): - for relationship in rule["relationships"]: - value = relationship["target"]["id"] - search_cwe(value, cwes) - return cwes - - for tag in get_properties_tags(rule): - search_cwe(tag, cwes) - return cwes - - -def get_result_cwes_properties(result): - """Some tools like njsscan store the CWE in the properties of the result""" - cwes = [] - if "properties" in result and "cwe" in result["properties"]: - value = result["properties"]["cwe"] - search_cwe(value, cwes) - return cwes - - def get_result_cwes_mcode(result): """Mayhem SARIF reports include CWE property under taxa.toolComponent.name and number under taxa.id""" cwes = [] @@ -202,13 +106,6 @@ def get_result_cwes_mcode(result): return cwes -def get_artifacts(run): - artifacts = {} - for custom_index, tree_artifact in enumerate(run.get("artifacts", [])): - artifacts[tree_artifact.get("index", custom_index)] = tree_artifact - return artifacts - - def clean_mayhem_title_text(text): """Clean the title text for Mayhem SARIF reports.""" if not text: @@ -237,7 +134,7 @@ def get_message_from_multiformatMessageString(data, rule, content_type="text"): """ Get a message from multimessage struct - See here for the specification: https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317468 + Differs from Sarif implementation in that it handles markdown, specifies content_type """ if content_type == "markdown" and "markdown" in data: # handle markdown content @@ -264,118 +161,8 @@ def get_message_from_multiformatMessageString(data, rule, content_type="text"): return "" -def cve_try(val): - # Match only the first CVE! - cveSearch = re.search(r"(CVE-[0-9]+-[0-9]+)", val, re.IGNORECASE) - if cveSearch: - return cveSearch.group(1).upper() - return None - - -def get_title(result, rule): - title = None - if "message" in result: - title = get_message_from_multiformatMessageString( - result["message"], rule, - ) - if title is None and rule is not None: - if "shortDescription" in rule: - title = get_message_from_multiformatMessageString( - rule["shortDescription"], rule, - ) - elif "fullDescription" in rule: - title = get_message_from_multiformatMessageString( - rule["fullDescription"], rule, - ) - elif "name" in rule: - title = rule["name"] - elif "id" in rule: - title = rule["id"] - - if title is None: - msg = "No information found to create a title" - raise ValueError(msg) - - # Clean the title text for Mayhem SARIF reports - title = clean_mayhem_title_text(title) - - return textwrap.shorten(title, 150) - - -def get_snippet(location): - - snippet = None - - if location and "physicalLocation" in location: - if "region" in location["physicalLocation"]: - if "snippet" in location["physicalLocation"]["region"]: - if ( - "text" - in location["physicalLocation"]["region"]["snippet"] - ): - snippet = location["physicalLocation"]["region"][ - "snippet" - ]["text"] - if ( - snippet is None - and "contextRegion" in location["physicalLocation"] - ): - if "snippet" in location["physicalLocation"]["contextRegion"]: - if ( - "text" - in location["physicalLocation"]["contextRegion"][ - "snippet" - ] - ): - snippet = location["physicalLocation"][ - "contextRegion" - ]["snippet"]["text"] - - return snippet - - -def get_codeFlowsDescription(code_flows): - description = "" - for codeFlow in code_flows: - for threadFlow in codeFlow.get("threadFlows", []): - if "locations" not in threadFlow: - continue - - description = f"**{_('Code flow')}:**\n" - - for line, location in enumerate(threadFlow.get("locations", [])): - physicalLocation = location.get("location", {}).get("physicalLocation", {}) - region = physicalLocation.get("region", {}) - uri = physicalLocation.get("artifactLocation").get("uri") - - start_line = "" - start_column = "" - snippet = "" - - if "startLine" in region: - start_line = f":L{region.get('startLine')}" - - if "startColumn" in region: - start_column = f":C{region.get('startColumn')}" - - if "snippet" in region: - snippet = f"\t-\t{region.get('snippet').get('text')}" - - description += f"{line + 1}. {uri}{start_line}{start_column}{snippet}\n" - - if "message" in location.get("location", {}): - message_field = location.get("location", {}).get("message", {}) - if "markdown" in message_field: - message = message_field.get("markdown", "") - else: - message = message_field.get("text", "") - - description += f"\t{message}\n" - - return description - - def get_description(result, rule, location): + """Overwrite the SarifParser get_description to handle markdown""" description = "" message = "" if "message" in result: @@ -415,58 +202,7 @@ def get_description(result, rule, location): return description.removesuffix("\n") -def get_references(rule): - reference = None - if rule is not None: - if "helpUri" in rule: - reference = rule["helpUri"] - elif "help" in rule: - helpText = get_message_from_multiformatMessageString( - rule["help"], rule, - ) - if helpText.startswith("http"): - reference = helpText - - return reference - - -def cvss_to_severity(cvss): - severity_mapping = { - 1: "Info", - 2: "Low", - 3: "Medium", - 4: "High", - 5: "Critical", - } - - if cvss >= 9: - return severity_mapping.get(5) - if cvss >= 7: - return severity_mapping.get(4) - if cvss >= 4: - return severity_mapping.get(3) - if cvss > 0: - return severity_mapping.get(2) - return severity_mapping.get(1) - - -def get_severity(result, rule): - severity = result.get("level") - if severity is None and rule is not None: - # get the severity from the rule - if "defaultConfiguration" in rule: - severity = rule["defaultConfiguration"].get("level") - - if severity == "note": - return "Info" - if severity == "warning": - return "Medium" - if severity == "error": - return "High" - return "Medium" - - -def get_items_from_result(result, rules, artifacts, run_date): +def get_items_from_result(result, rules, run_date): # see # https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html # / 3.27.9 @@ -516,11 +252,11 @@ def get_items_from_result(result, rules, artifacts, run_date): rule = rules.get(result.get("ruleId")) finding = Finding( - title=get_title(result, rule), + title=clean_mayhem_title_text(get_title(result, rule)), severity=get_severity(result, rule), description=get_description(result, rule, location), - static_finding=False, # by definition - dynamic_finding=True, # by definition + static_finding=False, + dynamic_finding=True, false_p=suppressed, active=not suppressed, file_path=file_path, @@ -594,31 +330,3 @@ def get_items_from_result(result, rules, artifacts, run_date): result_items.append(finding) return result_items - - -def get_fingerprints_hashes(values): - """ - Method that generate a `unique_id_from_tool` data from the `fingerprints` attribute. - - for now, we take the value of the last version of the first hash method. - """ - fingerprints = {} - for key in values: - if "/" in key: - key_method = key.split("/")[-2] - key_method_version = int(key.split("/")[-1].replace("v", "")) - else: - key_method = key - key_method_version = 0 - value = values[key] - if fingerprints.get(key_method): - if fingerprints[key_method]["version"] < key_method_version: - fingerprints[key_method] = { - "version": key_method_version, - "value": value, - } - else: - fingerprints[key_method] = { - "version": key_method_version, - "value": value, - } - return fingerprints From 41101e8711d1e842569e8e6d57e4fea1f51c0261 Mon Sep 17 00:00:00 2001 From: xansec Date: Wed, 18 Jun 2025 18:42:17 -0400 Subject: [PATCH 5/9] make linter happy --- dojo/tools/mayhem/parser.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/dojo/tools/mayhem/parser.py b/dojo/tools/mayhem/parser.py index da2ae0cff90..cdcdc4a713f 100644 --- a/dojo/tools/mayhem/parser.py +++ b/dojo/tools/mayhem/parser.py @@ -7,23 +7,13 @@ from dojo.models import Finding from dojo.tools.parser_test import ParserTest - -from dojo.tools.sarif.parser import SarifParser -from dojo.tools.sarif.parser import ( - get_codeFlowsDescription, - get_snippet, - get_title, - get_severity, - get_references, - cve_try, - get_rule_cwes, - get_result_cwes_properties, - cvss_to_severity, - get_properties_tags, - get_fingerprints_hashes, - get_rules - ) - +from dojo.tools.sarif.parser import (SarifParser, cve_try, cvss_to_severity, + get_codeFlowsDescription, + get_fingerprints_hashes, + get_properties_tags, get_references, + get_result_cwes_properties, get_rule_cwes, + get_rules, get_severity, get_snippet, + get_title) logger = logging.getLogger(__name__) @@ -31,8 +21,10 @@ class MayhemParser(SarifParser): + """ Mayhem SARIF Parser + This class extends the existing SARIF parser, but with some minor modifications to better support the structure of Mayhem SARIF reports. """ @@ -81,7 +73,7 @@ def __get_items_from_run(self, run): if result_items: items.extend(result_items) return items - + def __get_last_invocation_date(self, data): invocations = data.get("invocations", []) if len(invocations) == 0: From 49ca180a73b22d7f3422581ed7dc8ad87a58e477 Mon Sep 17 00:00:00 2001 From: xansec Date: Thu, 19 Jun 2025 12:46:36 -0400 Subject: [PATCH 6/9] linter again --- dojo/tools/mayhem/parser.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/dojo/tools/mayhem/parser.py b/dojo/tools/mayhem/parser.py index cdcdc4a713f..2ee0cf53599 100644 --- a/dojo/tools/mayhem/parser.py +++ b/dojo/tools/mayhem/parser.py @@ -7,13 +7,21 @@ from dojo.models import Finding from dojo.tools.parser_test import ParserTest -from dojo.tools.sarif.parser import (SarifParser, cve_try, cvss_to_severity, - get_codeFlowsDescription, - get_fingerprints_hashes, - get_properties_tags, get_references, - get_result_cwes_properties, get_rule_cwes, - get_rules, get_severity, get_snippet, - get_title) +from dojo.tools.sarif.parser import ( + SarifParser, + cve_try, + cvss_to_severity, + get_codeFlowsDescription, + get_fingerprints_hashes, + get_properties_tags, + get_references, + get_result_cwes_properties, + get_rule_cwes, + get_rules, + get_severity, + get_snippet, + get_title, +) logger = logging.getLogger(__name__) From 178449677cc4d749626c21f565508e229b31c315 Mon Sep 17 00:00:00 2001 From: xansec Date: Fri, 20 Jun 2025 12:18:49 -0400 Subject: [PATCH 7/9] make sure func signatures match --- dojo/tools/mayhem/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dojo/tools/mayhem/parser.py b/dojo/tools/mayhem/parser.py index 2ee0cf53599..2b5f5e9da3c 100644 --- a/dojo/tools/mayhem/parser.py +++ b/dojo/tools/mayhem/parser.py @@ -40,7 +40,7 @@ class MayhemParser(SarifParser): def get_scan_types(self): return ["Mayhem SARIF Report"] - def get_description_for_scan_types(self): + def get_description_for_scan_types(self, scan_type): return "Mayhem SARIF reports from code or API runs." # Due to mixing of class methods and package functions, we need to override some of these methods From 262782f2a761507bb193140367719999d862e7c4 Mon Sep 17 00:00:00 2001 From: xansec Date: Fri, 11 Jul 2025 15:46:35 -0400 Subject: [PATCH 8/9] tests --- unittests/tools/test_mayhem_parser.py | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/unittests/tools/test_mayhem_parser.py b/unittests/tools/test_mayhem_parser.py index da381004a75..f1873f8f4f2 100644 --- a/unittests/tools/test_mayhem_parser.py +++ b/unittests/tools/test_mayhem_parser.py @@ -9,8 +9,10 @@ def common_checks(self, finding): self.assertIn(finding.severity, Finding.SEVERITIES) if finding.cwe: self.assertIsInstance(finding.cwe, int) - self.assertEqual(False, finding.static_finding) # Mayhem is DAST! - self.assertEqual(True, finding.dynamic_finding) # Mayhem is DAST! + self.assertFalse(finding.static_finding) # Mayhem is DAST! + self.assertTrue(True, finding.dynamic_finding) # Mayhem is DAST! + self.assertIsInstance(finding.description, str) + self.assertEqual(1, finding.reporter_id) def test_mcode_many_report(self): with ( @@ -21,6 +23,15 @@ def test_mcode_many_report(self): self.assertEqual(8, len(findings)) for finding in findings: self.common_checks(finding) + # Sample a finding + finding = findings[3] + self.assertEqual("Uncontrolled Resource Consumption", finding.title) + self.assertEqual(400, finding.cwe) + self.assertEqual("High", finding.severity) + self.assertEqual("https://www.mayhem.security/", finding.references) + self.assertEqual(48, finding.line) + self.assertEqual("app/src/gps_uploader.c", finding.file_path) + self.assertEqual("MI102", finding.vuln_id_from_tool) def test_mapi_many_report(self): with ( @@ -31,6 +42,14 @@ def test_mapi_many_report(self): self.assertEqual(20, len(findings)) for finding in findings: self.common_checks(finding) + # Sample a finding + finding = findings[7] + self.assertEqual("Internal Server Error in POST /pet.", finding.title) + self.assertEqual(550, finding.cwe) + self.assertEqual("High", finding.severity) + self.assertEqual(497, finding.line) + self.assertEqual("io/swagger/oas/inflector/controllers/OpenAPIOperationController.java", finding.file_path) + self.assertEqual("internal-server-error (io.swagger.oas.inflector.utils.ApiException)", finding.vuln_id_from_tool) def test_mcode_one_report(self): with ( @@ -41,7 +60,11 @@ def test_mcode_one_report(self): self.assertEqual(1, len(findings)) finding = findings[0] self.common_checks(finding) + self.assertEqual("Improper Input Validation", finding.title) self.assertEqual(20, finding.cwe) + self.assertEqual("High", finding.severity) + self.assertEqual("https://www.mayhem.security/", finding.references) + self.assertEqual("MI101", finding.vuln_id_from_tool) def test_mapi_one_report(self): with ( @@ -52,7 +75,10 @@ def test_mapi_one_report(self): self.assertEqual(1, len(findings)) finding = findings[0] self.common_checks(finding) + self.assertEqual("Default Credentials Used in GET /info.", finding.title) self.assertEqual(1392, finding.cwe) + self.assertEqual("High", finding.severity) + self.assertEqual("default-credentials", finding.vuln_id_from_tool) def test_mcode_no_vulns_report(self): with ( From d8396c86bf62063a791b1d2f90f5e5360fcdb1d3 Mon Sep 17 00:00:00 2001 From: xansec Date: Fri, 11 Jul 2025 15:50:51 -0400 Subject: [PATCH 9/9] linter --- unittests/tools/test_mayhem_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/tools/test_mayhem_parser.py b/unittests/tools/test_mayhem_parser.py index f1873f8f4f2..400b32b6510 100644 --- a/unittests/tools/test_mayhem_parser.py +++ b/unittests/tools/test_mayhem_parser.py @@ -10,8 +10,8 @@ def common_checks(self, finding): if finding.cwe: self.assertIsInstance(finding.cwe, int) self.assertFalse(finding.static_finding) # Mayhem is DAST! - self.assertTrue(True, finding.dynamic_finding) # Mayhem is DAST! - self.assertIsInstance(finding.description, str) + self.assertTrue(finding.dynamic_finding) # Mayhem is DAST! + self.assertIsInstance(finding.description, str) self.assertEqual(1, finding.reporter_id) def test_mcode_many_report(self):