From 61024d1cd35e5842640d0cc49bb7519ea7f807c2 Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 7 Jul 2026 13:49:46 +0530 Subject: [PATCH 1/8] feat: add standalone OWASP AI and Top 10 importers --- application/cmd/cre_main.py | 30 +++++++ application/tests/owasp_aisvs_parser_test.py | 66 ++++++++++++++ .../tests/owasp_api_top10_2023_parser_test.py | 47 ++++++++++ .../tests/owasp_llm_top10_2025_parser_test.py | 45 ++++++++++ .../tests/owasp_top10_2025_parser_test.py | 80 +++++++++++++++++ .../data/owasp_aisvs_1_0.json | 86 +++++++++++++++++++ .../data/owasp_api_top10_2023.json | 62 +++++++++++++ .../data/owasp_llm_top10_2025.json | 62 +++++++++++++ .../data/owasp_top10_2025.json | 62 +++++++++++++ .../parsers/owasp_aisvs.py | 45 ++++++++++ .../parsers/owasp_api_top10_2023.py | 47 ++++++++++ .../parsers/owasp_llm_top10_2025.py | 47 ++++++++++ .../parsers/owasp_top10_2025.py | 47 ++++++++++ cre.py | 20 +++++ 14 files changed, 746 insertions(+) create mode 100644 application/tests/owasp_aisvs_parser_test.py create mode 100644 application/tests/owasp_api_top10_2023_parser_test.py create mode 100644 application/tests/owasp_llm_top10_2025_parser_test.py create mode 100644 application/tests/owasp_top10_2025_parser_test.py create mode 100644 application/utils/external_project_parsers/data/owasp_aisvs_1_0.json create mode 100644 application/utils/external_project_parsers/data/owasp_api_top10_2023.json create mode 100644 application/utils/external_project_parsers/data/owasp_llm_top10_2025.json create mode 100644 application/utils/external_project_parsers/data/owasp_top10_2025.json create mode 100644 application/utils/external_project_parsers/parsers/owasp_aisvs.py create mode 100644 application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py create mode 100644 application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py create mode 100644 application/utils/external_project_parsers/parsers/owasp_top10_2025.py diff --git a/application/cmd/cre_main.py b/application/cmd/cre_main.py index e9872e752..391b3f85c 100644 --- a/application/cmd/cre_main.py +++ b/application/cmd/cre_main.py @@ -975,6 +975,36 @@ def run(args: argparse.Namespace) -> None: # pragma: no cover BaseParser().register_resource( secure_headers.SecureHeaders, db_connection_str=args.cache_file ) + if args.owasp_top10_2025_in: + from application.utils.external_project_parsers.parsers import owasp_top10_2025 + + BaseParser().register_resource( + owasp_top10_2025.OwaspTop10_2025, db_connection_str=args.cache_file + ) + if args.owasp_api_top10_2023_in: + from application.utils.external_project_parsers.parsers import ( + owasp_api_top10_2023, + ) + + BaseParser().register_resource( + owasp_api_top10_2023.OwaspApiTop10_2023, + db_connection_str=args.cache_file, + ) + if args.owasp_llm_top10_2025_in: + from application.utils.external_project_parsers.parsers import ( + owasp_llm_top10_2025, + ) + + BaseParser().register_resource( + owasp_llm_top10_2025.OwaspLlmTop10_2025, + db_connection_str=args.cache_file, + ) + if args.owasp_aisvs_in: + from application.utils.external_project_parsers.parsers import owasp_aisvs + + BaseParser().register_resource( + owasp_aisvs.OwaspAisvs, db_connection_str=args.cache_file + ) if args.pci_dss_4_in: from application.utils.external_project_parsers.parsers import pci_dss diff --git a/application/tests/owasp_aisvs_parser_test.py b/application/tests/owasp_aisvs_parser_test.py new file mode 100644 index 000000000..edf547bcd --- /dev/null +++ b/application/tests/owasp_aisvs_parser_test.py @@ -0,0 +1,66 @@ +import unittest + +from application import create_app, sqla # type: ignore +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.parsers import owasp_aisvs + + +class TestOwaspAisvsParser(unittest.TestCase): + def tearDown(self) -> None: + sqla.session.remove() + sqla.drop_all() + self.app_context.pop() + + def setUp(self) -> None: + self.app = create_app(mode="test") + self.app_context = self.app.app_context() + self.app_context.push() + sqla.create_all() + self.collection = db.Node_collection() + + def test_parse(self) -> None: + for cre_id, name in [ + ("227-045", "Identify sensitive data and subject it to a policy"), + ( + "307-507", + "Allow only trusted sources both build time and runtime; therefore perform integrity checks on all resources and code", + ), + ( + "162-655", + "Documentation of all components' business or security function", + ), + ]: + self.collection.add_cre(defs.CRE(id=cre_id, name=name, description="")) + + result = owasp_aisvs.OwaspAisvs().parse( + self.collection, prompt_client.PromptHandler(database=self.collection) + ) + + entries = result.results["OWASP AI Security Verification Standard (AISVS)"] + self.assertEqual(14, len(entries)) + self.assertEqual("AISVS1", entries[0].sectionID) + self.assertEqual( + "Training Data Governance & Bias Management", entries[0].section + ) + self.assertEqual( + "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", + entries[0].hyperlink, + ) + self.assertEqual( + ["227-045", "307-507"], + [link.document.id for link in entries[0].links], + ) + self.assertEqual("AISVS14", entries[-1].sectionID) + self.assertEqual( + "Human Oversight, Accountability & Governance", entries[-1].section + ) + self.assertEqual( + "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C14-Human-Oversight.md", + entries[-1].hyperlink, + ) + self.assertEqual( + ["162-655"], + [link.document.id for link in entries[-1].links], + ) diff --git a/application/tests/owasp_api_top10_2023_parser_test.py b/application/tests/owasp_api_top10_2023_parser_test.py new file mode 100644 index 000000000..4f7d8e1b9 --- /dev/null +++ b/application/tests/owasp_api_top10_2023_parser_test.py @@ -0,0 +1,47 @@ +import unittest + +from application import create_app, sqla # type: ignore +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.parsers import owasp_api_top10_2023 + + +class TestOwaspApiTop10_2023Parser(unittest.TestCase): + def tearDown(self) -> None: + sqla.session.remove() + sqla.drop_all() + self.app_context.pop() + + def setUp(self) -> None: + self.app = create_app(mode="test") + self.app_context = self.app.app_context() + self.app_context.push() + sqla.create_all() + self.collection = db.Node_collection() + + def test_parse(self) -> None: + for cre_id, name in [ + ("304-667", "Protect API against unauthorized access/modification (IDOR)"), + ("724-770", "Technical application access control"), + ("715-223", "Ensure trusted origin of third party resources"), + ]: + self.collection.add_cre(defs.CRE(id=cre_id, name=name, description="")) + + result = owasp_api_top10_2023.OwaspApiTop10_2023().parse( + self.collection, prompt_client.PromptHandler(database=self.collection) + ) + + entries = result.results["OWASP API Security Top 10 2023"] + self.assertEqual(10, len(entries)) + self.assertEqual("API1", entries[0].sectionID) + self.assertEqual("Broken Object Level Authorization", entries[0].section) + self.assertEqual( + ["304-667", "724-770"], + [link.document.id for link in entries[0].links], + ) + self.assertEqual("API10", entries[-1].sectionID) + self.assertEqual( + ["715-223"], + [link.document.id for link in entries[-1].links], + ) diff --git a/application/tests/owasp_llm_top10_2025_parser_test.py b/application/tests/owasp_llm_top10_2025_parser_test.py new file mode 100644 index 000000000..f31e4316e --- /dev/null +++ b/application/tests/owasp_llm_top10_2025_parser_test.py @@ -0,0 +1,45 @@ +import unittest + +from application import create_app, sqla # type: ignore +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.parsers import owasp_llm_top10_2025 + + +class TestOwaspLlmTop10_2025Parser(unittest.TestCase): + def tearDown(self) -> None: + sqla.session.remove() + sqla.drop_all() + self.app_context.pop() + + def setUp(self) -> None: + self.app = create_app(mode="test") + self.app_context = self.app.app_context() + self.app_context.push() + sqla.create_all() + self.collection = db.Node_collection() + + def test_parse(self) -> None: + for cre_id, name in [ + ("161-451", "Output encoding and injection prevention"), + ("064-808", "Encode output context-specifically"), + ("760-764", "Injection protection"), + ("623-550", "Denial Of Service protection"), + ]: + self.collection.add_cre(defs.CRE(id=cre_id, name=name, description="")) + + result = owasp_llm_top10_2025.OwaspLlmTop10_2025().parse( + self.collection, prompt_client.PromptHandler(database=self.collection) + ) + + entries = result.results["OWASP Top 10 for LLM and Gen AI Apps 2025"] + self.assertEqual(10, len(entries)) + self.assertEqual("LLM01", entries[0].sectionID) + self.assertEqual("Prompt Injection", entries[0].section) + self.assertEqual( + ["161-451", "760-764"], [link.document.id for link in entries[0].links] + ) + self.assertEqual(["064-808"], [link.document.id for link in entries[4].links]) + self.assertEqual("LLM10", entries[-1].sectionID) + self.assertEqual(["623-550"], [link.document.id for link in entries[-1].links]) diff --git a/application/tests/owasp_top10_2025_parser_test.py b/application/tests/owasp_top10_2025_parser_test.py new file mode 100644 index 000000000..de4f86a9f --- /dev/null +++ b/application/tests/owasp_top10_2025_parser_test.py @@ -0,0 +1,80 @@ +import unittest + +from application import create_app, sqla # type: ignore +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.parsers import owasp_top10_2025 + + +class TestOwaspTop10_2025Parser(unittest.TestCase): + def tearDown(self) -> None: + sqla.session.remove() + sqla.drop_all() + self.app_context.pop() + + def setUp(self) -> None: + self.app = create_app(mode="test") + self.app_context = self.app.app_context() + self.app_context.push() + sqla.create_all() + self.collection = db.Node_collection() + + def test_parse(self) -> None: + self.collection.add_cre( + defs.CRE(id="177-260", name="Session management", description="") + ) + self.collection.add_cre( + defs.CRE( + id="117-371", + name="Use a centralized access control mechanism", + description="", + ) + ) + self.collection.add_cre( + defs.CRE( + id="724-770", + name="Technical application access control", + description="", + ) + ) + self.collection.add_cre( + defs.CRE( + id="031-447", name="Whitelist all external (HTTP) input", description="" + ) + ) + self.collection.add_cre( + defs.CRE( + id="064-808", name="Encode output context-specifically", description="" + ) + ) + self.collection.add_cre( + defs.CRE(id="760-764", name="Injection protection", description="") + ) + self.collection.add_cre( + defs.CRE(id="513-183", name="Error handling", description="") + ) + + result = owasp_top10_2025.OwaspTop10_2025().parse( + self.collection, + prompt_client.PromptHandler(database=self.collection), + ) + + entries = result.results["OWASP Top 10 2025"] + self.assertEqual(10, len(entries)) + self.assertEqual("A01", entries[0].sectionID) + self.assertEqual("Broken Access Control", entries[0].section) + self.assertEqual( + "https://owasp.org/Top10/2025/A01_2025-Broken_Access_Control/", + entries[0].hyperlink, + ) + self.assertEqual( + ["117-371", "177-260", "724-770"], + [link.document.id for link in entries[0].links], + ) + self.assertEqual( + ["031-447", "064-808", "760-764"], + [link.document.id for link in entries[4].links], + ) + self.assertEqual("A10", entries[-1].sectionID) + self.assertEqual(["513-183"], [link.document.id for link in entries[-1].links]) diff --git a/application/utils/external_project_parsers/data/owasp_aisvs_1_0.json b/application/utils/external_project_parsers/data/owasp_aisvs_1_0.json new file mode 100644 index 000000000..9142ec347 --- /dev/null +++ b/application/utils/external_project_parsers/data/owasp_aisvs_1_0.json @@ -0,0 +1,86 @@ +[ + { + "section_id": "AISVS1", + "section": "Training Data Governance & Bias Management", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", + "cre_ids": ["227-045", "307-507"] + }, + { + "section_id": "AISVS2", + "section": "User Input Validation", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C02-Input-Validation.md", + "cre_ids": ["031-447", "760-764"] + }, + { + "section_id": "AISVS3", + "section": "Model Lifecycle Management & Change Control", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C03-Model-Lifecycle-Management.md", + "cre_ids": ["148-853", "613-285"] + }, + { + "section_id": "AISVS4", + "section": "Infrastructure, Configuration & Deployment Security", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C04-Infrastructure.md", + "cre_ids": ["233-748", "486-813"] + }, + { + "section_id": "AISVS5", + "section": "Access Control & Identity for AI Components & Users", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C05-Access-Control-and-Identity.md", + "cre_ids": ["633-428", "724-770"] + }, + { + "section_id": "AISVS6", + "section": "Supply Chain Security for Models, Frameworks & Data", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C06-Supply-Chain.md", + "cre_ids": ["613-285", "613-287", "863-521"] + }, + { + "section_id": "AISVS7", + "section": "Model Behavior, Output Control & Safety Assurance", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C07-Model-Behavior.md", + "cre_ids": ["064-808", "141-555"] + }, + { + "section_id": "AISVS8", + "section": "Memory, Embeddings & Vector Database Security", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C08-Memory-Embeddings-and-Vector-Database.md", + "cre_ids": ["126-668", "538-770"] + }, + { + "section_id": "AISVS9", + "section": "Autonomous Orchestration & Agentic Action Security", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C09-Orchestration-and-Agentic-Action.md", + "cre_ids": ["117-371", "650-560"] + }, + { + "section_id": "AISVS10", + "section": "Model Context Protocol (MCP) Security", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C10-MCP-Security.md", + "cre_ids": ["307-507", "715-223"] + }, + { + "section_id": "AISVS11", + "section": "Adversarial Robustness & Privacy Defense", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C11-Adversarial-Robustness.md", + "cre_ids": ["141-555", "623-550"] + }, + { + "section_id": "AISVS12", + "section": "Privacy Protection & Personal Data Management", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C12-Privacy.md", + "cre_ids": ["126-668", "227-045", "482-866"] + }, + { + "section_id": "AISVS13", + "section": "Monitoring, Logging & Anomaly Detection", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C13-Monitoring-and-Logging.md", + "cre_ids": ["058-083", "148-420", "402-706", "843-841"] + }, + { + "section_id": "AISVS14", + "section": "Human Oversight, Accountability & Governance", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C14-Human-Oversight.md", + "cre_ids": ["162-655", "766-162"] + } +] diff --git a/application/utils/external_project_parsers/data/owasp_api_top10_2023.json b/application/utils/external_project_parsers/data/owasp_api_top10_2023.json new file mode 100644 index 000000000..7a8df0ed0 --- /dev/null +++ b/application/utils/external_project_parsers/data/owasp_api_top10_2023.json @@ -0,0 +1,62 @@ +[ + { + "section_id": "API1", + "section": "Broken Object Level Authorization", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/", + "cre_ids": ["304-667", "724-770"] + }, + { + "section_id": "API2", + "section": "Broken Authentication", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/", + "cre_ids": ["177-260", "586-842", "633-428"] + }, + { + "section_id": "API3", + "section": "Broken Object Property Level Authorization", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa3-broken-object-property-level-authorization/", + "cre_ids": ["538-770", "724-770", "128-128"] + }, + { + "section_id": "API4", + "section": "Unrestricted Resource Consumption", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/", + "cre_ids": ["623-550"] + }, + { + "section_id": "API5", + "section": "Broken Function Level Authorization", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa5-broken-function-level-authorization/", + "cre_ids": ["650-560", "724-770"] + }, + { + "section_id": "API6", + "section": "Unrestricted Access to Sensitive Business Flows", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa6-unrestricted-access-to-sensitive-business-flows/", + "cre_ids": ["534-605", "630-573"] + }, + { + "section_id": "API7", + "section": "Server Side Request Forgery", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa7-server-side-request-forgery/", + "cre_ids": ["028-728", "657-084"] + }, + { + "section_id": "API8", + "section": "Security Misconfiguration", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/", + "cre_ids": ["486-813"] + }, + { + "section_id": "API9", + "section": "Improper Inventory Management", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa9-improper-inventory-management/", + "cre_ids": ["162-655", "863-521"] + }, + { + "section_id": "API10", + "section": "Unsafe Consumption of APIs", + "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xaa-unsafe-consumption-of-apis/", + "cre_ids": ["715-223"] + } +] diff --git a/application/utils/external_project_parsers/data/owasp_llm_top10_2025.json b/application/utils/external_project_parsers/data/owasp_llm_top10_2025.json new file mode 100644 index 000000000..b761d5e09 --- /dev/null +++ b/application/utils/external_project_parsers/data/owasp_llm_top10_2025.json @@ -0,0 +1,62 @@ +[ + { + "section_id": "LLM01", + "section": "Prompt Injection", + "hyperlink": "https://genai.owasp.org/llmrisk/llm01-prompt-injection/", + "cre_ids": ["161-451", "760-764"] + }, + { + "section_id": "LLM02", + "section": "Sensitive Information Disclosure", + "hyperlink": "https://genai.owasp.org/llmrisk/llm022025-sensitive-information-disclosure/", + "cre_ids": ["126-668", "227-045"] + }, + { + "section_id": "LLM03", + "section": "Supply Chain", + "hyperlink": "https://genai.owasp.org/llmrisk/llm032025-supply-chain/", + "cre_ids": ["613-285", "613-287"] + }, + { + "section_id": "LLM04", + "section": "Data and Model Poisoning", + "hyperlink": "https://genai.owasp.org/llmrisk/llm042025-data-and-model-poisoning/", + "cre_ids": ["307-507", "613-287"] + }, + { + "section_id": "LLM05", + "section": "Improper Output Handling", + "hyperlink": "https://genai.owasp.org/llmrisk/llm052025-improper-output-handling/", + "cre_ids": ["064-808"] + }, + { + "section_id": "LLM06", + "section": "Excessive Agency", + "hyperlink": "https://genai.owasp.org/llmrisk/llm062025-excessive-agency/", + "cre_ids": ["117-371", "650-560"] + }, + { + "section_id": "LLM07", + "section": "System Prompt Leakage", + "hyperlink": "https://genai.owasp.org/llmrisk/llm072025-system-prompt-leakage/", + "cre_ids": ["126-668", "227-045"] + }, + { + "section_id": "LLM08", + "section": "Vector and Embedding Weaknesses", + "hyperlink": "https://genai.owasp.org/llmrisk/llm082025-vector-and-embedding-weaknesses/", + "cre_ids": ["126-668", "538-770"] + }, + { + "section_id": "LLM09", + "section": "Misinformation", + "hyperlink": "https://genai.owasp.org/llmrisk/llm092025-misinformation/", + "cre_ids": ["141-555"] + }, + { + "section_id": "LLM10", + "section": "Unbounded Consumption", + "hyperlink": "https://genai.owasp.org/llmrisk/llm102025-unbounded-consumption/", + "cre_ids": ["267-031", "623-550"] + } +] diff --git a/application/utils/external_project_parsers/data/owasp_top10_2025.json b/application/utils/external_project_parsers/data/owasp_top10_2025.json new file mode 100644 index 000000000..7e19d1a4e --- /dev/null +++ b/application/utils/external_project_parsers/data/owasp_top10_2025.json @@ -0,0 +1,62 @@ +[ + { + "section_id": "A01", + "section": "Broken Access Control", + "hyperlink": "https://owasp.org/Top10/2025/A01_2025-Broken_Access_Control/", + "cre_ids": ["117-371", "177-260", "724-770"] + }, + { + "section_id": "A02", + "section": "Security Misconfiguration", + "hyperlink": "https://owasp.org/Top10/2025/A02_2025-Security_Misconfiguration/", + "cre_ids": ["486-813"] + }, + { + "section_id": "A03", + "section": "Software Supply Chain Failures", + "hyperlink": "https://owasp.org/Top10/2025/A03_2025-Software_Supply_Chain_Failures/", + "cre_ids": ["613-286", "613-287", "715-223", "863-521"] + }, + { + "section_id": "A04", + "section": "Cryptographic Failures", + "hyperlink": "https://owasp.org/Top10/2025/A04_2025-Cryptographic_Failures/", + "cre_ids": ["170-772", "227-045"] + }, + { + "section_id": "A05", + "section": "Injection", + "hyperlink": "https://owasp.org/Top10/2025/A05_2025-Injection/", + "cre_ids": ["031-447", "064-808", "760-764"] + }, + { + "section_id": "A06", + "section": "Insecure Design", + "hyperlink": "https://owasp.org/Top10/2025/A06_2025-Insecure_Design/", + "cre_ids": ["126-668", "155-155"] + }, + { + "section_id": "A07", + "section": "Authentication Failures", + "hyperlink": "https://owasp.org/Top10/2025/A07_2025-Authentication_Failures/", + "cre_ids": ["002-630", "177-260", "586-842", "633-428"] + }, + { + "section_id": "A08", + "section": "Software or Data Integrity Failures", + "hyperlink": "https://owasp.org/Top10/2025/A08_2025-Software_or_Data_Integrity_Failures/", + "cre_ids": ["613-287", "836-068"] + }, + { + "section_id": "A09", + "section": "Security Logging and Alerting Failures", + "hyperlink": "https://owasp.org/Top10/2025/A09_2025-Security_Logging_and_Alerting_Failures/", + "cre_ids": ["067-050", "148-420", "402-706", "843-841"] + }, + { + "section_id": "A10", + "section": "Mishandling of Exceptional Conditions", + "hyperlink": "https://owasp.org/Top10/2025/A10_2025-Mishandling_of_Exceptional_Conditions/", + "cre_ids": ["513-183"] + } +] diff --git a/application/utils/external_project_parsers/parsers/owasp_aisvs.py b/application/utils/external_project_parsers/parsers/owasp_aisvs.py new file mode 100644 index 000000000..cec4abad9 --- /dev/null +++ b/application/utils/external_project_parsers/parsers/owasp_aisvs.py @@ -0,0 +1,45 @@ +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 OwaspAisvs(ParserInterface): + name = "OWASP AI Security Verification Standard (AISVS)" + data_file = Path(__file__).resolve().parent.parent / "data" / "owasp_aisvs_1_0.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_api_top10_2023.py b/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py new file mode 100644 index 000000000..08157a1e9 --- /dev/null +++ b/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py @@ -0,0 +1,47 @@ +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 OwaspApiTop10_2023(ParserInterface): + name = "OWASP API Security Top 10 2023" + data_file = ( + Path(__file__).resolve().parent.parent / "data" / "owasp_api_top10_2023.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_llm_top10_2025.py b/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py new file mode 100644 index 000000000..3971b9e6b --- /dev/null +++ b/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py @@ -0,0 +1,47 @@ +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 OwaspLlmTop10_2025(ParserInterface): + name = "OWASP Top 10 for LLM and Gen AI Apps 2025" + data_file = ( + Path(__file__).resolve().parent.parent / "data" / "owasp_llm_top10_2025.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_top10_2025.py b/application/utils/external_project_parsers/parsers/owasp_top10_2025.py new file mode 100644 index 000000000..070f869af --- /dev/null +++ b/application/utils/external_project_parsers/parsers/owasp_top10_2025.py @@ -0,0 +1,47 @@ +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 OwaspTop10_2025(ParserInterface): + name = "OWASP Top 10 2025" + data_file = ( + Path(__file__).resolve().parent.parent / "data" / "owasp_top10_2025.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/cre.py b/cre.py index 5cbe86ae5..2b7e36ebd 100644 --- a/cre.py +++ b/cre.py @@ -168,6 +168,26 @@ def main() -> None: action="store_true", help="import owasp secure headers", ) + parser.add_argument( + "--owasp_top10_2025_in", + action="store_true", + help="import OWASP Top 10 2025", + ) + parser.add_argument( + "--owasp_api_top10_2023_in", + action="store_true", + help="import OWASP API Security Top 10 2023", + ) + parser.add_argument( + "--owasp_llm_top10_2025_in", + action="store_true", + help="import OWASP Top 10 for LLM and Gen AI Apps 2025", + ) + parser.add_argument( + "--owasp_aisvs_in", + action="store_true", + help="import OWASP AI Security Verification Standard (AISVS)", + ) parser.add_argument( "--pci_dss_3_2_in", action="store_true", From 939d4d58cb45b68059afe33372685ffded9138b8 Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 11 Aug 2026 00:33:26 +0530 Subject: [PATCH 2/8] refactor: update known_ids retrieval and remove obsolete OWASP JSON files --- application/cmd/cre_main.py | 8 +- .../data/owasp_aisvs_1_0.json | 86 ------------------- .../data/owasp_api_top10_2023.json | 62 ------------- .../data/owasp_llm_top10_2025.json | 62 ------------- .../data/owasp_top10_2025.json | 62 ------------- 5 files changed, 7 insertions(+), 273 deletions(-) delete mode 100644 application/utils/external_project_parsers/data/owasp_aisvs_1_0.json delete mode 100644 application/utils/external_project_parsers/data/owasp_api_top10_2023.json delete mode 100644 application/utils/external_project_parsers/data/owasp_llm_top10_2025.json delete mode 100644 application/utils/external_project_parsers/data/owasp_top10_2025.json diff --git a/application/cmd/cre_main.py b/application/cmd/cre_main.py index 391b3f85c..7bf25c808 100644 --- a/application/cmd/cre_main.py +++ b/application/cmd/cre_main.py @@ -1188,7 +1188,7 @@ def run_librarian( # resolver may auto-link to (W2 seeded this from the golden set; here it is # the real DB-backed registry). cre_embeddings = database.get_embeddings_by_doc_type(defs.Credoctypes.CRE.value) - known_ids = set(cre_embeddings.keys()) + known_ids = {cre.external_id for cre in database.get_CREs()} # in_memory loads the hub matrix; pgvector ranks in the DB over the # embedding_vec column (no in-RAM pool). Both honor the same retrieve(). pool = ( @@ -1234,6 +1234,12 @@ def run_librarian( explicit += 1 logger.info("[explicit] %s -> %s", section.chunk_id, resolution.cre_ids[0]) continue + elif resolution.outcome == ResolutionOutcome.no_reference: + # Continue to semantic retrieval below + pass + else: # unknown_reference or conflicting_references + logger.info("[review] %s -> %s", section.chunk_id, resolution.outcome) + continue try: audit = retriever.retrieve(section.text) diff --git a/application/utils/external_project_parsers/data/owasp_aisvs_1_0.json b/application/utils/external_project_parsers/data/owasp_aisvs_1_0.json deleted file mode 100644 index 9142ec347..000000000 --- a/application/utils/external_project_parsers/data/owasp_aisvs_1_0.json +++ /dev/null @@ -1,86 +0,0 @@ -[ - { - "section_id": "AISVS1", - "section": "Training Data Governance & Bias Management", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", - "cre_ids": ["227-045", "307-507"] - }, - { - "section_id": "AISVS2", - "section": "User Input Validation", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C02-Input-Validation.md", - "cre_ids": ["031-447", "760-764"] - }, - { - "section_id": "AISVS3", - "section": "Model Lifecycle Management & Change Control", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C03-Model-Lifecycle-Management.md", - "cre_ids": ["148-853", "613-285"] - }, - { - "section_id": "AISVS4", - "section": "Infrastructure, Configuration & Deployment Security", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C04-Infrastructure.md", - "cre_ids": ["233-748", "486-813"] - }, - { - "section_id": "AISVS5", - "section": "Access Control & Identity for AI Components & Users", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C05-Access-Control-and-Identity.md", - "cre_ids": ["633-428", "724-770"] - }, - { - "section_id": "AISVS6", - "section": "Supply Chain Security for Models, Frameworks & Data", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C06-Supply-Chain.md", - "cre_ids": ["613-285", "613-287", "863-521"] - }, - { - "section_id": "AISVS7", - "section": "Model Behavior, Output Control & Safety Assurance", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C07-Model-Behavior.md", - "cre_ids": ["064-808", "141-555"] - }, - { - "section_id": "AISVS8", - "section": "Memory, Embeddings & Vector Database Security", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C08-Memory-Embeddings-and-Vector-Database.md", - "cre_ids": ["126-668", "538-770"] - }, - { - "section_id": "AISVS9", - "section": "Autonomous Orchestration & Agentic Action Security", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C09-Orchestration-and-Agentic-Action.md", - "cre_ids": ["117-371", "650-560"] - }, - { - "section_id": "AISVS10", - "section": "Model Context Protocol (MCP) Security", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C10-MCP-Security.md", - "cre_ids": ["307-507", "715-223"] - }, - { - "section_id": "AISVS11", - "section": "Adversarial Robustness & Privacy Defense", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C11-Adversarial-Robustness.md", - "cre_ids": ["141-555", "623-550"] - }, - { - "section_id": "AISVS12", - "section": "Privacy Protection & Personal Data Management", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C12-Privacy.md", - "cre_ids": ["126-668", "227-045", "482-866"] - }, - { - "section_id": "AISVS13", - "section": "Monitoring, Logging & Anomaly Detection", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C13-Monitoring-and-Logging.md", - "cre_ids": ["058-083", "148-420", "402-706", "843-841"] - }, - { - "section_id": "AISVS14", - "section": "Human Oversight, Accountability & Governance", - "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C14-Human-Oversight.md", - "cre_ids": ["162-655", "766-162"] - } -] diff --git a/application/utils/external_project_parsers/data/owasp_api_top10_2023.json b/application/utils/external_project_parsers/data/owasp_api_top10_2023.json deleted file mode 100644 index 7a8df0ed0..000000000 --- a/application/utils/external_project_parsers/data/owasp_api_top10_2023.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "section_id": "API1", - "section": "Broken Object Level Authorization", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/", - "cre_ids": ["304-667", "724-770"] - }, - { - "section_id": "API2", - "section": "Broken Authentication", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/", - "cre_ids": ["177-260", "586-842", "633-428"] - }, - { - "section_id": "API3", - "section": "Broken Object Property Level Authorization", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa3-broken-object-property-level-authorization/", - "cre_ids": ["538-770", "724-770", "128-128"] - }, - { - "section_id": "API4", - "section": "Unrestricted Resource Consumption", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/", - "cre_ids": ["623-550"] - }, - { - "section_id": "API5", - "section": "Broken Function Level Authorization", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa5-broken-function-level-authorization/", - "cre_ids": ["650-560", "724-770"] - }, - { - "section_id": "API6", - "section": "Unrestricted Access to Sensitive Business Flows", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa6-unrestricted-access-to-sensitive-business-flows/", - "cre_ids": ["534-605", "630-573"] - }, - { - "section_id": "API7", - "section": "Server Side Request Forgery", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa7-server-side-request-forgery/", - "cre_ids": ["028-728", "657-084"] - }, - { - "section_id": "API8", - "section": "Security Misconfiguration", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/", - "cre_ids": ["486-813"] - }, - { - "section_id": "API9", - "section": "Improper Inventory Management", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xa9-improper-inventory-management/", - "cre_ids": ["162-655", "863-521"] - }, - { - "section_id": "API10", - "section": "Unsafe Consumption of APIs", - "hyperlink": "https://owasp.org/API-Security/editions/2023/en/0xaa-unsafe-consumption-of-apis/", - "cre_ids": ["715-223"] - } -] diff --git a/application/utils/external_project_parsers/data/owasp_llm_top10_2025.json b/application/utils/external_project_parsers/data/owasp_llm_top10_2025.json deleted file mode 100644 index b761d5e09..000000000 --- a/application/utils/external_project_parsers/data/owasp_llm_top10_2025.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "section_id": "LLM01", - "section": "Prompt Injection", - "hyperlink": "https://genai.owasp.org/llmrisk/llm01-prompt-injection/", - "cre_ids": ["161-451", "760-764"] - }, - { - "section_id": "LLM02", - "section": "Sensitive Information Disclosure", - "hyperlink": "https://genai.owasp.org/llmrisk/llm022025-sensitive-information-disclosure/", - "cre_ids": ["126-668", "227-045"] - }, - { - "section_id": "LLM03", - "section": "Supply Chain", - "hyperlink": "https://genai.owasp.org/llmrisk/llm032025-supply-chain/", - "cre_ids": ["613-285", "613-287"] - }, - { - "section_id": "LLM04", - "section": "Data and Model Poisoning", - "hyperlink": "https://genai.owasp.org/llmrisk/llm042025-data-and-model-poisoning/", - "cre_ids": ["307-507", "613-287"] - }, - { - "section_id": "LLM05", - "section": "Improper Output Handling", - "hyperlink": "https://genai.owasp.org/llmrisk/llm052025-improper-output-handling/", - "cre_ids": ["064-808"] - }, - { - "section_id": "LLM06", - "section": "Excessive Agency", - "hyperlink": "https://genai.owasp.org/llmrisk/llm062025-excessive-agency/", - "cre_ids": ["117-371", "650-560"] - }, - { - "section_id": "LLM07", - "section": "System Prompt Leakage", - "hyperlink": "https://genai.owasp.org/llmrisk/llm072025-system-prompt-leakage/", - "cre_ids": ["126-668", "227-045"] - }, - { - "section_id": "LLM08", - "section": "Vector and Embedding Weaknesses", - "hyperlink": "https://genai.owasp.org/llmrisk/llm082025-vector-and-embedding-weaknesses/", - "cre_ids": ["126-668", "538-770"] - }, - { - "section_id": "LLM09", - "section": "Misinformation", - "hyperlink": "https://genai.owasp.org/llmrisk/llm092025-misinformation/", - "cre_ids": ["141-555"] - }, - { - "section_id": "LLM10", - "section": "Unbounded Consumption", - "hyperlink": "https://genai.owasp.org/llmrisk/llm102025-unbounded-consumption/", - "cre_ids": ["267-031", "623-550"] - } -] diff --git a/application/utils/external_project_parsers/data/owasp_top10_2025.json b/application/utils/external_project_parsers/data/owasp_top10_2025.json deleted file mode 100644 index 7e19d1a4e..000000000 --- a/application/utils/external_project_parsers/data/owasp_top10_2025.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "section_id": "A01", - "section": "Broken Access Control", - "hyperlink": "https://owasp.org/Top10/2025/A01_2025-Broken_Access_Control/", - "cre_ids": ["117-371", "177-260", "724-770"] - }, - { - "section_id": "A02", - "section": "Security Misconfiguration", - "hyperlink": "https://owasp.org/Top10/2025/A02_2025-Security_Misconfiguration/", - "cre_ids": ["486-813"] - }, - { - "section_id": "A03", - "section": "Software Supply Chain Failures", - "hyperlink": "https://owasp.org/Top10/2025/A03_2025-Software_Supply_Chain_Failures/", - "cre_ids": ["613-286", "613-287", "715-223", "863-521"] - }, - { - "section_id": "A04", - "section": "Cryptographic Failures", - "hyperlink": "https://owasp.org/Top10/2025/A04_2025-Cryptographic_Failures/", - "cre_ids": ["170-772", "227-045"] - }, - { - "section_id": "A05", - "section": "Injection", - "hyperlink": "https://owasp.org/Top10/2025/A05_2025-Injection/", - "cre_ids": ["031-447", "064-808", "760-764"] - }, - { - "section_id": "A06", - "section": "Insecure Design", - "hyperlink": "https://owasp.org/Top10/2025/A06_2025-Insecure_Design/", - "cre_ids": ["126-668", "155-155"] - }, - { - "section_id": "A07", - "section": "Authentication Failures", - "hyperlink": "https://owasp.org/Top10/2025/A07_2025-Authentication_Failures/", - "cre_ids": ["002-630", "177-260", "586-842", "633-428"] - }, - { - "section_id": "A08", - "section": "Software or Data Integrity Failures", - "hyperlink": "https://owasp.org/Top10/2025/A08_2025-Software_or_Data_Integrity_Failures/", - "cre_ids": ["613-287", "836-068"] - }, - { - "section_id": "A09", - "section": "Security Logging and Alerting Failures", - "hyperlink": "https://owasp.org/Top10/2025/A09_2025-Security_Logging_and_Alerting_Failures/", - "cre_ids": ["067-050", "148-420", "402-706", "843-841"] - }, - { - "section_id": "A10", - "section": "Mishandling of Exceptional Conditions", - "hyperlink": "https://owasp.org/Top10/2025/A10_2025-Mishandling_of_Exceptional_Conditions/", - "cre_ids": ["513-183"] - } -] From 1dcae39382c8eb55ef10f10596547e59d2344937 Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 11 Aug 2026 00:58:08 +0530 Subject: [PATCH 3/8] fix(owasp): update data_file path to point to the correct test fixture location --- application/tests/owasp_aisvs_parser_test.py | 4 ++-- .../utils/external_project_parsers/parsers/owasp_aisvs.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/tests/owasp_aisvs_parser_test.py b/application/tests/owasp_aisvs_parser_test.py index edf547bcd..b1c024619 100644 --- a/application/tests/owasp_aisvs_parser_test.py +++ b/application/tests/owasp_aisvs_parser_test.py @@ -45,7 +45,7 @@ def test_parse(self) -> None: "Training Data Governance & Bias Management", entries[0].section ) self.assertEqual( - "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", + "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", entries[0].hyperlink, ) self.assertEqual( @@ -57,7 +57,7 @@ def test_parse(self) -> None: "Human Oversight, Accountability & Governance", entries[-1].section ) self.assertEqual( - "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C14-Human-Oversight.md", + "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C14-Human-Oversight.md", entries[-1].hyperlink, ) self.assertEqual( diff --git a/application/utils/external_project_parsers/parsers/owasp_aisvs.py b/application/utils/external_project_parsers/parsers/owasp_aisvs.py index cec4abad9..fad9a6d20 100644 --- a/application/utils/external_project_parsers/parsers/owasp_aisvs.py +++ b/application/utils/external_project_parsers/parsers/owasp_aisvs.py @@ -12,7 +12,7 @@ class OwaspAisvs(ParserInterface): name = "OWASP AI Security Verification Standard (AISVS)" - data_file = Path(__file__).resolve().parent.parent / "data" / "owasp_aisvs_1_0.json" + data_file = Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings"/ "owasp_aisvs_1_0.json" def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): with self.data_file.open("r", encoding="utf-8") as handle: From c331f47099d1eb6c062ffec353983f1ae8b8d24c Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 11 Aug 2026 01:07:54 +0530 Subject: [PATCH 4/8] fix: update file paths for OWASP AISVS and Top 10 parsers to point to the correct fixture locations --- .../owasp_mappings/owasp_aisvs_1_0.json | 40 +++++++------------ application/tests/owasp_aisvs_parser_test.py | 20 +++++----- .../parsers/owasp_api_top10_2023.py | 2 +- .../parsers/owasp_llm_top10_2025.py | 2 +- .../parsers/owasp_top10_2025.py | 2 +- 5 files changed, 26 insertions(+), 40 deletions(-) diff --git a/application/tests/fixtures/owasp_mappings/owasp_aisvs_1_0.json b/application/tests/fixtures/owasp_mappings/owasp_aisvs_1_0.json index c4880546f..c06c4d5ad 100644 --- a/application/tests/fixtures/owasp_mappings/owasp_aisvs_1_0.json +++ b/application/tests/fixtures/owasp_mappings/owasp_aisvs_1_0.json @@ -1,86 +1,74 @@ [ { "section_id": "AISVS1", - "section": "Training Data Governance & Bias Management", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C01-Training-Data-Governance.md", + "section": "Training Data Integrity & Traceability", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", "cre_ids": ["227-045", "307-507"] }, { "section_id": "AISVS2", - "section": "User Input Validation", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C02-User-Input-Validation.md", + "section": "Input Validation", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C02-Input-Validation.md", "cre_ids": ["031-447", "760-764"] }, { "section_id": "AISVS3", "section": "Model Lifecycle Management & Change Control", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C03-Model-Lifecycle-Management.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C03-Model-Lifecycle-Management.md", "cre_ids": ["148-853", "613-285"] }, { "section_id": "AISVS4", "section": "Infrastructure, Configuration & Deployment Security", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C04-Infrastructure.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C04-Infrastructure.md", "cre_ids": ["233-748", "486-813"] }, { "section_id": "AISVS5", "section": "Access Control & Identity for AI Components & Users", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C05-Access-Control-and-Identity.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C05-Access-Control-and-Identity.md", "cre_ids": ["633-428", "724-770"] }, { "section_id": "AISVS6", "section": "Supply Chain Security for Models, Frameworks & Data", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C06-Supply-Chain.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C06-Supply-Chain.md", "cre_ids": ["613-285", "613-287", "863-521"] }, { "section_id": "AISVS7", "section": "Model Behavior, Output Control & Safety Assurance", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C07-Model-Behavior.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C07-Model-Behavior.md", "cre_ids": ["064-808", "141-555"] }, { "section_id": "AISVS8", "section": "Memory, Embeddings & Vector Database Security", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C08-Memory-Embeddings-and-Vector-Database.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C08-Memory-Embeddings-and-Vector-Database.md", "cre_ids": ["126-668", "538-770"] }, { "section_id": "AISVS9", "section": "Autonomous Orchestration & Agentic Action Security", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C09-Orchestration-and-Agentic-Action.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C09-Orchestration-and-Agentic-Action.md", "cre_ids": ["117-371", "650-560"] }, { "section_id": "AISVS10", "section": "Model Context Protocol (MCP) Security", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C10-MCP-Security.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C10-MCP-Security.md", "cre_ids": ["307-507", "715-223"] }, { "section_id": "AISVS11", "section": "Adversarial Robustness & Privacy Defense", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C11-Adversarial-Robustness.md", + "hyperlink": "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C11-Adversarial-Robustness.md", "cre_ids": ["141-555", "623-550"] }, { "section_id": "AISVS12", - "section": "Privacy Protection & Personal Data Management", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C12-Privacy.md", - "cre_ids": ["126-668", "227-045", "482-866"] - }, - { - "section_id": "AISVS13", "section": "Monitoring, Logging & Anomaly Detection", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C13-Monitoring-and-Logging.md", + "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C12-Monitoring-and-Logging.md", "cre_ids": ["058-083", "148-420", "402-706", "843-841"] - }, - { - "section_id": "AISVS14", - "section": "Human Oversight, Accountability & Governance", - "hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C14-Human-Oversight.md", - "cre_ids": ["162-655", "766-162"] } ] diff --git a/application/tests/owasp_aisvs_parser_test.py b/application/tests/owasp_aisvs_parser_test.py index b1c024619..c0afcc934 100644 --- a/application/tests/owasp_aisvs_parser_test.py +++ b/application/tests/owasp_aisvs_parser_test.py @@ -28,8 +28,8 @@ def test_parse(self) -> None: "Allow only trusted sources both build time and runtime; therefore perform integrity checks on all resources and code", ), ( - "162-655", - "Documentation of all components' business or security function", + "058-083", + "Generate and retain audit logs for security events", ), ]: self.collection.add_cre(defs.CRE(id=cre_id, name=name, description="")) @@ -39,28 +39,26 @@ def test_parse(self) -> None: ) entries = result.results["OWASP AI Security Verification Standard (AISVS)"] - self.assertEqual(14, len(entries)) + self.assertEqual(12, len(entries)) self.assertEqual("AISVS1", entries[0].sectionID) + self.assertEqual("Training Data Integrity & Traceability", entries[0].section) self.assertEqual( - "Training Data Governance & Bias Management", entries[0].section - ) - self.assertEqual( - "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", + "https://github.com/OWASP/AISVS/blob/main/1.0/en/0x10-C01-Training-Data-Integrity-and-Traceability.md", entries[0].hyperlink, ) self.assertEqual( ["227-045", "307-507"], [link.document.id for link in entries[0].links], ) - self.assertEqual("AISVS14", entries[-1].sectionID) + self.assertEqual("AISVS12", entries[-1].sectionID) self.assertEqual( - "Human Oversight, Accountability & Governance", entries[-1].section + "Monitoring, Logging & Anomaly Detection", entries[-1].section ) self.assertEqual( - "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C14-Human-Oversight.md", + "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C12-Monitoring-and-Logging.md", entries[-1].hyperlink, ) self.assertEqual( - ["162-655"], + ["058-083"], [link.document.id for link in entries[-1].links], ) diff --git a/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py b/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py index 08157a1e9..f3bdefeb2 100644 --- a/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py +++ b/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py @@ -13,7 +13,7 @@ class OwaspApiTop10_2023(ParserInterface): name = "OWASP API Security Top 10 2023" data_file = ( - Path(__file__).resolve().parent.parent / "data" / "owasp_api_top10_2023.json" + Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings" / "owasp_api_top10_2023.json" ) def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): diff --git a/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py b/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py index 3971b9e6b..bcfce694b 100644 --- a/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py +++ b/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py @@ -13,7 +13,7 @@ class OwaspLlmTop10_2025(ParserInterface): name = "OWASP Top 10 for LLM and Gen AI Apps 2025" data_file = ( - Path(__file__).resolve().parent.parent / "data" / "owasp_llm_top10_2025.json" + Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings" / "owasp_llm_top10_2025.json" ) def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): diff --git a/application/utils/external_project_parsers/parsers/owasp_top10_2025.py b/application/utils/external_project_parsers/parsers/owasp_top10_2025.py index 070f869af..86dbad8af 100644 --- a/application/utils/external_project_parsers/parsers/owasp_top10_2025.py +++ b/application/utils/external_project_parsers/parsers/owasp_top10_2025.py @@ -13,7 +13,7 @@ class OwaspTop10_2025(ParserInterface): name = "OWASP Top 10 2025" data_file = ( - Path(__file__).resolve().parent.parent / "data" / "owasp_top10_2025.json" + Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings" / "owasp_top10_2025.json" ) def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): From fdeebdbfb9d5b3b40afcaf3a4afe61e3b19bbc73 Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 11 Aug 2026 01:09:56 +0530 Subject: [PATCH 5/8] fix: format data_file paths for OWASP parsers for improved readability --- application/tests/owasp_aisvs_parser_test.py | 4 +--- .../utils/external_project_parsers/parsers/owasp_aisvs.py | 8 +++++++- .../parsers/owasp_api_top10_2023.py | 6 +++++- .../parsers/owasp_llm_top10_2025.py | 6 +++++- .../external_project_parsers/parsers/owasp_top10_2025.py | 6 +++++- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/application/tests/owasp_aisvs_parser_test.py b/application/tests/owasp_aisvs_parser_test.py index c0afcc934..3a83299af 100644 --- a/application/tests/owasp_aisvs_parser_test.py +++ b/application/tests/owasp_aisvs_parser_test.py @@ -51,9 +51,7 @@ def test_parse(self) -> None: [link.document.id for link in entries[0].links], ) self.assertEqual("AISVS12", entries[-1].sectionID) - self.assertEqual( - "Monitoring, Logging & Anomaly Detection", entries[-1].section - ) + self.assertEqual("Monitoring, Logging & Anomaly Detection", entries[-1].section) self.assertEqual( "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C12-Monitoring-and-Logging.md", entries[-1].hyperlink, diff --git a/application/utils/external_project_parsers/parsers/owasp_aisvs.py b/application/utils/external_project_parsers/parsers/owasp_aisvs.py index fad9a6d20..d32595666 100644 --- a/application/utils/external_project_parsers/parsers/owasp_aisvs.py +++ b/application/utils/external_project_parsers/parsers/owasp_aisvs.py @@ -12,7 +12,13 @@ class OwaspAisvs(ParserInterface): name = "OWASP AI Security Verification Standard (AISVS)" - data_file = Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings"/ "owasp_aisvs_1_0.json" + data_file = ( + Path(__file__).resolve().parents[3] + / "tests" + / "fixtures" + / "owasp_mappings" + / "owasp_aisvs_1_0.json" + ) def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): with self.data_file.open("r", encoding="utf-8") as handle: diff --git a/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py b/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py index f3bdefeb2..18c8310b2 100644 --- a/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py +++ b/application/utils/external_project_parsers/parsers/owasp_api_top10_2023.py @@ -13,7 +13,11 @@ class OwaspApiTop10_2023(ParserInterface): name = "OWASP API Security Top 10 2023" data_file = ( - Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings" / "owasp_api_top10_2023.json" + Path(__file__).resolve().parents[3] + / "tests" + / "fixtures" + / "owasp_mappings" + / "owasp_api_top10_2023.json" ) def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): diff --git a/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py b/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py index bcfce694b..233596cd6 100644 --- a/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py +++ b/application/utils/external_project_parsers/parsers/owasp_llm_top10_2025.py @@ -13,7 +13,11 @@ class OwaspLlmTop10_2025(ParserInterface): name = "OWASP Top 10 for LLM and Gen AI Apps 2025" data_file = ( - Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings" / "owasp_llm_top10_2025.json" + Path(__file__).resolve().parents[3] + / "tests" + / "fixtures" + / "owasp_mappings" + / "owasp_llm_top10_2025.json" ) def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): diff --git a/application/utils/external_project_parsers/parsers/owasp_top10_2025.py b/application/utils/external_project_parsers/parsers/owasp_top10_2025.py index 86dbad8af..51b59262f 100644 --- a/application/utils/external_project_parsers/parsers/owasp_top10_2025.py +++ b/application/utils/external_project_parsers/parsers/owasp_top10_2025.py @@ -13,7 +13,11 @@ class OwaspTop10_2025(ParserInterface): name = "OWASP Top 10 2025" data_file = ( - Path(__file__).resolve().parents[3] / "tests" / "fixtures" / "owasp_mappings" / "owasp_top10_2025.json" + Path(__file__).resolve().parents[3] + / "tests" + / "fixtures" + / "owasp_mappings" + / "owasp_top10_2025.json" ) def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler): From 4bc17b7123a0b6c2463ada4834a9fbfb7c40fed1 Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 11 Aug 2026 01:14:24 +0530 Subject: [PATCH 6/8] fix: handle unexpected resolution outcomes in run_librarian function --- application/cmd/cre_main.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/application/cmd/cre_main.py b/application/cmd/cre_main.py index 7bf25c808..d5e92ab48 100644 --- a/application/cmd/cre_main.py +++ b/application/cmd/cre_main.py @@ -1234,12 +1234,23 @@ def run_librarian( explicit += 1 logger.info("[explicit] %s -> %s", section.chunk_id, resolution.cre_ids[0]) continue - elif resolution.outcome == ResolutionOutcome.no_reference: + if resolution.outcome == ResolutionOutcome.no_reference: # Continue to semantic retrieval below pass - else: # unknown_reference or conflicting_references + elif resolution.outcome in ( + ResolutionOutcome.unknown_reference, + ResolutionOutcome.conflicting_references, + ): logger.info("[review] %s -> %s", section.chunk_id, resolution.outcome) continue + else: + rejected += 1 + logger.warning( + "[review] %s skipped: unexpected resolution outcome %s", + section.chunk_id, + resolution.outcome, + ) + continue try: audit = retriever.retrieve(section.text) From 9c1d3354a14eee85cc7ca5ec50cae97fa5a13f2a Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 11 Aug 2026 01:19:01 +0530 Subject: [PATCH 7/8] feat: implement error status code extraction for provider errors and update response handling --- application/tests/chat_completion_test.py | 4 ++-- application/tests/web_main_test.py | 1 - application/web/web_main.py | 26 ++++++++++++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/application/tests/chat_completion_test.py b/application/tests/chat_completion_test.py index 4eb6e6725..f601d7df5 100644 --- a/application/tests/chat_completion_test.py +++ b/application/tests/chat_completion_test.py @@ -50,7 +50,7 @@ def test_completion_returns_503_json_on_gemini_429(self) -> None: self.assertIn("error", data) self.assertIn("rate-limited", data["error"]) - def test_completion_returns_500_on_non_429_genai_error(self) -> None: + def test_completion_returns_provider_status_on_non_429_genai_error(self) -> None: os.environ["NO_LOGIN"] = "1" err = genai_errors.ClientError( 400, @@ -71,7 +71,7 @@ def test_completion_returns_500_on_non_429_genai_error(self) -> None: json={"prompt": "test"}, content_type="application/json", ) - self.assertEqual(500, response.status_code) + self.assertEqual(400, response.status_code) data = json.loads(response.data) self.assertIn("error", data) self.assertIn("AI Service Error", data["error"]) diff --git a/application/tests/web_main_test.py b/application/tests/web_main_test.py index 1bff0f94e..092e727b0 100644 --- a/application/tests/web_main_test.py +++ b/application/tests/web_main_test.py @@ -1561,7 +1561,6 @@ def test_import_from_cre_csv(self) -> None: buffered=True, content_type="multipart/form-data", ) - print(f"\nSTATUS CODE: {response.status_code}, DATA: {response.data}") self.assertEqual(200, response.status_code) data = json.loads(response.data) self.assertEqual("success", data.get("status")) diff --git a/application/web/web_main.py b/application/web/web_main.py index d338ed23f..3bdab8656 100644 --- a/application/web/web_main.py +++ b/application/web/web_main.py @@ -53,6 +53,29 @@ MAX_ITEMS_PER_PAGE = 100 OPENCRE_STANDARD_NAME = gap_analysis.OPENCRE_STANDARD_NAME +def _llm_error_status_code(err: BaseException) -> int | None: + """Best-effort extraction of an HTTP-like status code from provider errors.""" + for attr in ("status", "status_code", "http_status", "code"): + value = getattr(err, attr, None) + if isinstance(value, int) and 400 <= value <= 599: + return value + + if isinstance(getattr(err, "args", None), tuple) and err.args: + nested = err.args[0] + if isinstance(nested, dict): + for key in ("code", "status_code"): + value = nested.get(key) + if isinstance(value, int) and 400 <= value <= 599: + return value + nested_error = nested.get("error") + if isinstance(nested_error, dict): + for key in ("code", "status_code"): + value = nested_error.get(key) + if isinstance(value, int) and 400 <= value <= 599: + return value + return None + + app = Blueprint( "web", __name__, @@ -1196,9 +1219,10 @@ def chat_cre() -> Any: ), 503, ) + status_code = _llm_error_status_code(e) or 500 return ( jsonify({"error": f"AI Service Error: {str(e)}"}), - 500, + status_code, ) return jsonify(response) From e42fe127d8bc83f234da9cbc5275c4092d2223a0 Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Tue, 11 Aug 2026 01:34:00 +0530 Subject: [PATCH 8/8] fix: add a blank line for improved code readability before the error status code function --- application/web/web_main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/application/web/web_main.py b/application/web/web_main.py index 3bdab8656..ec6924de5 100644 --- a/application/web/web_main.py +++ b/application/web/web_main.py @@ -53,6 +53,7 @@ MAX_ITEMS_PER_PAGE = 100 OPENCRE_STANDARD_NAME = gap_analysis.OPENCRE_STANDARD_NAME + def _llm_error_status_code(err: BaseException) -> int | None: """Best-effort extraction of an HTTP-like status code from provider errors.""" for attr in ("status", "status_code", "http_status", "code"):