diff --git a/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2022.json b/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2022.json index c4eb3d6fd..0351893e9 100644 --- a/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2022.json +++ b/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2022.json @@ -3,13 +3,13 @@ "section_id": "K01", "section": "Insecure Workload Configurations", "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K01-insecure-workload-configurations", - "cre_ids": ["233-748", "486-813"] + "cre_ids": ["715-334", "053-751"] }, { "section_id": "K02", "section": "Supply Chain Vulnerabilities", "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K02-supply-chain-vulnerabilities", - "cre_ids": ["613-285", "613-287"] + "cre_ids": ["715-223", "307-507"] }, { "section_id": "K03", @@ -21,19 +21,19 @@ "section_id": "K04", "section": "Lack of Centralized Policy Enforcement", "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K04-lack-of-centralized-policy-enforcement", - "cre_ids": ["117-371"] + "cre_ids": ["117-371", "344-611"] }, { "section_id": "K05", "section": "Inadequate Logging and Monitoring", "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K05-inadequate-logging-and-monitoring", - "cre_ids": ["058-083", "148-420", "402-706", "843-841"] + "cre_ids": ["058-083", "148-420", "402-706"] }, { "section_id": "K06", "section": "Broken Authentication Mechanisms", "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K06-broken-authentication-mechanisms", - "cre_ids": ["177-260", "586-842", "633-428"] + "cre_ids": ["113-133", "576-042"] }, { "section_id": "K07", @@ -51,12 +51,12 @@ "section_id": "K09", "section": "Misconfigured Cluster Components", "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K09-misconfigured-cluster-components", - "cre_ids": ["233-748", "486-813"] + "cre_ids": ["053-751", "233-748", "715-334"] }, { "section_id": "K10", "section": "Outdated and Vulnerable Kubernetes Components", "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K10-outdated-and-vulnerable-kubernetes-components", - "cre_ids": ["053-751", "715-334", "863-521"] + "cre_ids": ["715-334", "053-751", "715-223"] } -] +] \ No newline at end of file diff --git a/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2025.json b/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2025.json index c55afb059..297bf52e2 100644 --- a/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2025.json +++ b/application/tests/fixtures/owasp_mappings/owasp_kubernetes_top10_2025.json @@ -2,71 +2,69 @@ { "section_id": "K01", "section": "Insecure Workload Configurations", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", - "cre_ids": ["233-748", "486-813"], - "fallback_section_ids": ["K01"] + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K01-Insecure-Workload-Configurations.html", + "cre_ids": ["233-748", "486-813"] }, { "section_id": "K02", "section": "Overly Permissive Authorization Configurations", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K02-Overly-Permissive-Authorization-Configurations.html", "cre_ids": ["128-128", "724-770"], "fallback_section_ids": ["K03"] }, { "section_id": "K03", "section": "Secrets Management Failures", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K03-Secrets-Management-Failures.html", "cre_ids": ["340-375", "774-888", "813-610"], "fallback_section_ids": ["K08"] }, { "section_id": "K04", "section": "Lack Of Cluster Level Policy Enforcement", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", - "cre_ids": ["117-371"], - "fallback_section_ids": ["K04"] + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K04-Lack-Of-Cluster-Level-Policy-Enforcement.html", + "cre_ids": ["117-371"] }, { "section_id": "K05", "section": "Missing Network Segmentation Controls", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K05-Missing-Network-Segmentation-Controls.html", "cre_ids": ["132-146", "467-784", "515-021"], "fallback_section_ids": ["K07"] }, { "section_id": "K06", "section": "Overly Exposed Kubernetes Components", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K06-Overly-Exposed-Kubernetes-Components.html", "cre_ids": ["152-725", "640-364"], "fallback_section_ids": ["K09"] }, { "section_id": "K07", "section": "Misconfigured And Vulnerable Cluster Components", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K07-Misconfigured-And-Vulnerable-Cluster-Components.html", "cre_ids": ["053-751", "233-748", "486-813", "715-334"], "fallback_section_ids": ["K09", "K10"] }, { "section_id": "K08", "section": "Cluster To Cloud Lateral Movement", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K08-Cluster-To-Cloud-Lateral-Movement.html", "cre_ids": ["132-146", "640-364", "724-770"], "fallback_section_ids": ["K03", "K07"] }, { "section_id": "K09", "section": "Broken Authentication Mechanisms", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K09-Broken-Authentication-Mechanisms.html", "cre_ids": ["177-260", "586-842", "633-428"], "fallback_section_ids": ["K06"] }, { "section_id": "K10", "section": "Inadequate Logging And Monitoring", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K10-Inadequate-Logging-And-Monitoring.html", "cre_ids": ["058-083", "148-420", "402-706", "843-841"], "fallback_section_ids": ["K05"] } -] +] \ No newline at end of file diff --git a/application/tests/librarian/dataset_test.py b/application/tests/librarian/dataset_test.py index bb497b719..8716f733c 100644 --- a/application/tests/librarian/dataset_test.py +++ b/application/tests/librarian/dataset_test.py @@ -151,9 +151,71 @@ def test_duplicate_id_is_rejected(self): os.unlink(tmp) +# Import the parsers for the new standards so they can be registered into the DB. +from application.utils.external_project_parsers.base_parser import BaseParser +from application.utils.external_project_parsers.parsers import ( + owasp_kubernetes_top10_2022, + owasp_kubernetes_top10_2025, +) + +# Optionally import API, LLM, AISVS if they exist; if not, skip gracefully. +try: + from application.utils.external_project_parsers.parsers import ( + owasp_api_security_top10_2023, + ) +except ImportError: + owasp_api_security_top10_2023 = None +try: + from application.utils.external_project_parsers.parsers import ( + owasp_llm_top10_2025, + ) +except ImportError: + owasp_llm_top10_2025 = None +try: + from application.utils.external_project_parsers.parsers import ( + owasp_aisvs, + ) +except ImportError: + owasp_aisvs = None + + class TestDatasetDeterminism(unittest.TestCase): """The committed JSON must re-derive identically from the DB.""" + @classmethod + def setUpClass(cls): + """Populate the DB with all standards that appear in the golden dataset.""" + if not os.path.exists(_DB): + return # test will be skipped anyway + parser = BaseParser() + # Register Kubernetes standards + parser.register_resource( + owasp_kubernetes_top10_2022.OwaspKubernetesTop10_2022, + db_connection_str=f"sqlite:///{_DB}", + ) + parser.register_resource( + owasp_kubernetes_top10_2025.OwaspKubernetesTop10_2025, + db_connection_str=f"sqlite:///{_DB}", + ) + # Register API Security if available + if owasp_api_security_top10_2023 is not None: + parser.register_resource( + owasp_api_security_top10_2023.OwaspApiSecurityTop10_2023, + db_connection_str=f"sqlite:///{_DB}", + ) + # Register LLM if available + if owasp_llm_top10_2025 is not None: + parser.register_resource( + owasp_llm_top10_2025.OwaspLlmTop10_2025, + db_connection_str=f"sqlite:///{_DB}", + ) + # Register AISVS if available + if owasp_aisvs is not None: + parser.register_resource( + owasp_aisvs.AISVS, + db_connection_str=f"sqlite:///{_DB}", + ) + def test_build_check_matches_committed_dataset(self): if not os.path.exists(_DB): self.skipTest("standards_cache.sqlite not present") diff --git a/application/tests/librarian/fixtures/golden_dataset.json b/application/tests/librarian/fixtures/golden_dataset.json index 816539b4a..1b9e0e310 100644 --- a/application/tests/librarian/fixtures/golden_dataset.json +++ b/application/tests/librarian/fixtures/golden_dataset.json @@ -5645,263 +5645,263 @@ } }, { - "id": "gold:cwe:1004:positive_multi", + "id": "gold:cwe:1021:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Sensitive Cookie Without 'HttpOnly' Flag", + "text": "Improper Restriction of Rendered UI Layers or Frames", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "284-521", - "804-220" + "257-668", + "480-071" ] }, "provenance": { - "section_path": "1004", + "section_path": "1021", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1021:positive_multi", + "id": "gold:cwe:1022:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Improper Restriction of Rendered UI Layers or Frames", + "text": "Use of Web Link to Untrusted Target with window.opener Access", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "257-668", - "480-071" + "117-371", + "757-271", + "801-310" ] }, "provenance": { - "section_path": "1021", + "section_path": "1022", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1053:positive_multi", + "id": "gold:cwe:1188:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Missing Documentation for Design", + "text": "Initialization of a Resource with an Insecure Default", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "068-102", - "162-655", - "820-878" + "206-254", + "287-251", + "346-640", + "622-835" ] }, "provenance": { - "section_path": "1053", + "section_path": "1188", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1059:positive_multi", + "id": "gold:cwe:1191:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Insufficient Technical Documentation", + "text": "On-Chip Debug and Test Interface With Improper Access Control", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "068-102", - "162-655", - "820-878" + "117-371", + "757-271", + "801-310" ] }, "provenance": { - "section_path": "1059", + "section_path": "1191", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1110:positive_multi", + "id": "gold:cwe:1204:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Incomplete Design Documentation", + "text": "Generation of Weak Initialization Vector (IV)", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "068-102", - "162-655", - "820-878", - "822-100" + "206-254", + "287-251", + "346-640", + "622-835" ] }, "provenance": { - "section_path": "1110", + "section_path": "1204", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1111:positive_multi", + "id": "gold:cwe:1220:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Incomplete I/O Documentation", + "text": "Insufficient Granularity of Access Control", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "068-102", - "162-655", - "820-878" + "117-371", + "757-271", + "801-310" ] }, "provenance": { - "section_path": "1111", + "section_path": "1220", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1112:positive_multi", + "id": "gold:cwe:1222:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Incomplete Documentation of Program Execution", + "text": "Insufficient Granularity of Address Regions Protected by Register Locks", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "068-102", - "162-655", - "820-878" + "117-371", + "757-271", + "801-310" ] }, "provenance": { - "section_path": "1112", + "section_path": "1222", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1118:positive_multi", + "id": "gold:cwe:1224:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Insufficient Documentation of Error Handling Techniques", + "text": "Improper Restriction of Write-Once Bit Fields", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "068-102", - "162-655", - "820-878" + "117-371", + "757-271", + "801-310" ] }, "provenance": { - "section_path": "1118", + "section_path": "1224", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1173:positive_multi", + "id": "gold:cwe:1231:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Improper Use of Validation Framework", + "text": "Improper Prevention of Lock Bit Modification", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "031-447", - "146-706", - "611-051", - "653-242" + "117-371", + "757-271", + "801-310" ] }, "provenance": { - "section_path": "1173", + "section_path": "1231", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1174:positive_multi", + "id": "gold:cwe:1233:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "ASP.NET Misconfiguration: Improper Model Validation", + "text": "Security-Sensitive Hardware Controls with Missing Lock Bit Protection", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "031-447", - "146-706", - "611-051", - "653-242" + "117-371", + "757-271", + "801-310" ] }, "provenance": { - "section_path": "1174", + "section_path": "1233", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1191:positive_multi", + "id": "gold:cwe:1239:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "On-Chip Debug and Test Interface With Improper Access Control", + "text": "Improper Zeroization of Hardware Register", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "117-371", - "757-271", - "801-310" + "715-304", + "762-451" ] }, "provenance": { - "section_path": "1191", + "section_path": "1239", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:120:positive_multi", + "id": "gold:cwe:1240:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')", + "text": "Use of a Cryptographic Primitive with a Risky Implementation", "source_standard": "OTHER" }, "expected": { "decision": "linked", "cre_ids": [ - "314-131", - "831-570" + "002-801", + "504-340", + "742-431" ] }, "provenance": { - "section_path": "120", + "section_path": "1240", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1204:positive_multi", + "id": "gold:cwe:1241:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Generation of Weak Initialization Vector (IV)", + "text": "Use of Predictable Algorithm in Random Number Generator", "source_standard": "OTHER" }, "expected": { @@ -5914,16 +5914,16 @@ ] }, "provenance": { - "section_path": "1204", + "section_path": "1241", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1220:positive_multi", + "id": "gold:cwe:1243:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Insufficient Granularity of Access Control", + "text": "Sensitive Non-Volatile Information Not Protected During Debug", "source_standard": "OTHER" }, "expected": { @@ -5935,16 +5935,16 @@ ] }, "provenance": { - "section_path": "1220", + "section_path": "1243", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, { - "id": "gold:cwe:1222:positive_multi", + "id": "gold:cwe:1252:positive_multi", "schema_version": "0.1.0", "slice": "positive", "input": { - "text": "Insufficient Granularity of Address Regions Protected by Register Locks", + "text": "CPU Hardware Not Configured to Support Exclusivity of Write and Execute Operations", "source_standard": "OTHER" }, "expected": { @@ -5956,10 +5956,432 @@ ] }, "provenance": { - "section_path": "1222", + "section_path": "1252", "ground_truth_source": "OpenCRE DB mapping (multi-CRE node from CWE)" } }, + { + "id": "gold:kubernetes:2022:K01:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Insecure Workload Configurations", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "053-751", + "233-748", + "486-813", + "715-334" + ] + }, + "provenance": { + "section_path": "K01", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K02:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Supply Chain Vulnerabilities", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "307-507", + "613-285", + "613-287", + "715-223" + ] + }, + "provenance": { + "section_path": "K02", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K03:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Overly Permissive RBAC Configurations", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "128-128", + "724-770" + ] + }, + "provenance": { + "section_path": "K03", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K04:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Lack of Centralized Policy Enforcement", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "117-371", + "344-611" + ] + }, + "provenance": { + "section_path": "K04", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K05:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Inadequate Logging and Monitoring", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "058-083", + "148-420", + "402-706", + "843-841" + ] + }, + "provenance": { + "section_path": "K05", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K06:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Broken Authentication Mechanisms", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "113-133", + "177-260", + "576-042", + "586-842", + "633-428" + ] + }, + "provenance": { + "section_path": "K06", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K07:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Missing Network Segmentation Controls", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "132-146", + "467-784", + "515-021" + ] + }, + "provenance": { + "section_path": "K07", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K08:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Secrets Management Failures", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "340-375", + "774-888", + "813-610" + ] + }, + "provenance": { + "section_path": "K08", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K09:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Misconfigured Cluster Components", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "053-751", + "233-748", + "486-813", + "715-334" + ] + }, + "provenance": { + "section_path": "K09", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2022:K10:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Outdated and Vulnerable Kubernetes Components", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "053-751", + "715-223", + "715-334", + "863-521" + ] + }, + "provenance": { + "section_path": "K10", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2022" + } + }, + { + "id": "gold:kubernetes:2025:K01:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Insecure Workload Configurations", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "233-748", + "486-813" + ] + }, + "provenance": { + "section_path": "K01", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K02:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Overly Permissive Authorization Configurations", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "128-128", + "724-770" + ] + }, + "provenance": { + "section_path": "K02", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K03:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Secrets Management Failures", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "340-375", + "774-888", + "813-610" + ] + }, + "provenance": { + "section_path": "K03", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K04:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Lack Of Cluster Level Policy Enforcement", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "117-371" + ] + }, + "provenance": { + "section_path": "K04", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K05:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Missing Network Segmentation Controls", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "132-146", + "467-784", + "515-021" + ] + }, + "provenance": { + "section_path": "K05", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K06:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Overly Exposed Kubernetes Components", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "152-725", + "640-364" + ] + }, + "provenance": { + "section_path": "K06", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K07:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Misconfigured And Vulnerable Cluster Components", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "053-751", + "233-748", + "486-813", + "715-334" + ] + }, + "provenance": { + "section_path": "K07", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K08:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Cluster To Cloud Lateral Movement", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "132-146", + "640-364", + "724-770" + ] + }, + "provenance": { + "section_path": "K08", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K09:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Broken Authentication Mechanisms", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "177-260", + "586-842", + "633-428" + ] + }, + "provenance": { + "section_path": "K09", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, + { + "id": "gold:kubernetes:2025:K10:positive", + "schema_version": "0.1.0", + "slice": "positive", + "input": { + "text": "Inadequate Logging And Monitoring", + "source_standard": "OTHER" + }, + "expected": { + "decision": "linked", + "cre_ids": [ + "058-083", + "148-420", + "402-706", + "843-841" + ] + }, + "provenance": { + "section_path": "K10", + "ground_truth_source": "manual mapping from OWASP Kubernetes Top Ten 2025" + } + }, { "id": "gold:asvs:V1.1.3:hard_negative", "schema_version": "0.1.0", diff --git a/application/utils/external_project_parsers/parsers/owasp_kubernetes_top10_2022.py b/application/utils/external_project_parsers/parsers/owasp_kubernetes_top10_2022.py new file mode 100644 index 000000000..cfd3f3894 --- /dev/null +++ b/application/utils/external_project_parsers/parsers/owasp_kubernetes_top10_2022.py @@ -0,0 +1,51 @@ +import json +from pathlib import Path + +from application.database import db +from application.defs import cre_defs as defs +from application.prompt_client import prompt_client +from application.utils.external_project_parsers.base_parser_defs import ( + ParseResult, + ParserInterface, +) + + +class OwaspKubernetesTop10_2022(ParserInterface): + name = "OWASP Kubernetes Top Ten 2022" + data_file = ( + Path(__file__).resolve().parents[3] + / "tests" + / "fixtures" + / "owasp_mappings" + / "owasp_kubernetes_top10_2022.json" + ) + + def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): + with self.data_file.open("r", encoding="utf-8") as handle: + raw_entries = json.load(handle) + + entries = [] + for entry in raw_entries: + standard = defs.Standard( + name=self.name, + sectionID=entry["section_id"], + section=entry["section"], + hyperlink=entry["hyperlink"], + ) + for cre_id in entry.get("cre_ids", []): + cres = cache.get_CREs(external_id=cre_id) + if not cres: + continue + standard.add_link( + defs.Link( + ltype=defs.LinkTypes.LinkedTo, + document=cres[0].shallow_copy(), + ) + ) + entries.append(standard) + + return ParseResult( + results={self.name: entries}, + calculate_gap_analysis=False, + calculate_embeddings=False, + ) diff --git a/application/utils/external_project_parsers/parsers/owasp_kubernetes_top10_2025.py b/application/utils/external_project_parsers/parsers/owasp_kubernetes_top10_2025.py new file mode 100644 index 000000000..cd3a09336 --- /dev/null +++ b/application/utils/external_project_parsers/parsers/owasp_kubernetes_top10_2025.py @@ -0,0 +1,82 @@ +import json +from pathlib import Path + +from application.database import db +from application.defs import cre_defs as defs +from application.prompt_client import prompt_client +from application.utils.external_project_parsers.base_parser_defs import ( + ParseResult, + ParserInterface, +) + + +class OwaspKubernetesTop10_2025(ParserInterface): + name = "OWASP Kubernetes Top Ten 2025 (Draft)" + data_file = ( + Path(__file__).resolve().parents[3] + / "tests" + / "fixtures" + / "owasp_mappings" + / "owasp_kubernetes_top10_2025.json" + ) + fallback_data_file = ( + Path(__file__).resolve().parents[3] + / "tests" + / "fixtures" + / "owasp_mappings" + / "owasp_kubernetes_top10_2022.json" + ) + + def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): + with self.data_file.open("r", encoding="utf-8") as handle: + raw_entries = json.load(handle) + with self.fallback_data_file.open("r", encoding="utf-8") as handle: + fallback_entries = { + entry["section_id"]: entry for entry in json.load(handle) + } + + entries = [] + for entry in raw_entries: + standard = defs.Standard( + name=self.name, + sectionID=entry["section_id"], + section=entry["section"], + hyperlink=entry["hyperlink"], + ) + linked_cre_ids = [] + for cre_id in entry.get("cre_ids", []): + cres = cache.get_CREs(external_id=cre_id) + if not cres: + continue + linked_cre_ids.append(cre_id) + standard.add_link( + defs.Link( + ltype=defs.LinkTypes.LinkedTo, + document=cres[0].shallow_copy(), + ) + ) + if not linked_cre_ids: + for section_id in entry.get("fallback_section_ids", []): + fallback_entry = fallback_entries.get(section_id) + if not fallback_entry: + continue + for cre_id in fallback_entry.get("cre_ids", []): + if cre_id in linked_cre_ids: + continue + cres = cache.get_CREs(external_id=cre_id) + if not cres: + continue + linked_cre_ids.append(cre_id) + standard.add_link( + defs.Link( + ltype=defs.LinkTypes.LinkedTo, + document=cres[0].shallow_copy(), + ) + ) + entries.append(standard) + + return ParseResult( + results={self.name: entries}, + calculate_gap_analysis=False, + calculate_embeddings=False, + ) diff --git a/scripts/build_golden_dataset.py b/scripts/build_golden_dataset.py index fe8187e97..56ca09aca 100644 --- a/scripts/build_golden_dataset.py +++ b/scripts/build_golden_dataset.py @@ -6,6 +6,7 @@ positive : all 277 ASVS requirements (1:1 mapping) + multi-link rows from OWASP Top 10 and CWE (2-4 CREs) + + OWASP Kubernetes Top Ten 2022 and 2025 (all 10 each) hard_negative : ASVS requirements whose text contains a negation phrase ("do not", "does not", "shall not", "should not"), with their real DB CRE mapping (cross-encoder must beat cosine @@ -24,6 +25,7 @@ import json import sqlite3 import sys +import re from pathlib import Path from typing import Dict, List, Optional @@ -290,6 +292,43 @@ def build_positive_multilink(conn: sqlite3.Connection) -> List[Dict]: return out +def build_kubernetes(conn: sqlite3.Connection) -> List[Dict]: + """Add OWASP Kubernetes Top Ten 2022 and 2025 entries.""" + rows = conn.execute( + """ + SELECT n.name, n.section_id, n.section, + GROUP_CONCAT(c.external_id, '|') + FROM node n + JOIN cre_node_links l ON l.node = n.id + JOIN cre c ON c.id = l.cre + WHERE n.name LIKE '%Kubernetes Top Ten%' + AND n.section_id LIKE 'K%' + GROUP BY n.id + ORDER BY n.name, n.section_id + """ + ).fetchall() + out = [] + for name, section_id, text, cre_concat in rows: + cre_ids = sorted(set(cre_concat.split("|"))) + year_match = re.search(r"\b(20\d{2})\b", name) + year = year_match.group(1) if year_match else "2025" + # Unique ID with year + out.append( + { + "id": f"gold:kubernetes:{year}:{section_id}:positive", + "schema_version": SCHEMA_VERSION, + "slice": "positive", + "input": {"text": text, "source_standard": "OTHER"}, + "expected": {"decision": "linked", "cre_ids": cre_ids}, + "provenance": { + "section_path": section_id, + "ground_truth_source": f"manual mapping from OWASP Kubernetes Top Ten {year}", + }, + } + ) + return out + + def build_hard_negative(conn: sqlite3.Connection) -> List[Dict]: rows = conn.execute( """ @@ -427,6 +466,7 @@ def build(conn: sqlite3.Connection) -> List[Dict]: rows.extend(build_explicit(conn)) rows.extend(build_positive_asvs(conn)) rows.extend(build_positive_multilink(conn)) + rows.extend(build_kubernetes(conn)) # <-- NEW rows.extend(build_hard_negative(conn)) rows.extend(build_update(conn)) rows.extend(build_ambiguous()) diff --git a/scripts/validate_golden_dataset.py b/scripts/validate_golden_dataset.py new file mode 100644 index 000000000..3eba61fbd --- /dev/null +++ b/scripts/validate_golden_dataset.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +""" +Validate golden_dataset.json entries against the OpenCRE database. +Checks that all CRE IDs referenced in the dataset actually exist. +""" + +import json +import sqlite3 +import sys +from pathlib import Path + + +def validate_golden_dataset(golden_path: str, db_path: str) -> int: + """Validate all CRE IDs in the golden dataset.""" + errors = 0 + + # Load golden dataset + with open(golden_path, "r") as f: + entries = json.load(f) + + # Connect to database + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + # Get all existing CRE IDs + cursor.execute("SELECT external_id FROM cre") + existing_ids = {row[0] for row in cursor.fetchall()} + + print(f"šŸ“Š Found {len(existing_ids)} CREs in database") + print(f"šŸ“Š Checking {len(entries)} golden entries...") + print() + + for entry in entries: + if entry["expected"]["decision"] != "linked": + continue + + for cre_id in entry["expected"]["cre_ids"]: + if cre_id not in existing_ids: + print(f"āŒ CRE ID not found: {cre_id} in {entry['id']}") + errors += 1 + else: + print(f"āœ… {cre_id} OK in {entry['id']}") + + conn.close() + + if errors == 0: + print(f"\nāœ… All {len(entries)} entries validated successfully!") + else: + print(f"\nāŒ {errors} errors found. Please fix the missing CRE IDs.") + + return errors + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print( + "Usage: python validate_golden_dataset.py " + ) + sys.exit(1) + + golden_path = sys.argv[1] + db_path = sys.argv[2] + + if not Path(golden_path).exists(): + print(f"āŒ File not found: {golden_path}") + sys.exit(1) + + if not Path(db_path).exists(): + print(f"āŒ Database not found: {db_path}") + sys.exit(1) + + sys.exit(validate_golden_dataset(golden_path, db_path))