diff --git a/README.md b/README.md index 4d5b67f..b2a894c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ machine-readable answer key per target, and a control panel to drive it all. > enforces it. Read [SECURITY.md](SECURITY.md) before first run, including the > part about what container isolation does not cover. +[![The limeyard control panel, listing every target with its kind, state, address and upstream](https://raw.githubusercontent.com/clickswave/limeyard/main/docs/panel.png)](#control-panel) + +The control panel on http://127.0.0.1:7000, showing the lab as it runs. + limeyard was `vuln_apps`. It was renamed because it stopped being a folder of applications: it now holds bare services, a DNS zone, a WAF pair, a precision target and APK fixtures, none of which are apps. diff --git a/control/limed/api.py b/control/limed/api.py index 00960ed..f4fcc9f 100644 --- a/control/limed/api.py +++ b/control/limed/api.py @@ -906,7 +906,7 @@ def _post(self, p): if tr: truth[slug] = tr card = scorer.score(findings, truth, tool=body.get("tool", "unknown"), - only=body.get("targets")) + only=body.get("targets"), cost=body.get("cost")) if body.get("save"): path = scorer.save(card, lime.TRUTH_DIR) card["saved"] = path diff --git a/control/limed/lime.py b/control/limed/lime.py index f3520a0..2b64381 100755 --- a/control/limed/lime.py +++ b/control/limed/lime.py @@ -206,6 +206,12 @@ def containers(t): def state_of(t): + # A fixture has no containers by design: an APK is installed on a device, + # not started here. Calling that "stopped" makes a healthy lab look like + # something is down, and made the CLI contradict both the panel and + # doctor, which have always called it what it is. + if is_fixture(t): + return "fixture", "d" cs = containers(t) if not cs: return "stopped", "d" diff --git a/control/limed/scorer.py b/control/limed/scorer.py index b44bcbd..833fad4 100644 --- a/control/limed/scorer.py +++ b/control/limed/scorer.py @@ -185,8 +185,18 @@ def entry_matches(entry, finding): return flags -def score(findings, truth, tool="unknown", only=None, scopes=("black-box", "authed")): - """findings: [{target, class, path, method?, param?, in?, severity?}]""" +def score(findings, truth, tool="unknown", only=None, scopes=("black-box", "authed"), + cost=None): + """findings: [{target, class, path, method?, param?, in?, severity?}] + + `cost` is what the run spent, per target: + {target: {seconds, requests, truncated}}. A target marked `truncated` had + its pass cut by a deadline, so its misses are not evidence about the tool. + It is scored because scan time is a product quality attribute and nothing + was measuring it: a pass against a 45-endpoint application quietly grew to + 69 minutes, and the scorecard that recorded 43 of 48 said nothing about it. + A scanner nobody can afford to run is not accurate, it is theoretical. + """ per_target, totals = {}, {"expected": 0, "detected": 0, "missed": 0, "false_positive": 0, "unmatched": 0, "out_of_scope": 0} by_class = {} @@ -279,6 +289,41 @@ def score(findings, truth, tool="unknown", only=None, scopes=("black-box", "auth for c, b in by_class.items(): b["recall"] = round(b["detected"] / b["expected"], 4) if b["expected"] else None + # Cost, alongside correctness. Reported per target and in total, with the + # slowest named: an average hides the one target that took an hour. + cost = cost or {} + if cost: + secs = {t: float(v.get("seconds") or 0) for t, v in cost.items()} + reqs = {t: int(v.get("requests") or 0) for t, v in cost.items()} + slowest = max(secs, key=secs.get) if secs else None + totals["seconds"] = round(sum(secs.values()), 1) + totals["requests"] = sum(reqs.values()) + totals["slowest_target"] = slowest + totals["slowest_seconds"] = round(secs.get(slowest, 0), 1) if slowest else None + for t, v in cost.items(): + if t in per_target: + per_target[t]["seconds"] = round(float(v.get("seconds") or 0), 1) + per_target[t]["requests"] = int(v.get("requests") or 0) + # Sites the engine could not get a baseline for. They were not + # tested, and a recall figure that counts them as clean misses is + # measuring the load the pass applied, not the tool. + if v.get("skipped_sites"): + per_target[t]["skipped_sites"] = int(v["skipped_sites"]) + # Where the time went, per class. The ratio is what tells a + # slow scan from an expensive one. + if v.get("by_class"): + per_target[t]["by_class"] = str(v["by_class"]) + if v.get("truncated"): + per_target[t]["truncated"] = True + # A run the clock cut short did not miss what it never reached, and a + # recall figure that does not say so is worse than no figure: it turns + # "we ran out of time" into "the scanner cannot find this". Named at + # the top of the card so it cannot be read past. + cut = sorted(t for t, v in cost.items() if v.get("truncated")) + if cut: + totals["truncated_targets"] = cut + totals["recall_is_a_lower_bound"] = True + return { "tool": tool, "generated": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), diff --git a/docker-compose.yml b/docker-compose.yml index d3bd66e..4c1c839 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,6 +50,12 @@ services: # ...and see the definitions at their real host path, so relative paths # inside a target's compose resolve correctly through the socket. - ${LIMEYARD_DIR:-.}:${LIMEYARD_DIR:-/work} + # ...and run the control plane from the working tree rather than the copy + # baked into the image. Without this an edit to lime.py, api.py or + # scorer.py does nothing at all until someone remembers to rebuild, and + # the symptom is the worst kind: the code you just changed runs exactly as + # it did before, silently. Cost an hour of confusion on 2026-09-11. + - ./control/limed:/opt/limed:ro working_dir: ${LIMEYARD_DIR:-/work} ports: - "127.0.0.1:7099:7099" diff --git a/docs/panel.png b/docs/panel.png new file mode 100644 index 0000000..89dc0d9 Binary files /dev/null and b/docs/panel.png differ diff --git a/truth/scorecards/2026-09-10-crossfyre-oast-batched.json b/truth/scorecards/2026-09-10-crossfyre-oast-batched.json new file mode 100644 index 0000000..53d77b0 --- /dev/null +++ b/truth/scorecards/2026-09-10-crossfyre-oast-batched.json @@ -0,0 +1,1471 @@ +{ + "tool": "crossfyre-oast-batched", + "generated": "2026-09-10T17:32:25Z", + "totals": { + "expected": 48, + "detected": 43, + "missed": 5, + "false_positive": 0, + "unmatched": 155, + "out_of_scope": 24, + "precision": 1.0, + "recall": 0.8958, + "f1": 0.945 + }, + "by_class": { + "bfla": { + "expected": 1, + "detected": 1, + "recall": 1.0 + }, + "bola": { + "expected": 4, + "detected": 4, + "recall": 1.0 + }, + "cmdi": { + "expected": 4, + "detected": 3, + "recall": 0.75 + }, + "cors": { + "expected": 1, + "detected": 1, + "recall": 1.0 + }, + "cve": { + "expected": 1, + "detected": 1, + "recall": 1.0 + }, + "default-creds": { + "expected": 2, + "detected": 2, + "recall": 1.0 + }, + "deserialization": { + "expected": 1, + "detected": 0, + "recall": 0.0 + }, + "dos": { + "expected": 2, + "detected": 2, + "recall": 1.0 + }, + "excessive-exposure": { + "expected": 5, + "detected": 5, + "recall": 1.0 + }, + "exposure": { + "expected": 5, + "detected": 5, + "recall": 1.0 + }, + "info-disclosure": { + "expected": 1, + "detected": 1, + "recall": 1.0 + }, + "lfi": { + "expected": 3, + "detected": 2, + "recall": 0.6667 + }, + "misconfig": { + "expected": 6, + "detected": 6, + "recall": 1.0 + }, + "sqli": { + "expected": 7, + "detected": 6, + "recall": 0.8571 + }, + "tech": { + "expected": 2, + "detected": 2, + "recall": 1.0 + }, + "xss-reflected": { + "expected": 3, + "detected": 2, + "recall": 0.6667 + } + }, + "by_target": { + "androgoat": { + "expected_in_scope": 0, + "detected": [], + "missed": [], + "false_positives": [], + "unmatched": [], + "out_of_scope": [ + "AG1", + "AG2", + "AG3", + "AG4" + ], + "location_flags": [], + "precision": null, + "recall": null, + "f1": null + }, + "bwapp": { + "expected_in_scope": 5, + "detected": [ + "BW1", + "BW2", + "BW3", + "BW4", + "BW5" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "exposure", + "path": "/web.config", + "param": null, + "severity": "high" + }, + { + "class": "no-rate-limit", + "path": "/login.php", + "param": null, + "severity": "medium" + }, + { + "class": "xss-reflected", + "path": "/htmli_get.php", + "param": null, + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/xss_get.php", + "param": null, + "severity": "high" + } + ], + "out_of_scope": [ + "BW6" + ], + "location_flags": [ + { + "id": "BW1", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'title')" + ] + }, + { + "id": "BW2", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'firstname')" + ] + }, + { + "id": "BW3", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'target')" + ] + }, + { + "id": "BW4", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'language')" + ] + } + ], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "crapi": { + "expected_in_scope": 3, + "detected": [ + "CR1", + "CR2", + "CR3" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "bola", + "path": "/identity/api/v2/vehicle/cd515c12-0fc1-48ae-8b61-9230b70a845b/location", + "param": null, + "severity": "critical" + }, + { + "class": "bola", + "path": "/identity/api/v2/vehicle/f89b5f21-7829-45cb-a650-299a61090378/location", + "param": null, + "severity": "critical" + }, + { + "class": "bola", + "path": "/workshop/api/mechanic/mechanic_report", + "param": "report_id", + "severity": "critical" + }, + { + "class": "bola", + "path": "/workshop/api/mechanic/mechanic_report", + "param": "report_id", + "severity": "critical" + }, + { + "class": "bola", + "path": "/workshop/api/mechanic/mechanic_report", + "param": "report_id", + "severity": "critical" + }, + { + "class": "bola", + "path": "/workshop/api/mechanic/mechanic_report", + "param": "report_id", + "severity": "critical" + }, + { + "class": "bola", + "path": "/workshop/api/mechanic/mechanic_report", + "param": "report_id", + "severity": "critical" + } + ], + "out_of_scope": [ + "CR4", + "CR5" + ], + "location_flags": [ + { + "id": "CR1", + "flags": [ + "location mismatch: expected path, reported endpoint" + ] + } + ], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "crawlground": { + "expected_in_scope": 0, + "detected": [], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "no-rate-limit", + "path": "/reset", + "param": null, + "severity": "medium" + }, + { + "class": "open-redirect", + "path": "/api/redirect", + "param": "url", + "severity": "medium" + }, + { + "class": "xss-reflected", + "path": "/api/page", + "param": "url", + "severity": "high" + } + ], + "out_of_scope": [], + "location_flags": [], + "precision": null, + "recall": null, + "f1": null + }, + "crawlmaze": { + "expected_in_scope": 0, + "detected": [], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + } + ], + "out_of_scope": [], + "location_flags": [], + "precision": null, + "recall": null, + "f1": null + }, + "dvga": { + "expected_in_scope": 7, + "detected": [ + "DG1", + "DG2", + "DG3", + "DG4", + "DG5", + "DG6", + "DG7" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "exposure", + "path": "/graphql", + "param": "query", + "severity": "medium" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": null + } + ], + "out_of_scope": [], + "location_flags": [], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "dvwa": { + "expected_in_scope": 4, + "detected": [ + "DV1", + "DV2", + "DV3", + "DV4" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "no-rate-limit", + "path": "/login.php", + "param": null, + "severity": "medium" + }, + { + "class": "sqli", + "path": "/vulnerabilities/sqli_blind", + "param": null, + "severity": "high" + }, + { + "class": "sqli", + "path": "/vulnerabilities/fi", + "param": "page", + "severity": "high" + }, + { + "class": "sqli", + "path": "/vulnerabilities/xss_r", + "param": "name", + "severity": "high" + }, + { + "class": "ssrf", + "path": "/vulnerabilities/fi", + "param": "page", + "severity": "high" + }, + { + "class": "open-redirect", + "path": "/vulnerabilities/open_redirect/source/low.php", + "param": "redirect", + "severity": "medium" + }, + { + "class": "xss-reflected", + "path": "/vulnerabilities/fi", + "param": "page", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/vulnerabilities/fi", + "param": "page", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/vulnerabilities/fi", + "param": "page", + "severity": "high" + } + ], + "out_of_scope": [ + "DV5", + "DV6", + "DV7" + ], + "location_flags": [ + { + "id": "DV1", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'id')" + ] + }, + { + "id": "DV4", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'ip')" + ] + } + ], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "faultline": { + "expected_in_scope": 3, + "detected": [ + "FL1", + "FL2", + "FL3" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "cors", + "path": "/api/v1/users/1", + "param": null, + "severity": "high" + }, + { + "class": "cors", + "path": "/api/v2/user/dashboard", + "param": null, + "severity": "high" + }, + { + "class": "cors", + "path": "/graphql", + "param": null, + "severity": "high" + }, + { + "class": "exposure", + "path": "/.well-known/security.txt", + "param": null, + "severity": "info" + }, + { + "class": "excessive-exposure", + "path": "/api/v1/admin/users", + "param": null, + "severity": "medium" + }, + { + "class": "excessive-exposure", + "path": "/api/v1/users/1", + "param": null, + "severity": "high" + }, + { + "class": "bola", + "path": "/api/v1/users/2", + "param": null, + "severity": "critical" + }, + { + "class": "excessive-exposure", + "path": "/api/v1/users/2", + "param": null, + "severity": "high" + }, + { + "class": "bola", + "path": "/api/v1/users/3", + "param": null, + "severity": "critical" + }, + { + "class": "excessive-exposure", + "path": "/api/v1/users/3", + "param": null, + "severity": "high" + }, + { + "class": "bola", + "path": "/api/v1/users/4", + "param": null, + "severity": "critical" + }, + { + "class": "excessive-exposure", + "path": "/api/v1/users/4", + "param": null, + "severity": "high" + }, + { + "class": "bola", + "path": "/api/v1/users/5", + "param": null, + "severity": "critical" + }, + { + "class": "excessive-exposure", + "path": "/api/v1/users/5", + "param": null, + "severity": "high" + } + ], + "out_of_scope": [ + "FL4", + "FL5" + ], + "location_flags": [ + { + "id": "FL2", + "flags": [ + "location mismatch: expected path, reported endpoint" + ] + } + ], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "juice-shop": { + "expected_in_scope": 3, + "detected": [ + "JS1", + "JS2", + "JS3" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "exposure", + "path": "/security.txt", + "param": null, + "severity": "info" + }, + { + "class": "exposure", + "path": "/.well-known/security.txt", + "param": null, + "severity": "info" + }, + { + "class": "bola", + "path": "/rest/basket/2", + "param": null, + "severity": "critical" + }, + { + "class": "bola", + "path": "/rest/basket/6", + "param": null, + "severity": "critical" + }, + { + "class": "bola", + "path": "/rest/basket/7", + "param": null, + "severity": "critical" + } + ], + "out_of_scope": [ + "JS4", + "JS5" + ], + "location_flags": [ + { + "id": "JS2", + "flags": [ + "location mismatch: expected path, reported endpoint" + ] + } + ], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "log4shell": { + "expected_in_scope": 2, + "detected": [ + "LS1", + "LS2" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + } + ], + "out_of_scope": [], + "location_flags": [ + { + "id": "LS1", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'X-Api-Version')" + ] + }, + { + "id": "LS2", + "flags": [ + "matched on class alone; the finding carried no location" + ] + } + ], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "mirage": { + "expected_in_scope": 0, + "detected": [], + "missed": [], + "false_positives": [], + "unmatched": [], + "out_of_scope": [], + "location_flags": [], + "precision": null, + "recall": null, + "f1": null + }, + "modsec": { + "expected_in_scope": 1, + "detected": [ + "WAF2" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + } + ], + "out_of_scope": [ + "WAF1" + ], + "location_flags": [ + { + "id": "WAF2", + "flags": [ + "matched on class alone; the finding carried no location" + ] + } + ], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "mutillidae": { + "expected_in_scope": 6, + "detected": [ + "MU5", + "MU6" + ], + "missed": [ + "MU1", + "MU2", + "MU3", + "MU4" + ], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "exposure", + "path": "/phpinfo.php", + "param": null, + "severity": "low" + }, + { + "class": "no-rate-limit", + "path": "/webservices/rest/ws-login.php", + "param": null, + "severity": "medium" + }, + { + "class": "exposure", + "path": "/webservices/soap/ws-dns-lookup.php", + "param": null, + "severity": "low" + }, + { + "class": "no-rate-limit", + "path": "/documentation/mutillidae-test-scripts.txt", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/webservices/soap/ws-login.php", + "param": null, + "severity": "medium" + }, + { + "class": "exposure", + "path": "/webservices/soap/ws-login.php", + "param": null, + "severity": "low" + }, + { + "class": "exposure", + "path": "/webservices/soap/ws-test-connectivity.php", + "param": null, + "severity": "low" + }, + { + "class": "sqli", + "path": "/hints-page-wrapper.php", + "param": "level1HintIncludeFile", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/hints-page-wrapper.php", + "param": "level1HintIncludeFile", + "severity": "high" + }, + { + "class": "sqli", + "path": "/hints-page-wrapper.php", + "param": "level1HintIncludeFile", + "severity": "high" + }, + { + "class": "sqli", + "path": "/hints-page-wrapper.php", + "param": "level1HintIncludeFile", + "severity": "high" + }, + { + "class": "sqli", + "path": "/hints-page-wrapper.php", + "param": "level1HintIncludeFile", + "severity": "high" + }, + { + "class": "cve", + "path": "/", + "param": null, + "severity": "critical" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "info-disclosure", + "path": "/phpinfo.php", + "param": null, + "severity": "low" + } + ], + "out_of_scope": [ + "MU7", + "MU8" + ], + "location_flags": [ + { + "id": "MU5", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'username')", + "location mismatch: expected body, reported header" + ] + } + ], + "precision": 1.0, + "recall": 0.3333, + "f1": 0.5 + }, + "openservices": { + "expected_in_scope": 7, + "detected": [ + "SV1", + "SV2", + "SV3", + "SV4", + "SV5", + "SV6", + "SV8" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "high" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + } + ], + "out_of_scope": [ + "SV7" + ], + "location_flags": [], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "railsgoat": { + "expected_in_scope": 3, + "detected": [ + "RG2", + "RG3" + ], + "missed": [ + "RG1" + ], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "no-rate-limit", + "path": "/forgot_password", + "param": null, + "severity": "medium" + }, + { + "class": "xss-reflected", + "path": "/dashboard/home", + "param": "font", + "severity": "high" + } + ], + "out_of_scope": [ + "RG4", + "RG5" + ], + "location_flags": [ + { + "id": "RG2", + "flags": [ + "location mismatch: expected path, reported endpoint" + ] + }, + { + "id": "RG3", + "flags": [ + "matched on class and path; the finding named no parameter (the answer key names 'user[id]')" + ] + } + ], + "precision": 1.0, + "recall": 0.6667, + "f1": 0.8 + }, + "vampi": { + "expected_in_scope": 4, + "detected": [ + "VP1", + "VP2", + "VP3", + "VP4" + ], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "tech", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "no-rate-limit", + "path": "/users/v1/1/password", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/users/v1/login", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/users/v1/register", + "param": null, + "severity": "medium" + }, + { + "class": "excessive-exposure", + "path": "/users/v1/_debug", + "param": null, + "severity": "high" + } + ], + "out_of_scope": [], + "location_flags": [], + "precision": 1.0, + "recall": 1.0, + "f1": 1.0 + }, + "vulnerableapp": { + "expected_in_scope": 0, + "detected": [], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_10", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_5", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_1", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_6", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_4", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_2", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_7", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_3", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_8", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/VulnerableApp/AuthenticationVulnerability/LEVEL_9", + "param": null, + "severity": "medium" + } + ], + "out_of_scope": [], + "location_flags": [], + "precision": null, + "recall": null, + "f1": null + }, + "webgoat": { + "expected_in_scope": 0, + "detected": [], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "no-rate-limit", + "path": "/WebGoat/login", + "param": null, + "severity": "medium" + }, + { + "class": "no-rate-limit", + "path": "/WebGoat/register.mvc", + "param": null, + "severity": "medium" + } + ], + "out_of_scope": [ + "WG1", + "WG2", + "WG3", + "WG4" + ], + "location_flags": [], + "precision": null, + "recall": null, + "f1": null + }, + "xssmaze": { + "expected_in_scope": 0, + "detected": [], + "missed": [], + "false_positives": [], + "unmatched": [ + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "low" + }, + { + "class": "misconfig", + "path": "/", + "param": null, + "severity": "info" + }, + { + "class": "xss-reflected", + "path": "/svg/level1", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/pdiff/level5", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/dataurl/level1", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/ctype/level4", + "param": "callback", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/social-media/level1", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/ctype/level1", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/multireflect/level6", + "param": "msg", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/cmspattern/level3", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/css/level1", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/whitespace/level5", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/clipboard/level2", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/csti/level2", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/misc-context/level5", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/markdown/level3", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/scriptgadget/level6", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/domctx/level6", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/multiline/level1", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/svgctx/level5", + "param": "query", + "severity": "high" + }, + { + "class": "xss-reflected", + "path": "/rwpattern/level4", + "param": "query", + "severity": "high" + } + ], + "out_of_scope": [], + "location_flags": [], + "precision": null, + "recall": null, + "f1": null + } + } +}