Skip to content

Commit fab3098

Browse files
committed
Add OWASP AI resource importer support
1 parent 508dcc0 commit fab3098

6 files changed

Lines changed: 347 additions & 0 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import unittest
2+
3+
from application import create_app, sqla # type: ignore
4+
from application.database import db
5+
from application.defs import cre_defs as defs
6+
from application.prompt_client import prompt_client
7+
from application.utils.external_project_parsers.parsers import owasp_aisvs
8+
9+
10+
class TestOwaspAisvsParser(unittest.TestCase):
11+
def tearDown(self) -> None:
12+
sqla.session.remove()
13+
sqla.drop_all()
14+
self.app_context.pop()
15+
16+
def setUp(self) -> None:
17+
self.app = create_app(mode="test")
18+
self.app_context = self.app.app_context()
19+
self.app_context.push()
20+
sqla.create_all()
21+
self.collection = db.Node_collection()
22+
23+
def test_parse(self) -> None:
24+
for cre_id, name in [
25+
("227-045", "Identify sensitive data and subject it to a policy"),
26+
(
27+
"307-507",
28+
"Allow only trusted sources both build time and runtime; therefore perform integrity checks on all resources and code",
29+
),
30+
(
31+
"162-655",
32+
"Documentation of all components' business or security function",
33+
),
34+
]:
35+
self.collection.add_cre(defs.CRE(id=cre_id, name=name, description=""))
36+
37+
result = owasp_aisvs.OwaspAisvs().parse(
38+
self.collection, prompt_client.PromptHandler(database=self.collection)
39+
)
40+
41+
entries = result.results["OWASP AI Security Verification Standard (AISVS)"]
42+
self.assertEqual(14, len(entries))
43+
self.assertEqual("AISVS1", entries[0].sectionID)
44+
self.assertEqual(
45+
"Training Data Governance & Bias Management", entries[0].section
46+
)
47+
self.assertEqual(
48+
"https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C01-Training-Data-Governance.md",
49+
entries[0].hyperlink,
50+
)
51+
self.assertEqual(
52+
["227-045", "307-507"], [l.document.id for l in entries[0].links]
53+
)
54+
self.assertEqual("AISVS14", entries[-1].sectionID)
55+
self.assertEqual(
56+
"Human Oversight, Accountability & Governance", entries[-1].section
57+
)
58+
self.assertEqual(
59+
"https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C14-Human-Oversight.md",
60+
entries[-1].hyperlink,
61+
)
62+
self.assertEqual(["162-655"], [l.document.id for l in entries[-1].links])
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import unittest
2+
3+
from application import create_app, sqla # type: ignore
4+
from application.database import db
5+
from application.defs import cre_defs as defs
6+
from application.prompt_client import prompt_client
7+
from application.utils.external_project_parsers.parsers import owasp_llm_top10_2025
8+
9+
10+
class TestOwaspLlmTop10_2025Parser(unittest.TestCase):
11+
def tearDown(self) -> None:
12+
sqla.session.remove()
13+
sqla.drop_all()
14+
self.app_context.pop()
15+
16+
def setUp(self) -> None:
17+
self.app = create_app(mode="test")
18+
self.app_context = self.app.app_context()
19+
self.app_context.push()
20+
sqla.create_all()
21+
self.collection = db.Node_collection()
22+
23+
def test_parse(self) -> None:
24+
for cre_id, name in [
25+
("161-451", "Output encoding and injection prevention"),
26+
("064-808", "Encode output context-specifically"),
27+
("760-764", "Injection protection"),
28+
("623-550", "Denial Of Service protection"),
29+
]:
30+
self.collection.add_cre(defs.CRE(id=cre_id, name=name, description=""))
31+
32+
result = owasp_llm_top10_2025.OwaspLlmTop10_2025().parse(
33+
self.collection, prompt_client.PromptHandler(database=self.collection)
34+
)
35+
36+
entries = result.results["OWASP Top 10 for LLM and Gen AI Apps 2025"]
37+
self.assertEqual(10, len(entries))
38+
self.assertEqual("LLM01", entries[0].sectionID)
39+
self.assertEqual("Prompt Injection", entries[0].section)
40+
self.assertEqual(
41+
["161-451", "760-764"], [l.document.id for l in entries[0].links]
42+
)
43+
self.assertEqual(["064-808"], [l.document.id for l in entries[4].links])
44+
self.assertEqual("LLM10", entries[-1].sectionID)
45+
self.assertEqual(["623-550"], [l.document.id for l in entries[-1].links])
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
[
2+
{
3+
"section_id": "AISVS1",
4+
"section": "Training Data Governance & Bias Management",
5+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C01-Training-Data-Governance.md",
6+
"cre_ids": ["227-045", "307-507"]
7+
},
8+
{
9+
"section_id": "AISVS2",
10+
"section": "User Input Validation",
11+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C02-User-Input-Validation.md",
12+
"cre_ids": ["031-447", "760-764"]
13+
},
14+
{
15+
"section_id": "AISVS3",
16+
"section": "Model Lifecycle Management & Change Control",
17+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C03-Model-Lifecycle-Management.md",
18+
"cre_ids": ["148-853", "613-285"]
19+
},
20+
{
21+
"section_id": "AISVS4",
22+
"section": "Infrastructure, Configuration & Deployment Security",
23+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C04-Infrastructure.md",
24+
"cre_ids": ["233-748", "486-813"]
25+
},
26+
{
27+
"section_id": "AISVS5",
28+
"section": "Access Control & Identity for AI Components & Users",
29+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C05-Access-Control-and-Identity.md",
30+
"cre_ids": ["633-428", "724-770"]
31+
},
32+
{
33+
"section_id": "AISVS6",
34+
"section": "Supply Chain Security for Models, Frameworks & Data",
35+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C06-Supply-Chain.md",
36+
"cre_ids": ["613-285", "613-287", "863-521"]
37+
},
38+
{
39+
"section_id": "AISVS7",
40+
"section": "Model Behavior, Output Control & Safety Assurance",
41+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C07-Model-Behavior.md",
42+
"cre_ids": ["064-808", "141-555"]
43+
},
44+
{
45+
"section_id": "AISVS8",
46+
"section": "Memory, Embeddings & Vector Database Security",
47+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C08-Memory-Embeddings-and-Vector-Database.md",
48+
"cre_ids": ["126-668", "538-770"]
49+
},
50+
{
51+
"section_id": "AISVS9",
52+
"section": "Autonomous Orchestration & Agentic Action Security",
53+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C09-Orchestration-and-Agentic-Action.md",
54+
"cre_ids": ["117-371", "650-560"]
55+
},
56+
{
57+
"section_id": "AISVS10",
58+
"section": "Model Context Protocol (MCP) Security",
59+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C10-MCP-Security.md",
60+
"cre_ids": ["307-507", "715-223"]
61+
},
62+
{
63+
"section_id": "AISVS11",
64+
"section": "Adversarial Robustness & Privacy Defense",
65+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C11-Adversarial-Robustness.md",
66+
"cre_ids": ["141-555", "623-550"]
67+
},
68+
{
69+
"section_id": "AISVS12",
70+
"section": "Privacy Protection & Personal Data Management",
71+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C12-Privacy.md",
72+
"cre_ids": ["126-668", "227-045", "482-866"]
73+
},
74+
{
75+
"section_id": "AISVS13",
76+
"section": "Monitoring, Logging & Anomaly Detection",
77+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C13-Monitoring-and-Logging.md",
78+
"cre_ids": ["058-083", "148-420", "402-706", "843-841"]
79+
},
80+
{
81+
"section_id": "AISVS14",
82+
"section": "Human Oversight, Accountability & Governance",
83+
"hyperlink": "https://github.com/OWASP/AISVS/tree/main/1.0/en/0x10-C14-Human-Oversight.md",
84+
"cre_ids": ["162-655", "766-162"]
85+
}
86+
]
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
[
2+
{
3+
"section_id": "LLM01",
4+
"section": "Prompt Injection",
5+
"hyperlink": "https://genai.owasp.org/llmrisk/llm01-prompt-injection/",
6+
"cre_ids": ["161-451", "760-764"]
7+
},
8+
{
9+
"section_id": "LLM02",
10+
"section": "Sensitive Information Disclosure",
11+
"hyperlink": "https://genai.owasp.org/llmrisk/llm022025-sensitive-information-disclosure/",
12+
"cre_ids": ["126-668", "227-045"]
13+
},
14+
{
15+
"section_id": "LLM03",
16+
"section": "Supply Chain",
17+
"hyperlink": "https://genai.owasp.org/llmrisk/llm032025-supply-chain/",
18+
"cre_ids": ["613-285", "613-287"]
19+
},
20+
{
21+
"section_id": "LLM04",
22+
"section": "Data and Model Poisoning",
23+
"hyperlink": "https://genai.owasp.org/llmrisk/llm042025-data-and-model-poisoning/",
24+
"cre_ids": ["307-507", "613-287"]
25+
},
26+
{
27+
"section_id": "LLM05",
28+
"section": "Improper Output Handling",
29+
"hyperlink": "https://genai.owasp.org/llmrisk/llm052025-improper-output-handling/",
30+
"cre_ids": ["064-808"]
31+
},
32+
{
33+
"section_id": "LLM06",
34+
"section": "Excessive Agency",
35+
"hyperlink": "https://genai.owasp.org/llmrisk/llm062025-excessive-agency/",
36+
"cre_ids": ["117-371", "650-560"]
37+
},
38+
{
39+
"section_id": "LLM07",
40+
"section": "System Prompt Leakage",
41+
"hyperlink": "https://genai.owasp.org/llmrisk/llm072025-system-prompt-leakage/",
42+
"cre_ids": ["126-668", "227-045"]
43+
},
44+
{
45+
"section_id": "LLM08",
46+
"section": "Vector and Embedding Weaknesses",
47+
"hyperlink": "https://genai.owasp.org/llmrisk/llm082025-vector-and-embedding-weaknesses/",
48+
"cre_ids": ["126-668", "538-770"]
49+
},
50+
{
51+
"section_id": "LLM09",
52+
"section": "Misinformation",
53+
"hyperlink": "https://genai.owasp.org/llmrisk/llm092025-misinformation/",
54+
"cre_ids": ["141-555"]
55+
},
56+
{
57+
"section_id": "LLM10",
58+
"section": "Unbounded Consumption",
59+
"hyperlink": "https://genai.owasp.org/llmrisk/llm102025-unbounded-consumption/",
60+
"cre_ids": ["267-031", "623-550"]
61+
}
62+
]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import json
2+
from pathlib import Path
3+
4+
from application.database import db
5+
from application.defs import cre_defs as defs
6+
from application.prompt_client import prompt_client
7+
from application.utils.external_project_parsers.base_parser_defs import (
8+
ParseResult,
9+
ParserInterface,
10+
)
11+
12+
13+
class OwaspAisvs(ParserInterface):
14+
name = "OWASP AI Security Verification Standard (AISVS)"
15+
data_file = Path(__file__).resolve().parent.parent / "data" / "owasp_aisvs_1_0.json"
16+
17+
def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler):
18+
with self.data_file.open("r", encoding="utf-8") as handle:
19+
raw_entries = json.load(handle)
20+
21+
entries = []
22+
for entry in raw_entries:
23+
standard = defs.Standard(
24+
name=self.name,
25+
sectionID=entry["section_id"],
26+
section=entry["section"],
27+
hyperlink=entry["hyperlink"],
28+
)
29+
for cre_id in entry.get("cre_ids", []):
30+
cres = cache.get_CREs(external_id=cre_id)
31+
if not cres:
32+
continue
33+
standard.add_link(
34+
defs.Link(
35+
ltype=defs.LinkTypes.LinkedTo,
36+
document=cres[0].shallow_copy(),
37+
)
38+
)
39+
entries.append(standard)
40+
41+
return ParseResult(
42+
results={self.name: entries},
43+
calculate_gap_analysis=False,
44+
calculate_embeddings=False,
45+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import json
2+
from pathlib import Path
3+
4+
from application.database import db
5+
from application.defs import cre_defs as defs
6+
from application.prompt_client import prompt_client
7+
from application.utils.external_project_parsers.base_parser_defs import (
8+
ParseResult,
9+
ParserInterface,
10+
)
11+
12+
13+
class OwaspLlmTop10_2025(ParserInterface):
14+
name = "OWASP Top 10 for LLM and Gen AI Apps 2025"
15+
data_file = (
16+
Path(__file__).resolve().parent.parent / "data" / "owasp_llm_top10_2025.json"
17+
)
18+
19+
def parse(self, cache: db.Node_collection, ph: prompt_client.PromptHandler):
20+
with self.data_file.open("r", encoding="utf-8") as handle:
21+
raw_entries = json.load(handle)
22+
23+
entries = []
24+
for entry in raw_entries:
25+
standard = defs.Standard(
26+
name=self.name,
27+
sectionID=entry["section_id"],
28+
section=entry["section"],
29+
hyperlink=entry["hyperlink"],
30+
)
31+
for cre_id in entry.get("cre_ids", []):
32+
cres = cache.get_CREs(external_id=cre_id)
33+
if not cres:
34+
continue
35+
standard.add_link(
36+
defs.Link(
37+
ltype=defs.LinkTypes.LinkedTo,
38+
document=cres[0].shallow_copy(),
39+
)
40+
)
41+
entries.append(standard)
42+
43+
return ParseResult(
44+
results={self.name: entries},
45+
calculate_gap_analysis=False,
46+
calculate_embeddings=False,
47+
)

0 commit comments

Comments
 (0)