Skip to content
22 changes: 22 additions & 0 deletions application/cmd/cre_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
from application.utils import db_backend
from application.utils import gap_analysis
from application.utils import cres_csv_export
from application.utils.external_project_parsers.parsers import (
owasp_kubernetes_top10_2022,
owasp_kubernetes_top10_2025,
)

if TYPE_CHECKING:
from application.prompt_client import prompt_client as prompt_client
Expand Down Expand Up @@ -975,6 +979,24 @@ def run(args: argparse.Namespace) -> None: # pragma: no cover
BaseParser().register_resource(
secure_headers.SecureHeaders, db_connection_str=args.cache_file
)
if args.owasp_kubernetes_top10_2022_in:
from application.utils.external_project_parsers.parsers import (
owasp_kubernetes_top10_2022,
)

BaseParser().register_resource(
owasp_kubernetes_top10_2022.OwaspKubernetesTop10_2022,
db_connection_str=args.cache_file,
)
if args.owasp_kubernetes_top10_2025_in:
from application.utils.external_project_parsers.parsers import (
owasp_kubernetes_top10_2025,
)

BaseParser().register_resource(
owasp_kubernetes_top10_2025.OwaspKubernetesTop10_2025,
db_connection_str=args.cache_file,
)
if args.pci_dss_4_in:
from application.utils.external_project_parsers.parsers import pci_dss

Expand Down
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])
Original file line number Diff line number Diff line change
@@ -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,
)
Loading
Loading