Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
0cf8f32
feat: add standalone OWASP Kubernetes importers
Bornunique911 Jul 1, 2026
5e5fd23
refactor: update data file paths for OWASP Kubernetes Top Ten parsers…
Bornunique911 Aug 7, 2026
0affe86
fix: update OWASP Kubernetes Top Ten parser to 2025 version
Bornunique911 Aug 7, 2026
78ca542
feat: update OWASP Kubernetes importers to include 2022 version and s…
Bornunique911 Aug 7, 2026
fb9ced2
fix: add missing newline at end of file in cre_main.py
Bornunique911 Aug 7, 2026
69a57bd
Merge remote-tracking branch 'upstream/main' into review/issue-471-ku…
Bornunique911 Aug 20, 2026
1bbad66
refactor: simplify Kubernetes parser registration in run function
Bornunique911 Aug 20, 2026
b9997bf
Merge remote-tracking branch 'upstream/main' into review/issue-471-ku…
Bornunique911 Aug 22, 2026
c3476dc
test: strengthen OWASP mapping fixture validation
Bornunique911 Aug 22, 2026
97d41d4
refactor: streamline formatting of expected link prefixes in OWASP ma…
Bornunique911 Aug 22, 2026
b1ea34f
feat: add additional mappings for Kubernetes top 10 2025 in fixture v…
Bornunique911 Aug 22, 2026
4803118
refactor: remove deprecated OWASP Kubernetes Top Ten 2022 and 2025 im…
Bornunique911 Aug 22, 2026
73ecd51
feat: update OWASP Kubernetes Top Ten 2022 and 2025 data files and ad…
Bornunique911 Aug 22, 2026
f0c5b7a
feat: add OWASP Kubernetes Top Ten 2022 and 2025 mappings for fixture…
Bornunique911 Aug 22, 2026
dd7b0d9
fix: add missing newline at end of file in OWASP Kubernetes Top Ten 2…
Bornunique911 Aug 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,68 @@
{
"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"]
}
Expand Down
45 changes: 45 additions & 0 deletions application/tests/owasp_kubernetes_top10_2022_parser_test.py
Original file line number Diff line number Diff line change
@@ -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_kubernetes_top10_2022,
)


class TestOwaspKubernetesTop10_2022Parser(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 [
("233-748", "Configuration hardening"),
("486-813", "Configuration"),
("053-751", "Force build pipeline to check outdated/insecure components"),
]:
self.collection.add_cre(defs.CRE(id=cre_id, name=name, description=""))

result = owasp_kubernetes_top10_2022.OwaspKubernetesTop10_2022().parse(
self.collection, prompt_client.PromptHandler(database=self.collection)
)

entries = result.results["OWASP Kubernetes Top Ten 2022"]
self.assertEqual(10, len(entries))
self.assertEqual("K01", entries[0].sectionID)
self.assertEqual("Insecure Workload Configurations", entries[0].section)
self.assertEqual(
["233-748", "486-813"], [link.document.id for link in entries[0].links]
)
self.assertEqual("K10", entries[-1].sectionID)
self.assertEqual(["053-751"], [link.document.id for link in entries[-1].links])
102 changes: 102 additions & 0 deletions application/tests/owasp_kubernetes_top10_2025_parser_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import unittest
import tempfile
from pathlib import Path

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_kubernetes_top10_2025,
)


class TestOwaspKubernetesTop10_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 [
("233-748", "Configuration hardening"),
("486-813", "Configuration"),
("148-420", "Log integrity"),
("402-706", "Log relevant"),
("843-841", "Log discretely"),
]:
self.collection.add_cre(defs.CRE(id=cre_id, name=name, description=""))

result = owasp_kubernetes_top10_2025.OwaspKubernetesTop10_2025().parse(
self.collection, prompt_client.PromptHandler(database=self.collection)
)

entries = result.results["OWASP Kubernetes Top Ten 2025 (Draft)"]
self.assertEqual(10, len(entries))
self.assertEqual("K01", entries[0].sectionID)
self.assertEqual("Insecure Workload Configurations", entries[0].section)
self.assertEqual(
["233-748", "486-813"], [link.document.id for link in entries[0].links]
)
self.assertEqual("K10", entries[-1].sectionID)
self.assertEqual(
["148-420", "402-706", "843-841"],
[link.document.id for link in entries[-1].links],
)

