From d1afff629566dc34bd8cac6642fb306a927a94fc Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Wed, 1 Apr 2026 17:57:44 +0530 Subject: [PATCH 1/3] Add Kubernetes Top Ten comparison view --- .../src/pages/GapAnalysis/GapAnalysis.tsx | 20 +-- application/frontend/src/types.ts | 4 +- application/tests/web_main_test.py | 86 ++++++++++- application/web/web_main.py | 134 ++++++++++++------ 4 files changed, 184 insertions(+), 60 deletions(-) diff --git a/application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx b/application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx index 1bc72a4e4..e9480a09c 100644 --- a/application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx +++ b/application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx @@ -481,8 +481,8 @@ export const GapAnalysis = () => { Rank - OWASP Top 10 2021 - OWASP Top 10 2025 + {owaspComparison.standards[0]} + {owaspComparison.standards[1]} Changed @@ -493,21 +493,21 @@ export const GapAnalysis = () => { {item.rank} - {item.top10_2021?.hyperlink ? ( - - {item.top10_2021.section} + {item.left?.hyperlink ? ( + + {item.left.section} ) : ( - item.top10_2021?.section ?? Not mapped + item.left?.section ?? Not mapped )} - {item.top10_2025?.hyperlink ? ( - - {item.top10_2025.section} + {item.right?.hyperlink ? ( + + {item.right.section} ) : ( - item.top10_2025?.section ?? Not mapped + item.right?.section ?? Not mapped )} {item.changed ? 'Yes' : 'No'} diff --git a/application/frontend/src/types.ts b/application/frontend/src/types.ts index ec7ece9f7..a509d95a8 100644 --- a/application/frontend/src/types.ts +++ b/application/frontend/src/types.ts @@ -48,8 +48,8 @@ export interface OwaspTop10ComparisonItemEntry { export interface OwaspTop10ComparisonItem { rank: string; changed: boolean; - top10_2021?: OwaspTop10ComparisonItemEntry; - top10_2025?: OwaspTop10ComparisonItemEntry; + left?: OwaspTop10ComparisonItemEntry; + right?: OwaspTop10ComparisonItemEntry; } export interface OwaspTop10Comparison { diff --git a/application/tests/web_main_test.py b/application/tests/web_main_test.py index 763e32ae9..4b783edc5 100644 --- a/application/tests/web_main_test.py +++ b/application/tests/web_main_test.py @@ -772,11 +772,11 @@ def get_nodes_side_effect(name=None, **kwargs): ) self.assertEqual( "Broken Access Controls", - payload["owasp_top10_comparison"]["items"][0]["top10_2021"]["section"], + payload["owasp_top10_comparison"]["items"][0]["left"]["section"], ) self.assertEqual( "Broken Access Control", - payload["owasp_top10_comparison"]["items"][0]["top10_2025"]["section"], + payload["owasp_top10_comparison"]["items"][0]["right"]["section"], ) @patch.object(web_main.gap_analysis, "schedule") @@ -824,7 +824,7 @@ def get_nodes_side_effect(name=None, **kwargs): self.assertIn("owasp_top10_comparison", payload) self.assertEqual( "Broken Access Controls", - payload["owasp_top10_comparison"]["items"][0]["top10_2021"]["section"], + payload["owasp_top10_comparison"]["items"][0]["left"]["section"], ) @patch.object(web_main.cre_main, "fetch_upstream_json") @@ -1312,12 +1312,90 @@ def get_nodes_side_effect(name=None, **kwargs): self.assertIn("owasp_top10_comparison", payload) self.assertEqual( "Broken Access Control", - payload["owasp_top10_comparison"]["items"][0]["top10_2025"]["section"], + payload["owasp_top10_comparison"]["items"][0]["right"]["section"], ) schedule_mock.assert_called_once_with( ["OWASP Top 10 2021", "OWASP Top 10 2025"], db_mock.return_value ) + @patch.object(web_main.gap_analysis, "schedule") + @patch.object(db, "Node_collection") + def test_gap_analysis_adds_kubernetes_comparison_section( + self, db_mock, schedule_mock + ) -> None: + kubernetes_2022 = [ + defs.Standard( + name="OWASP Kubernetes Top Ten 2022", + sectionID="K01", + section="Insecure Workload Configurations", + hyperlink="https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K01-insecure-workload-configurations", + ), + defs.Standard( + name="OWASP Kubernetes Top Ten 2022", + sectionID="K02", + section="Supply Chain Vulnerabilities", + hyperlink="https://owasp.org/www-project-kubernetes-top-ten/2022/en/src/K02-supply-chain-vulnerabilities", + ), + ] + kubernetes_2025 = [ + defs.Standard( + name="OWASP Kubernetes Top Ten 2025 (Draft)", + sectionID="K01", + section="Insecure Workload Configurations", + hyperlink="https://owasp.org/www-project-kubernetes-top-ten/", + ), + defs.Standard( + name="OWASP Kubernetes Top Ten 2025 (Draft)", + sectionID="K02", + section="Overly Permissive Authorization Configurations", + hyperlink="https://owasp.org/www-project-kubernetes-top-ten/", + ), + ] + db_mock.return_value.get_gap_analysis_result.return_value = None + db_mock.return_value.gap_analysis_exists.return_value = False + + def get_nodes_side_effect(name=None, **kwargs): + if name == "OWASP Kubernetes Top Ten 2022": + return kubernetes_2022 + if name == "OWASP Kubernetes Top Ten 2025 (Draft)": + return kubernetes_2025 + return [] + + db_mock.return_value.get_nodes.side_effect = get_nodes_side_effect + schedule_mock.side_effect = RuntimeError("redis unavailable") + + with self.app.test_client() as client: + response = client.get( + "/rest/v1/map_analysis?standard=OWASP%20Kubernetes%20Top%20Ten%202022&standard=OWASP%20Kubernetes%20Top%20Ten%202025%20%28Draft%29", + headers={"Content-Type": "application/json"}, + ) + + payload = json.loads(response.data) + self.assertEqual(200, response.status_code) + self.assertIn("owasp_top10_comparison", payload) + self.assertEqual( + [ + "OWASP Kubernetes Top Ten 2022", + "OWASP Kubernetes Top Ten 2025 (Draft)", + ], + payload["owasp_top10_comparison"]["standards"], + ) + self.assertEqual( + "Supply Chain Vulnerabilities", + payload["owasp_top10_comparison"]["items"][1]["left"]["section"], + ) + self.assertEqual( + "Overly Permissive Authorization Configurations", + payload["owasp_top10_comparison"]["items"][1]["right"]["section"], + ) + schedule_mock.assert_called_once_with( + [ + "OWASP Kubernetes Top Ten 2022", + "OWASP Kubernetes Top Ten 2025 (Draft)", + ], + db_mock.return_value, + ) + @patch.object(db, "Node_collection") @patch.object(rq.job.Job, "fetch") @patch.object(rq.Queue, "enqueue_call") diff --git a/application/web/web_main.py b/application/web/web_main.py index b610d8b28..cc5b043a0 100644 --- a/application/web/web_main.py +++ b/application/web/web_main.py @@ -54,6 +54,20 @@ / "data" / "owasp_top10_2025.json" ) +OWASP_KUBERNETES_TOP10_2022_DATA_FILE = ( + pathlib.Path(__file__).resolve().parent.parent + / "utils" + / "external_project_parsers" + / "data" + / "owasp_kubernetes_top10_2022.json" +) +OWASP_KUBERNETES_TOP10_2025_DATA_FILE = ( + pathlib.Path(__file__).resolve().parent.parent + / "utils" + / "external_project_parsers" + / "data" + / "owasp_kubernetes_top10_2025.json" +) app = Blueprint( "web", @@ -185,8 +199,10 @@ def _normalize_source_name(source: Any) -> str | None: return normalized_source[:64] -def _load_owasp_top10_2025_entries() -> list[dict[str, str]]: - with OWASP_TOP10_2025_DATA_FILE.open("r", encoding="utf-8") as handle: +def _load_ranked_standard_entries( + data_file: pathlib.Path, +) -> list[dict[str, str]]: + with data_file.open("r", encoding="utf-8") as handle: return json.load(handle) @@ -195,65 +211,95 @@ def _normalize_standard_name(standard: str) -> str: return STANDARD_NAME_ALIASES.get(normalized_standard.lower(), normalized_standard) -def _build_owasp_top10_comparison( +def _standard_nodes_to_ranked_entries( + nodes: list[defs.Standard], +) -> list[dict[str, str]]: + return [ + { + "section_id": node.sectionID or "", + "section": node.section or "", + "hyperlink": node.hyperlink or "", + } + for node in nodes + ] + + +def _build_ranked_standard_comparison( standards: list[str], collection: db.Node_collection ) -> dict[str, Any] | None: requested = {str(standard).strip().lower() for standard in standards} - if requested != {"owasp top 10 2021", "owasp top 10 2025"}: - return None + if requested == {"owasp top 10 2021", "owasp top 10 2025"}: + left_standard = "OWASP Top 10 2021" + right_standard = "OWASP Top 10 2025" + left_nodes = sorted( + collection.get_nodes(name=left_standard), + key=lambda node: node.sectionID or "", + ) + if not left_nodes: + return None + left_entries = _standard_nodes_to_ranked_entries(left_nodes) - top10_2021 = sorted( - collection.get_nodes(name="OWASP Top 10 2021"), - key=lambda node: node.sectionID or "", - ) - if not top10_2021: + right_nodes = sorted( + collection.get_nodes(name=right_standard), + key=lambda node: node.sectionID or "", + ) + if right_nodes: + right_entries = _standard_nodes_to_ranked_entries(right_nodes) + else: + right_entries = _load_ranked_standard_entries(OWASP_TOP10_2025_DATA_FILE) + elif requested == { + KUBERNETES_TOP10_2022_STANDARD_NAME.lower(), + KUBERNETES_TOP10_2025_STANDARD_NAME.lower(), + }: + left_standard = KUBERNETES_TOP10_2022_STANDARD_NAME + right_standard = KUBERNETES_TOP10_2025_STANDARD_NAME + left_nodes = sorted( + collection.get_nodes(name=left_standard), + key=lambda node: node.sectionID or "", + ) + right_nodes = sorted( + collection.get_nodes(name=right_standard), + key=lambda node: node.sectionID or "", + ) + left_entries = ( + _standard_nodes_to_ranked_entries(left_nodes) + if left_nodes + else _load_ranked_standard_entries(OWASP_KUBERNETES_TOP10_2022_DATA_FILE) + ) + right_entries = ( + _standard_nodes_to_ranked_entries(right_nodes) + if right_nodes + else _load_ranked_standard_entries(OWASP_KUBERNETES_TOP10_2025_DATA_FILE) + ) + else: return None - top10_2025_nodes = sorted( - collection.get_nodes(name="OWASP Top 10 2025"), - key=lambda node: node.sectionID or "", - ) - if top10_2025_nodes: - top10_2025 = [ - { - "section_id": node.sectionID or "", - "section": node.section or "", - "hyperlink": node.hyperlink or "", - } - for node in top10_2025_nodes - ] - else: - top10_2025 = _load_owasp_top10_2025_entries() + if not left_entries: + return None - top10_2021_by_rank = { - node.sectionID - or "": { - "section_id": node.sectionID or "", - "section": node.section or "", - "hyperlink": node.hyperlink or "", - } - for node in top10_2021 + left_by_rank = { + entry["section_id"]: entry for entry in left_entries if entry.get("section_id") } - top10_2025_by_rank = { - entry["section_id"]: entry for entry in top10_2025 if entry.get("section_id") + right_by_rank = { + entry["section_id"]: entry for entry in right_entries if entry.get("section_id") } - ranks = sorted(set(top10_2021_by_rank.keys()) | set(top10_2025_by_rank.keys())) + ranks = sorted(set(left_by_rank.keys()) | set(right_by_rank.keys())) comparison = [] for rank in ranks: - item_2021 = top10_2021_by_rank.get(rank) - item_2025 = top10_2025_by_rank.get(rank) + left_entry = left_by_rank.get(rank) + right_entry = right_by_rank.get(rank) comparison.append( { "rank": rank, - "top10_2021": item_2021, - "top10_2025": item_2025, - "changed": (item_2021 or {}).get("section") - != (item_2025 or {}).get("section"), + "left": left_entry, + "right": right_entry, + "changed": (left_entry or {}).get("section") + != (right_entry or {}).get("section"), } ) return { - "standards": ["OWASP Top 10 2021", "OWASP Top 10 2025"], + "standards": [left_standard, right_standard], "items": comparison, } @@ -796,7 +842,7 @@ def map_analysis() -> Any: if direct_gap_analysis: return jsonify(direct_gap_analysis) abort(404, "No direct overlap found for requested standards") - owasp_top10_comparison = _build_owasp_top10_comparison(standards, database) + owasp_top10_comparison = _build_ranked_standard_comparison(standards, database) # First, check if we have cached results in the database if database.gap_analysis_exists(standards_hash): From 8abd85bd105d887c4b92a86a0f4c049b7800bbe7 Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Wed, 1 Apr 2026 18:10:02 +0530 Subject: [PATCH 2/3] Cover Kubernetes map analysis with cloud standards --- application/tests/web_main_test.py | 151 +++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/application/tests/web_main_test.py b/application/tests/web_main_test.py index 4b783edc5..00ff3c747 100644 --- a/application/tests/web_main_test.py +++ b/application/tests/web_main_test.py @@ -1078,6 +1078,157 @@ def test_gap_analysis_adds_specialized_section_for_api_cheatsheets( ) self.assertIn(base.id, payload["specialized_cheatsheet_section"]["result"]) + @patch.object(web_main.cre_main, "fetch_upstream_json") + @patch.object(web_main.gap_analysis, "schedule") + @patch.object(db, "Node_collection") + def test_gap_analysis_adds_specialized_section_for_kubernetes_2022_cheatsheets( + self, db_mock, schedule_mock, upstream_fetch_mock + ) -> None: + network_cre = defs.CRE( + id="132-146", name="Network segmentation", description="" + ) + base = defs.Standard( + name="OWASP Kubernetes Top Ten 2022", + sectionID="K07", + section="Missing Network Segmentation Controls", + ) + base.add_link( + defs.Link(ltype=defs.LinkTypes.LinkedTo, document=network_cre.shallow_copy()) + ) + + compare = defs.Standard( + name="OWASP Cheat Sheets", + section="Kubernetes Security Cheat Sheet", + ) + compare.add_link( + defs.Link(ltype=defs.LinkTypes.LinkedTo, document=network_cre.shallow_copy()) + ) + + db_mock.return_value.get_gap_analysis_result.return_value = None + db_mock.return_value.gap_analysis_exists.return_value = False + db_mock.return_value.get_nodes.side_effect = lambda name=None, **kwargs: ( + [base] + if name == "OWASP Kubernetes Top Ten 2022" + else [compare] if name == "OWASP Cheat Sheets" else [] + ) + schedule_mock.side_effect = RuntimeError("redis unavailable") + upstream_fetch_mock.side_effect = RuntimeError("upstream unavailable") + + with self.app.test_client() as client: + response = client.get( + "/rest/v1/map_analysis?standard=OWASP%20Kubernetes%20Top%20Ten%202022&standard=OWASP%20Cheat%20Sheets", + headers={"Content-Type": "application/json"}, + ) + + payload = json.loads(response.data) + self.assertEqual(200, response.status_code) + self.assertEqual( + "Cloud Cheat Sheets", + payload["specialized_cheatsheet_section"]["category"], + ) + self.assertIn(base.id, payload["specialized_cheatsheet_section"]["result"]) + self.assertIn(compare.id, payload["result"][base.id]["paths"]) + + @patch.object(web_main.cre_main, "fetch_upstream_json") + @patch.object(web_main.gap_analysis, "schedule") + @patch.object(db, "Node_collection") + def test_gap_analysis_adds_specialized_section_for_kubernetes_2025_cheatsheets( + self, db_mock, schedule_mock, upstream_fetch_mock + ) -> None: + network_cre = defs.CRE( + id="132-146", name="Network segmentation", description="" + ) + base = defs.Standard( + name="OWASP Kubernetes Top Ten 2025 (Draft)", + sectionID="K05", + section="Missing Network Segmentation Controls", + ) + base.add_link( + defs.Link(ltype=defs.LinkTypes.LinkedTo, document=network_cre.shallow_copy()) + ) + + compare = defs.Standard( + name="OWASP Cheat Sheets", + section="Kubernetes Security Cheat Sheet", + ) + compare.add_link( + defs.Link(ltype=defs.LinkTypes.LinkedTo, document=network_cre.shallow_copy()) + ) + + db_mock.return_value.get_gap_analysis_result.return_value = None + db_mock.return_value.gap_analysis_exists.return_value = False + db_mock.return_value.get_nodes.side_effect = lambda name=None, **kwargs: ( + [base] + if name == "OWASP Kubernetes Top Ten 2025 (Draft)" + else [compare] if name == "OWASP Cheat Sheets" else [] + ) + schedule_mock.side_effect = RuntimeError("redis unavailable") + upstream_fetch_mock.side_effect = RuntimeError("upstream unavailable") + + with self.app.test_client() as client: + response = client.get( + "/rest/v1/map_analysis?standard=OWASP%20Kubernetes%20Top%20Ten%202025%20%28Draft%29&standard=OWASP%20Cheat%20Sheets", + headers={"Content-Type": "application/json"}, + ) + + payload = json.loads(response.data) + self.assertEqual(200, response.status_code) + self.assertEqual( + "Cloud Cheat Sheets", + payload["specialized_cheatsheet_section"]["category"], + ) + self.assertIn(base.id, payload["specialized_cheatsheet_section"]["result"]) + self.assertIn(compare.id, payload["result"][base.id]["paths"]) + + @patch.object(web_main.cre_main, "fetch_upstream_json") + @patch.object(web_main.gap_analysis, "schedule") + @patch.object(db, "Node_collection") + def test_gap_analysis_returns_direct_overlap_for_kubernetes_and_ccm( + self, db_mock, schedule_mock, upstream_fetch_mock + ) -> None: + shared_cre = defs.CRE( + id="117-371", name="Centralized policy enforcement", description="" + ) + base = defs.Standard( + name="OWASP Kubernetes Top Ten 2022", + sectionID="K04", + section="Lack of Centralized Policy Enforcement", + ) + base.add_link( + defs.Link(ltype=defs.LinkTypes.LinkedTo, document=shared_cre.shallow_copy()) + ) + + compare = defs.Standard( + name="Cloud Controls Matrix", + sectionID="AIS-01", + section="Application and Interface Security", + ) + compare.add_link( + defs.Link(ltype=defs.LinkTypes.LinkedTo, document=shared_cre.shallow_copy()) + ) + + db_mock.return_value.get_gap_analysis_result.return_value = None + db_mock.return_value.gap_analysis_exists.return_value = False + db_mock.return_value.get_nodes.side_effect = lambda name=None, **kwargs: ( + [base] + if name == "OWASP Kubernetes Top Ten 2022" + else [compare] if name == "Cloud Controls Matrix" else [] + ) + schedule_mock.side_effect = RuntimeError("redis unavailable") + upstream_fetch_mock.side_effect = RuntimeError("upstream unavailable") + + with self.app.test_client() as client: + response = client.get( + "/rest/v1/map_analysis?standard=OWASP%20Kubernetes%20Top%20Ten%202022&standard=Cloud%20Controls%20Matrix", + headers={"Content-Type": "application/json"}, + ) + + payload = json.loads(response.data) + self.assertEqual(200, response.status_code) + self.assertIn("result", payload) + self.assertIn(base.id, payload["result"]) + self.assertIn(compare.id, payload["result"][base.id]["paths"]) + @patch.object(web_main.gap_analysis, "schedule") @patch.object(db, "Node_collection") def test_gap_analysis_supports_opencre_as_standard( From 0c816b78e54e95a0350b180219c17c8a01a51a3d Mon Sep 17 00:00:00 2001 From: bornunique911 Date: Wed, 1 Apr 2026 18:40:49 +0530 Subject: [PATCH 3/3] Fix Kubernetes Top Ten 2025 section hyperlinks --- ...owasp_kubernetes_top10_2025_parser_test.py | 8 ++++++++ .../data/owasp_kubernetes_top10_2025.json | 20 +++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/application/tests/owasp_kubernetes_top10_2025_parser_test.py b/application/tests/owasp_kubernetes_top10_2025_parser_test.py index 6f444c9a9..b453975a9 100644 --- a/application/tests/owasp_kubernetes_top10_2025_parser_test.py +++ b/application/tests/owasp_kubernetes_top10_2025_parser_test.py @@ -42,10 +42,18 @@ def test_parse(self) -> None: self.assertEqual(10, len(entries)) self.assertEqual("K01", entries[0].sectionID) self.assertEqual("Insecure Workload Configurations", entries[0].section) + self.assertEqual( + "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K01-Insecure-Workload-Configurations.html", + entries[0].hyperlink, + ) self.assertEqual( ["233-748", "486-813"], [l.document.id for l in entries[0].links] ) self.assertEqual("K10", entries[-1].sectionID) + self.assertEqual( + "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K10-Inadequate-Logging-And-Monitoring.html", + entries[-1].hyperlink, + ) self.assertEqual( ["148-420", "402-706", "843-841"], [l.document.id for l in entries[-1].links], diff --git a/application/utils/external_project_parsers/data/owasp_kubernetes_top10_2025.json b/application/utils/external_project_parsers/data/owasp_kubernetes_top10_2025.json index c55afb059..cf72881d0 100644 --- a/application/utils/external_project_parsers/data/owasp_kubernetes_top10_2025.json +++ b/application/utils/external_project_parsers/data/owasp_kubernetes_top10_2025.json @@ -2,70 +2,70 @@ { "section_id": "K01", "section": "Insecure Workload Configurations", - "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K01-Insecure-Workload-Configurations.html", "cre_ids": ["233-748", "486-813"], "fallback_section_ids": ["K01"] }, { "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/", + "hyperlink": "https://owasp.org/www-project-kubernetes-top-ten/2025/en/src/K04-Lack-Of-Cluster-Level-Policy-Enforcement.html", "cre_ids": ["117-371"], "fallback_section_ids": ["K04"] }, { "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"] }