cwe-tree 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cwe_tree-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Yichao Xu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include data *.csv
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.2
2
+ Name: cwe_tree
3
+ Version: 1.0.0
4
+ Summary: A Python package for querying CWE trees
5
+ Home-page: https://github.com/YichaoXu/cwe-tree
6
+ Author: Yichao Xu
7
+ Author-email: yxu166@jhu.edu
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: requires-python
21
+ Dynamic: summary
22
+
23
+ # CWE Tree Python Package
24
+
25
+ ## Overview
26
+
27
+ This package provides a structured representation of the [Common Weakness Enumeration (CWE)](https://cwe.mitre.org/) hierarchy using a tree-based data model. It allows users to query CWE nodes, understand parent-child relationships, and extract metadata for each CWE entry.
28
+
29
+ ## Features
30
+ - Load CWE nodes and relationships from CSV files (`nodes.csv` and `rels.csv`).
31
+ - Represent CWE entries as `CweNode` objects.
32
+ - Store and manage the entire CWE structure as a `CweTree`.
33
+ - Query CWE metadata, parent-child relationships, and tree layers.
34
+
35
+ ## Installation
36
+
37
+ Clone this repository and install dependencies if required:
38
+
39
+ ```sh
40
+ pip install -e .
41
+ ```
42
+
43
+ or using pip repo
44
+
45
+ ```sh
46
+ pip install cwe_tree
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ### Import the package
52
+ ```python
53
+ from cwe_tree import query, CweTree, CweNode
54
+ ```
55
+
56
+ ### Querying CWE nodes
57
+
58
+ ```python
59
+ # Retrieve a CWE node by ID
60
+ cwe_node = query.get_node("CWE-732")
61
+ if cwe_node:
62
+ print(cwe_node.get_metadata())
63
+ ```
64
+
65
+ ### Fetching parent-child relationships
66
+
67
+ ```python
68
+ # Get parent CWEs
69
+ parents = query.get_parents("CWE-732")
70
+ print("Parent CWE IDs:", parents)
71
+
72
+ # Get child CWEs
73
+ children = query.get_children("CWE-732")
74
+ print("Child CWE IDs:", children)
75
+ ```
76
+
77
+ ### Retrieving metadata
78
+
79
+ ```python
80
+ metadata = query.get_metadata("CWE-732")
81
+ print("CWE Metadata:", metadata)
82
+ ```
83
+
84
+ ### Getting root nodes
85
+ ```python
86
+ roots = query.get_roots()
87
+ print("Root CWE nodes:", [node.cwe_id for node in roots])
88
+ ```
89
+
90
+ ## API Reference
91
+
92
+ ### `CweNode`
93
+ Represents a single CWE entry.
94
+
95
+ #### Properties:
96
+ - `cwe_id`: The unique CWE identifier.
97
+ - `name`: The CWE name/description.
98
+ - `abstract`: The abstraction type (e.g., Class, Base, Variant).
99
+ - `layer`: A dictionary representing the depth in various CWE trees.
100
+ - `parents`: A set of parent CWE IDs.
101
+ - `children`: A set of child CWE IDs.
102
+
103
+ #### Methods:
104
+ - `get_metadata() -> dict`: Returns CWE node metadata.
105
+
106
+ ### `CweTree`
107
+ Manages the CWE hierarchy and provides querying capabilities.
108
+
109
+ #### Methods:
110
+ - `get_node(cwe_id: str) -> CweNode`: Retrieves a CWE node by ID.
111
+ - `get_parents(cwe_id: str) -> set`: Returns parent CWE IDs.
112
+ - `get_children(cwe_id: str) -> set`: Returns child CWE IDs.
113
+ - `get_layer(cwe_id: str) -> dict`: Returns the CWE's layer mapping.
114
+ - `get_metadata(cwe_id: str) -> dict`: Returns CWE metadata.
115
+ - `get_roots() -> list`: Returns a list of root CWE nodes.
116
+
117
+ ## Data Format
118
+
119
+ The package loads CWE data from CSV files:
120
+ - `nodes.csv`: Contains CWE nodes with columns: `id`, `name`, `abstract`, `layer`
121
+ - `rels.csv`: Contains relationships with columns: `source`, `target`
122
+
123
+ ## License
124
+ This package is released under the MIT License.
125
+
126
+ ## Contributing
127
+ Contributions are welcome! Please submit a pull request or open an issue for discussions.
128
+
@@ -0,0 +1,106 @@
1
+ # CWE Tree Python Package
2
+
3
+ ## Overview
4
+
5
+ This package provides a structured representation of the [Common Weakness Enumeration (CWE)](https://cwe.mitre.org/) hierarchy using a tree-based data model. It allows users to query CWE nodes, understand parent-child relationships, and extract metadata for each CWE entry.
6
+
7
+ ## Features
8
+ - Load CWE nodes and relationships from CSV files (`nodes.csv` and `rels.csv`).
9
+ - Represent CWE entries as `CweNode` objects.
10
+ - Store and manage the entire CWE structure as a `CweTree`.
11
+ - Query CWE metadata, parent-child relationships, and tree layers.
12
+
13
+ ## Installation
14
+
15
+ Clone this repository and install dependencies if required:
16
+
17
+ ```sh
18
+ pip install -e .
19
+ ```
20
+
21
+ or using pip repo
22
+
23
+ ```sh
24
+ pip install cwe_tree
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Import the package
30
+ ```python
31
+ from cwe_tree import query, CweTree, CweNode
32
+ ```
33
+
34
+ ### Querying CWE nodes
35
+
36
+ ```python
37
+ # Retrieve a CWE node by ID
38
+ cwe_node = query.get_node("CWE-732")
39
+ if cwe_node:
40
+ print(cwe_node.get_metadata())
41
+ ```
42
+
43
+ ### Fetching parent-child relationships
44
+
45
+ ```python
46
+ # Get parent CWEs
47
+ parents = query.get_parents("CWE-732")
48
+ print("Parent CWE IDs:", parents)
49
+
50
+ # Get child CWEs
51
+ children = query.get_children("CWE-732")
52
+ print("Child CWE IDs:", children)
53
+ ```
54
+
55
+ ### Retrieving metadata
56
+
57
+ ```python
58
+ metadata = query.get_metadata("CWE-732")
59
+ print("CWE Metadata:", metadata)
60
+ ```
61
+
62
+ ### Getting root nodes
63
+ ```python
64
+ roots = query.get_roots()
65
+ print("Root CWE nodes:", [node.cwe_id for node in roots])
66
+ ```
67
+
68
+ ## API Reference
69
+
70
+ ### `CweNode`
71
+ Represents a single CWE entry.
72
+
73
+ #### Properties:
74
+ - `cwe_id`: The unique CWE identifier.
75
+ - `name`: The CWE name/description.
76
+ - `abstract`: The abstraction type (e.g., Class, Base, Variant).
77
+ - `layer`: A dictionary representing the depth in various CWE trees.
78
+ - `parents`: A set of parent CWE IDs.
79
+ - `children`: A set of child CWE IDs.
80
+
81
+ #### Methods:
82
+ - `get_metadata() -> dict`: Returns CWE node metadata.
83
+
84
+ ### `CweTree`
85
+ Manages the CWE hierarchy and provides querying capabilities.
86
+
87
+ #### Methods:
88
+ - `get_node(cwe_id: str) -> CweNode`: Retrieves a CWE node by ID.
89
+ - `get_parents(cwe_id: str) -> set`: Returns parent CWE IDs.
90
+ - `get_children(cwe_id: str) -> set`: Returns child CWE IDs.
91
+ - `get_layer(cwe_id: str) -> dict`: Returns the CWE's layer mapping.
92
+ - `get_metadata(cwe_id: str) -> dict`: Returns CWE metadata.
93
+ - `get_roots() -> list`: Returns a list of root CWE nodes.
94
+
95
+ ## Data Format
96
+
97
+ The package loads CWE data from CSV files:
98
+ - `nodes.csv`: Contains CWE nodes with columns: `id`, `name`, `abstract`, `layer`
99
+ - `rels.csv`: Contains relationships with columns: `source`, `target`
100
+
101
+ ## License
102
+ This package is released under the MIT License.
103
+
104
+ ## Contributing
105
+ Contributions are welcome! Please submit a pull request or open an issue for discussions.
106
+
@@ -0,0 +1,48 @@
1
+ import csv, os
2
+ from .cwe_node import CweNode # Import the CweNode class (represents a CWE node)
3
+ from .cwe_tree import CweTree # Import the CweTree class (represents the entire CWE tree)
4
+
5
+ # Get the absolute path of the current file and locate the `data/` directory containing CSV files
6
+ base_path = os.path.dirname(os.path.abspath(__file__)) # Get the module's directory path
7
+ nodes_csv = os.path.join(base_path, "data", "nodes.csv") # Path to the nodes CSV file
8
+ rels_csv = os.path.join(base_path, "data", "rels.csv") # Path to the relationships CSV file
9
+
10
+ def _load_data(cwe_tree: CweTree) -> CweTree:
11
+ """
12
+ Loads data from `nodes.csv` and `rels.csv` in the `data/` directory and populates the CweTree instance.
13
+
14
+ Args:
15
+ cwe_tree (CweTree): An instance of CweTree to be populated with data.
16
+
17
+ Returns:
18
+ CweTree: A fully populated CweTree instance containing all CWE nodes and relationships.
19
+ """
20
+ base_path = os.path.dirname(os.path.abspath(__file__)) # Get module's directory path
21
+ nodes_csv = os.path.join(base_path, "data", "nodes.csv") # Path to the nodes CSV file
22
+ rels_csv = os.path.join(base_path, "data", "rels.csv") # Path to the relationships CSV file
23
+
24
+ # Read `nodes.csv` and add nodes to the CweTree
25
+ with open(nodes_csv, "r", encoding="utf-8") as f:
26
+ reader = csv.DictReader(f) # Read CSV as a dictionary
27
+ for row in reader:
28
+ cwe_id = row["id"] # Retrieve CWE ID
29
+ name = row["name"] # Retrieve CWE name
30
+ abstract = row["abstract"] # Retrieve CWE abstraction type
31
+ layer = row["layer"] # Retrieve layer information
32
+ cwe_tree._add_node(cwe_id, name, abstract, layer) # Add node to CweTree
33
+
34
+ # Read `rels.csv` and add relationships (edges)
35
+ with open(rels_csv, "r", encoding="utf-8") as f:
36
+ reader = csv.DictReader(f) # Read CSV as a dictionary
37
+ for row in reader:
38
+ parent_id = row["source"] # Retrieve source (parent) CWE ID
39
+ child_id = row["target"] # Retrieve target (child) CWE ID
40
+ cwe_tree._add_edge(parent_id, child_id) # Add edge (parent-child relationship) in CweTree
41
+
42
+ return cwe_tree # Return the populated CweTree instance
43
+
44
+ # Create a `CweTree` instance and load data immediately when the module is imported
45
+ query: CweTree = _load_data(CweTree())
46
+
47
+ # Define `__all__` to specify the public API of the module
48
+ __all__ = ["query", "CweTree", "CweNode"] # Allows users to import `query`, `CweTree`, and `CweNode`
@@ -0,0 +1,116 @@
1
+ class CweNode:
2
+ """
3
+ Represents a single CWE (Common Weakness Enumeration) node in the CWE hierarchy.
4
+
5
+ A CWE node contains:
6
+ - A unique CWE ID.
7
+ - A name describing the weakness.
8
+ - An abstract type (e.g., Class, Base, Variant).
9
+ - A layer mapping indicating its depth in different root trees.
10
+ - Parent-child relationships to track CWE dependencies.
11
+ """
12
+
13
+ def __init__(self, cwe_id: str, name: str, abstract: str):
14
+ """
15
+ Initializes a CWE node with its unique ID, name, and type.
16
+
17
+ Args:
18
+ cwe_id (str): The unique CWE identifier (e.g., "CWE-732").
19
+ name (str): The name/description of the CWE.
20
+ abstract (str): The abstraction type (e.g., "Class", "Base", "Variant").
21
+ """
22
+ self._cwe_id = cwe_id # CWE ID (e.g., "CWE-732")
23
+ self._name = name # CWE Name (e.g., "Incorrect Permission Assignment for Critical Resource")
24
+ self._abstract = abstract # CWE Type (e.g., "Class", "Base", "Variant")
25
+ self._layer = {} # Dictionary to store layer levels in different trees { "CWE-284": 2 }
26
+ self._parents = set() # Set of parent nodes (other CWEs this node is derived from)
27
+ self._children = set() # Set of child nodes (other CWEs that depend on this node)
28
+
29
+ @property
30
+ def cwe_id(self) -> str:
31
+ """Returns the CWE ID (read-only)."""
32
+ return self._cwe_id
33
+
34
+ @property
35
+ def name(self) -> str:
36
+ """Returns the CWE name (read-only)."""
37
+ return self._name
38
+
39
+ @property
40
+ def abstract(self) -> str:
41
+ """Returns the CWE abstraction type (read-only)."""
42
+ return self._abstract
43
+
44
+ @property
45
+ def layer(self) -> dict:
46
+ """
47
+ Returns a copy of the layer mapping.
48
+
49
+ The layer mapping stores the depth level of this node in different root CWE trees.
50
+ Example:
51
+ { "CWE-284": 2 } means this node is at level 2 in the CWE-284 hierarchy.
52
+ """
53
+ return self._layer.copy() # Return a copy to prevent modification
54
+
55
+ @property
56
+ def parents(self) -> set:
57
+ """
58
+ Returns a copy of the set of parent CWE nodes.
59
+
60
+ Parents are CWEs from which this node is derived.
61
+ """
62
+ return self._parents.copy() # Return a copy to prevent modification
63
+
64
+ @property
65
+ def children(self) -> set:
66
+ """
67
+ Returns a copy of the set of child CWE nodes.
68
+
69
+ Children are CWEs that depend on this node.
70
+ """
71
+ return self._children.copy() # Return a copy to prevent modification
72
+
73
+ def _add_layer(self, root_id: str, level: int):
74
+ """
75
+ Adds or updates the layer mapping for this node.
76
+
77
+ Args:
78
+ root_id (str): The root CWE ID representing the tree.
79
+ level (int): The depth level of this node in the specified CWE tree.
80
+ """
81
+ self._layer[root_id] = level
82
+
83
+ def _add_parent(self, parent_id: str):
84
+ """
85
+ Adds a parent CWE ID to this node.
86
+
87
+ Args:
88
+ parent_id (str): The CWE ID of the parent node.
89
+ """
90
+ self._parents.add(parent_id)
91
+
92
+ def _add_child(self, child_id: str):
93
+ """
94
+ Adds a child CWE ID to this node.
95
+
96
+ Args:
97
+ child_id (str): The CWE ID of the child node.
98
+ """
99
+ self._children.add(child_id)
100
+
101
+ def get_metadata(self) -> dict:
102
+ """
103
+ Returns metadata of this CWE node, including its ID, name, abstraction type, layer mapping,
104
+ and relationships (parents and children).
105
+
106
+ Returns:
107
+ dict: A dictionary containing CWE node information.
108
+ """
109
+ return {
110
+ "id": self.cwe_id,
111
+ "name": self.name,
112
+ "abstract": self.abstract,
113
+ "layer": self.layer, # Layer mapping in different root trees
114
+ "parents": list(self.parents), # Convert set to list for serialization
115
+ "children": list(self.children), # Convert set to list for serialization
116
+ }
@@ -0,0 +1,147 @@
1
+ import json
2
+ from .cwe_node import CweNode
3
+
4
+ class CweTree:
5
+ """
6
+ Represents a CWE Tree, which contains CWE nodes and their relationships.
7
+
8
+ This class manages:
9
+ - Creating and storing CWE nodes.
10
+ - Establishing parent-child relationships between CWE nodes.
11
+ - Providing utility functions for retrieving metadata and relationships.
12
+ """
13
+
14
+ def __init__(self):
15
+ """ Initializes an empty CWE Tree. """
16
+ self._nodes = {} # Dictionary to store CWE nodes { "CWE-732": CweNode, ... }
17
+
18
+ def _normalize_cwe(self, cwe_id: str) -> str:
19
+ """
20
+ Normalizes a CWE ID to ensure consistency.
21
+
22
+ If the CWE ID does not start with "CWE-", it adds the prefix.
23
+ This allows users to query using either "732" or "CWE-732".
24
+
25
+ Args:
26
+ cwe_id (str): The CWE ID to normalize.
27
+
28
+ Returns:
29
+ str: Normalized CWE ID (e.g., "CWE-732").
30
+ """
31
+ return f"CWE-{cwe_id}" if not cwe_id.startswith("CWE-") else cwe_id
32
+
33
+ def _add_node(self, cwe_id: str, name: str, abstract: str, layer: str):
34
+ """
35
+ Adds a CWE node to the tree.
36
+
37
+ Args:
38
+ cwe_id (str): The unique CWE identifier.
39
+ name (str): The name/description of the CWE.
40
+ abstract (str): The abstraction type (e.g., "Class", "Base", "Variant").
41
+ layer (str): A JSON string or dictionary representing the node's layer levels.
42
+ """
43
+ cwe_id = self._normalize_cwe(cwe_id)
44
+
45
+ # Create node if it doesn't already exist
46
+ if cwe_id not in self._nodes:
47
+ self._nodes[cwe_id] = CweNode(cwe_id, name, abstract)
48
+
49
+ # Parse and store layer data (JSON format)
50
+ try:
51
+ layer_data = json.loads(layer) if isinstance(layer, str) else layer
52
+ if isinstance(layer_data, dict):
53
+ for root, level in layer_data.items():
54
+ self._nodes[cwe_id]._add_layer(root, level)
55
+ except json.JSONDecodeError:
56
+ pass # Ignore invalid layer data
57
+
58
+ def _add_edge(self, parent_id: str, child_id: str):
59
+ """
60
+ Establishes a parent-child relationship between two CWE nodes.
61
+
62
+ Args:
63
+ parent_id (str): The CWE ID of the parent node.
64
+ child_id (str): The CWE ID of the child node.
65
+ """
66
+ parent_id, child_id = self._normalize_cwe(parent_id), self._normalize_cwe(child_id)
67
+
68
+ # Ensure both nodes exist before creating a relationship
69
+ if parent_id in self._nodes and child_id in self._nodes:
70
+ self._nodes[parent_id]._add_child(child_id)
71
+ self._nodes[child_id]._add_parent(parent_id)
72
+
73
+ def get_node(self, cwe_id: str) -> CweNode:
74
+ """
75
+ Retrieves a CWE node by its ID.
76
+
77
+ Args:
78
+ cwe_id (str): The CWE ID to retrieve.
79
+
80
+ Returns:
81
+ CweNode: The requested CWE node, or None if it does not exist.
82
+ """
83
+ cwe_id = self._normalize_cwe(cwe_id)
84
+ return self._nodes.get(cwe_id)
85
+
86
+ def get_parents(self, cwe_id: str) -> set:
87
+ """
88
+ Retrieves the parents of a given CWE node.
89
+
90
+ Args:
91
+ cwe_id (str): The CWE ID whose parents should be retrieved.
92
+
93
+ Returns:
94
+ set: A set of parent CWE IDs.
95
+ """
96
+ node = self.get_node(cwe_id)
97
+ return node.parents if node else set()
98
+
99
+ def get_children(self, cwe_id: str) -> set:
100
+ """
101
+ Retrieves the children of a given CWE node.
102
+
103
+ Args:
104
+ cwe_id (str): The CWE ID whose children should be retrieved.
105
+
106
+ Returns:
107
+ set: A set of child CWE IDs.
108
+ """
109
+ node = self.get_node(cwe_id)
110
+ return node.children if node else set()
111
+
112
+ def get_layer(self, cwe_id: str) -> dict:
113
+ """
114
+ Retrieves the layer information for a given CWE node.
115
+
116
+ Args:
117
+ cwe_id (str): The CWE ID whose layer should be retrieved.
118
+
119
+ Returns:
120
+ dict: A dictionary representing the layer mapping (e.g., { "CWE-284": 2 }).
121
+ """
122
+ node = self.get_node(cwe_id)
123
+ return node.layer if node else {}
124
+
125
+ def get_metadata(self, cwe_id: str) -> dict:
126
+ """
127
+ Retrieves metadata for a given CWE node.
128
+
129
+ Args:
130
+ cwe_id (str): The CWE ID whose metadata should be retrieved.
131
+
132
+ Returns:
133
+ dict: A dictionary containing metadata about the CWE node.
134
+ """
135
+ node = self.get_node(cwe_id)
136
+ return node.get_metadata() if node else None
137
+
138
+ def get_roots(self) -> list:
139
+ """
140
+ Retrieves all root nodes in the CWE tree.
141
+
142
+ Root nodes are nodes that have no parents.
143
+
144
+ Returns:
145
+ list: A list of CweNode instances that have no parents.
146
+ """
147
+ return [node for node in self._nodes.values() if not node.parents]