def test_parse_falls_back_to_2022_mapping_when_2025_links_missing(self) -> None:
self.collection.add_cre(
defs.CRE(id="148-420", name="Log integrity", description="")
)

with tempfile.TemporaryDirectory() as tmpdir:
tmp_path = Path(tmpdir)
current_file = tmp_path / "k8s_2025.json"
fallback_file = tmp_path / "k8s_2022.json"
current_file.write_text(
"""
[
{
"section_id": "K10",
"section": "Inadequate Logging And Monitoring",
"hyperlink": "https://example.com/k10",
"cre_ids": ["999-999"],
"fallback_section_ids": ["K05"]
}
]
""".strip(),
encoding="utf-8",
)
fallback_file.write_text(
"""
[
{
"section_id": "K05",
"section": "Inadequate Logging and Monitoring",
"hyperlink": "https://example.com/k05",
"cre_ids": ["148-420"]
}
]
""".strip(),
encoding="utf-8",
)

parser = owasp_kubernetes_top10_2025.OwaspKubernetesTop10_2025()
parser.data_file = current_file
parser.fallback_data_file = fallback_file

result = parser.parse(
self.collection,
prompt_client.PromptHandler(database=self.collection),
)

entries = result.results["OWASP Kubernetes Top Ten 2025 (Draft)"]
self.assertEqual(1, len(entries))
self.assertEqual(["148-420"], [link.document.id for link in entries[0].links])
125 changes: 122 additions & 3 deletions application/tests/owasp_mapping_fixtures_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,87 @@
"owasp_top10_2025.json",
}
CRE_ID_PATTERN = re.compile(r"^\d{3}-\d{3}$")
EXPECTED_SECTION_COUNTS = {
"owasp_aisvs_1_0.json": 14,
"owasp_api_top10_2023.json": 10,
"owasp_cheatsheets_supplement.json": 9,
"owasp_kubernetes_top10_2022.json": 10,
"owasp_kubernetes_top10_2025.json": 10,
"owasp_llm_top10_2025.json": 10,
"owasp_top10_2025.json": 10,
}
EXPECTED_LINK_PREFIXES = {
"owasp_aisvs_1_0.json": ("https://github.com/OWASP/AISVS/tree/main/1.0/en/",),
"owasp_api_top10_2023.json": ("https://owasp.org/API-Security/editions/2023/en/",),
"owasp_cheatsheets_supplement.json": (
"https://cheatsheetseries.owasp.org/cheatsheets/",
),
"owasp_kubernetes_top10_2022.json": (
"https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/",
),
"owasp_kubernetes_top10_2025.json": (
"https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/",
),
"owasp_llm_top10_2025.json": ("https://genai.owasp.org/llmrisk/",),
"owasp_top10_2025.json": ("https://owasp.org/Top10/2025/",),
}
EXPECTED_GOLDEN_MAPPINGS = {
("owasp_api_top10_2023.json", "API7"): {
"section": "Server Side Request Forgery",
"cre_ids": ["028-728", "657-084"],
},
("owasp_kubernetes_top10_2025.json", "K01"): {
"section": "Insecure Workload Configurations",
"cre_ids": ["233-748", "486-813"],
"fallback_section_ids": None,
},
("owasp_kubernetes_top10_2025.json", "K04"): {
"section": "Lack Of Cluster Level Policy Enforcement",
"cre_ids": ["117-371"],
"fallback_section_ids": None,
},
("owasp_kubernetes_top10_2025.json", "K07"): {
"section": "Misconfigured And Vulnerable Cluster Components",
"cre_ids": ["053-751", "233-748", "486-813", "715-334"],
"fallback_section_ids": ["K09", "K10"],
},
("owasp_llm_top10_2025.json", "LLM01"): {
"section": "Prompt Injection",
"cre_ids": ["161-451", "760-764"],
},
("owasp_top10_2025.json", "A05"): {
"section": "Injection",
"cre_ids": ["031-447", "064-808", "760-764"],
},
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
EXPECTED_CROSS_FIXTURE_LINKS = {
("owasp_cheatsheets_supplement.json", "Kubernetes Security Cheat Sheet"): {
"cre_ids": ["467-784", "233-748", "486-813"],
},
("owasp_aisvs_1_0.json", "AISVS10"): {
"cre_ids": ["307-507", "715-223"],
},
}


class TestOwaspMappingFixtures(unittest.TestCase):
@staticmethod
def _load_fixture(path: Path) -> list[dict[str, object]]:
payload = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(payload, list):
raise AssertionError(f"{path.name} must contain a top-level list")
return payload

def test_fixture_set_is_complete(self) -> None:
actual = {path.name for path in FIXTURE_DIR.glob("*.json")}
self.assertEqual(actual, EXPECTED_FIXTURES)

def test_fixtures_have_expected_mapping_shape(self) -> None:
for path in sorted(FIXTURE_DIR.glob("*.json")):
with self.subTest(fixture=path.name):
payload = json.loads(path.read_text(encoding="utf-8"))

self.assertIsInstance(payload, list)
payload = self._load_fixture(path)
self.assertGreater(len(payload), 0)
self.assertEqual(EXPECTED_SECTION_COUNTS[path.name], len(payload))

known_section_ids = {
entry["section_id"]
Expand All @@ -43,6 +110,15 @@ def test_fixtures_have_expected_mapping_shape(self) -> None:
self.assertTrue(entry["section"].strip())
self.assertIsInstance(entry.get("hyperlink"), str)
self.assertTrue(entry["hyperlink"].strip())
self.assertTrue(
entry["hyperlink"].startswith(
EXPECTED_LINK_PREFIXES[path.name]
),
msg=(
f"Unexpected hyperlink prefix for {path.name}: "
f"{entry['hyperlink']}"
),
)
self.assertIn("cre_ids", entry)
self.assertIsInstance(entry["cre_ids"], list)
self.assertGreater(len(entry["cre_ids"]), 0)
Expand Down Expand Up @@ -76,3 +152,46 @@ def test_fixtures_have_expected_mapping_shape(self) -> None:
self.assertIsInstance(cre_id, str)
self.assertTrue(cre_id.strip())
self.assertRegex(cre_id, CRE_ID_PATTERN)

def test_expected_golden_mappings_remain_stable(self) -> None:
for (fixture_name, section_key), expected in EXPECTED_GOLDEN_MAPPINGS.items():
path = FIXTURE_DIR / fixture_name
payload = self._load_fixture(path)
entry = next(
entry for entry in payload if entry.get("section_id") == section_key
)
with self.subTest(fixture=fixture_name, section_id=section_key):
self.assertEqual(expected["section"], entry["section"])
self.assertEqual(expected["cre_ids"], entry["cre_ids"])
if "fallback_section_ids" in expected:
self.assertEqual(
expected["fallback_section_ids"],
entry.get("fallback_section_ids"),
)

def test_cross_fixture_reference_rows_stay_reviewable(self) -> None:
for (fixture_name, key), expected in EXPECTED_CROSS_FIXTURE_LINKS.items():
path = FIXTURE_DIR / fixture_name
payload = self._load_fixture(path)
if key.startswith("AISVS"):
entry = next(
entry for entry in payload if entry.get("section_id") == key
)
else:
entry = next(entry for entry in payload if entry.get("section") == key)
with self.subTest(fixture=fixture_name, key=key):
self.assertEqual(expected["cre_ids"], entry["cre_ids"])

def test_known_ambiguous_kubernetes_mapping_is_explicit(self) -> None:
payload = self._load_fixture(FIXTURE_DIR / "owasp_kubernetes_top10_2022.json")
entries_by_section_id = {
entry["section_id"]: entry
for entry in payload
if isinstance(entry.get("section_id"), str)
}
# K01/K09 intentionally share configuration-focused CREs until a better
# K09-specific mapping is validated from upstream source material.
self.assertEqual(
entries_by_section_id["K01"]["cre_ids"],
entries_by_section_id["K09"]["cre_ids"],
)
Loading
Loading