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 +21 -0
- cwe_tree-1.0.0/MANIFEST.in +3 -0
- cwe_tree-1.0.0/PKG-INFO +128 -0
- cwe_tree-1.0.0/README.md +106 -0
- cwe_tree-1.0.0/cwe_tree/__init__.py +48 -0
- cwe_tree-1.0.0/cwe_tree/cwe_node.py +116 -0
- cwe_tree-1.0.0/cwe_tree/cwe_tree.py +147 -0
- cwe_tree-1.0.0/cwe_tree/data/nodes.csv +939 -0
- cwe_tree-1.0.0/cwe_tree/data/rels.csv +929 -0
- cwe_tree-1.0.0/cwe_tree.egg-info/PKG-INFO +128 -0
- cwe_tree-1.0.0/cwe_tree.egg-info/SOURCES.txt +14 -0
- cwe_tree-1.0.0/cwe_tree.egg-info/dependency_links.txt +1 -0
- cwe_tree-1.0.0/cwe_tree.egg-info/top_level.txt +1 -0
- cwe_tree-1.0.0/pyproject.toml +3 -0
- cwe_tree-1.0.0/setup.cfg +4 -0
- cwe_tree-1.0.0/setup.py +22 -0
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.
|
cwe_tree-1.0.0/PKG-INFO
ADDED
|
@@ -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
|
+
|
cwe_tree-1.0.0/README.md
ADDED
|
@@ -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]
|