Last active
November 26, 2025 05:46
-
-
Save righettod/770eab8fbe6918db3b2c33baf8873cfa to your computer and use it in GitHub Desktop.
Script to search against the XML reference file for CWE records using either a CWE ID or a term.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| import os | |
| import re | |
| from termcolor import colored | |
| from lxml import etree | |
| from lxml.etree import XMLParser | |
| from pathlib import Path | |
| """ | |
| Script to search for CWE records using either a CWE ID or a term. | |
| Search is performed against the XML reference file available for download here: | |
| https://cwe.mitre.org/data/xml/cwec_latest.xml.zip | |
| Dependencies: | |
| pip install termcolor lxml | |
| """ | |
| CWE_XML_REFERENTIAL = str(Path(os.getenv("WORKSPACE_FOLDER_BASE_PATH") + "/cache/CWE/cwe.xml")) | |
| CWE_XML_REFERENTIAL_NAMESPACES = {"cwe": "http://cwe.mitre.org/cwe-7"} | |
| SECURE_PARSER = XMLParser(resolve_entities=False, no_network=True, load_dtd=False) | |
| class Weakness: | |
| id = -1 | |
| name = "" | |
| description = "" | |
| status = "" | |
| mapping_usage = "" | |
| capec_ids_list = "" | |
| def __init__(self, cwe_node): | |
| self.id = int(cwe_node.get("ID")) | |
| self.name = cwe_node.get("Name") | |
| self.status = cwe_node.get("Status").strip().lower() | |
| self.description = cwe_node.find("cwe:Description", namespaces=CWE_XML_REFERENTIAL_NAMESPACES).text.strip() | |
| self.description = re.sub(r'(\n|\r|\t)', '', self.description) | |
| self.mapping_usage = cwe_node.find("cwe:Mapping_Notes", namespaces=CWE_XML_REFERENTIAL_NAMESPACES).find("cwe:Usage", namespaces=CWE_XML_REFERENTIAL_NAMESPACES).text.strip().lower() | |
| capec_node_parent = cwe_node.find("cwe:Related_Attack_Patterns", namespaces=CWE_XML_REFERENTIAL_NAMESPACES) | |
| capecs_ids = [] | |
| if capec_node_parent is not None: | |
| capec_nodes = capec_node_parent.findall("cwe:Related_Attack_Pattern", namespaces=CWE_XML_REFERENTIAL_NAMESPACES) | |
| for capec_node in capec_nodes: | |
| capec_node_id = capec_node.get("CAPEC_ID") | |
| capecs_ids.append(int(capec_node_id)) | |
| if len(capecs_ids) > 0: | |
| capecs_ids.sort() | |
| capec_ids_rendering = [] | |
| for capec_id in capecs_ids: | |
| capec_ids_rendering.append(f"CAPEC-{capec_id}") | |
| self.capec_ids_list = " / ".join(capec_ids_rendering) | |
| else: | |
| self.capec_ids_list = "None" | |
| def get_weaknesses(search_criteria): | |
| weaknesses = [] | |
| tree = etree.parse(CWE_XML_REFERENTIAL, parser=SECURE_PARSER) | |
| if search_criteria.isdigit(): | |
| xpath_expr = f".//cwe:Weakness[@ID='{search_criteria}']" | |
| else: | |
| terms = str(search_criteria).lower().strip() | |
| xpath_expr = f".//cwe:Weakness[contains(translate(@Name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),'{terms}')]" | |
| cwe_nodes = tree.xpath(xpath_expr, namespaces=CWE_XML_REFERENTIAL_NAMESPACES) | |
| if cwe_nodes is not None and len(cwe_nodes) > 0: | |
| for cwe_node in cwe_nodes: | |
| weakness = Weakness(cwe_node) | |
| weaknesses.append(weakness) | |
| return weaknesses | |
| def is_category(cwe_id): | |
| tree = etree.parse(CWE_XML_REFERENTIAL, parser=SECURE_PARSER) | |
| xpath_expr = f".//cwe:Category[@ID='{cwe_id}']" | |
| cwe_nodes = tree.xpath(xpath_expr, namespaces=CWE_XML_REFERENTIAL_NAMESPACES) | |
| return (cwe_nodes is not None and len(cwe_nodes) > 0) | |
| def render_weaknesses(weaknesses, search_criteria): | |
| if len(weaknesses) == 0 and search_criteria.isdigit() and is_category(search_criteria): | |
| print(colored("⚠️ CWE ID specified is a category.", "magenta")) | |
| print(colored(f"https://cwe.mitre.org/data/definitions/{search_criteria}.html", "cyan")) | |
| elif len(weaknesses) == 0: | |
| print(colored("❌ No weakness found.", "red")) | |
| else: | |
| for weakness in weaknesses: | |
| print(colored(f"🐞 CWE-{weakness.id}: {weakness.name}", "yellow")) | |
| if weakness.status == "stable": | |
| color = "green" | |
| else: | |
| color = "magenta" | |
| print(f"Status is {colored(weakness.status, color)}") | |
| if weakness.mapping_usage.startswith("allowed"): | |
| color = "green" | |
| elif weakness.mapping_usage == "discouraged": | |
| color = "red" | |
| else: | |
| color = "magenta" | |
| print(f"Mapping is {colored(weakness.mapping_usage, color)}") | |
| print(f"Related CAPEC: {weakness.capec_ids_list}") | |
| print(colored(f"https://cwe.mitre.org/data/definitions/{weakness.id}.html", "cyan")) | |
| print(f"{weakness.description}\n") | |
| if len(sys.argv) < 2: | |
| print(colored("⚠️ Missing search criteria: CWE ID or terms.", "yellow")) | |
| else: | |
| search_criteria = sys.argv[1] | |
| search_criteria_type = "terms" | |
| if search_criteria.isdigit(): | |
| search_criteria_type = "ID" | |
| print(f"ℹ️ Search weaknesses by {search_criteria_type}.") | |
| weaknesses = get_weaknesses(search_criteria) | |
| render_weaknesses(weaknesses, search_criteria) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment