graphrag-codeproperty-graph 0.4.3__tar.gz → 0.4.5__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.
- graphrag_codeproperty_graph-0.4.5/.gitignore +31 -0
- graphrag_codeproperty_graph-0.4.5/CHANGELOG.md +16 -0
- graphrag_codeproperty_graph-0.4.5/CONTRIBUTING.md +76 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/PKG-INFO +35 -29
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/README.md +30 -23
- graphrag_codeproperty_graph-0.4.5/examples/README.md +45 -0
- graphrag_codeproperty_graph-0.4.5/examples/notebooks/01-CPG-Models-and-GraphDiff.ipynb +309 -0
- graphrag_codeproperty_graph-0.4.5/examples/notebooks/02-Delta-Ingestion-Pipeline.ipynb +253 -0
- graphrag_codeproperty_graph-0.4.5/examples/notebooks/data/sample_edges.json +25 -0
- graphrag_codeproperty_graph-0.4.5/examples/notebooks/data/sample_nodes.json +21 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/pyproject.toml +8 -10
- graphrag_codeproperty_graph-0.4.3/.gitignore +0 -5
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/.github/workflows/publish.yml +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/LICENSE +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/NOTICE +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/ARCHITECTURE.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/Defining the Master Evidence Graph: A Formal Model for Stateful Software Verdicts.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/HYBRID-GRAPH.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/NOTICE.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/Toward a Master Evidence Graph for Software Verdicts.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/graphrag_com_redrawn_architecture.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/graphrag_phased_architecture.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/neptune_vs_neptune_analytics_cpg_rag_architecture copy.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/neptune_vs_neptune_analytics_cpg_rag_architecture.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/sbom/AWS-SERVICES.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/sbom/SBOM.md +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/sbom/codeproperty-graph.cdx.json +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/src/codeproperty_graph/__init__.py +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/src/codeproperty_graph/delta_ingestor.py +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/src/codeproperty_graph/graph_diff.py +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/src/codeproperty_graph/manifest_manager.py +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/src/codeproperty_graph/models.py +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/src/codeproperty_graph/tenant_ops.py +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/tests/__init__.py +0 -0
- {graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/tests/test_codeproperty_graph.py +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Kiro
|
|
2
|
+
.kiro/
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg
|
|
12
|
+
.eggs/
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
ENV/
|
|
18
|
+
|
|
19
|
+
# IDE
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
22
|
+
*.swp
|
|
23
|
+
|
|
24
|
+
# OS
|
|
25
|
+
.DS_Store
|
|
26
|
+
Thumbs.db
|
|
27
|
+
|
|
28
|
+
# Testing
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.coverage
|
|
31
|
+
htmlcov/
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.4.3] - 2026-08-03
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial public release
|
|
7
|
+
- Joern/Semgrep CPG extraction and ingestion
|
|
8
|
+
- Delta ingestion — skip-or-replace logic to minimize Neptune writes
|
|
9
|
+
- Graph diff comparison between CPG states
|
|
10
|
+
- S3-backed manifest management for repository state tracking
|
|
11
|
+
- AI vulnerability enrichment and risk scoring
|
|
12
|
+
- Tenant lifecycle management (create, purge, replace)
|
|
13
|
+
- CPGNode and CPGEdge Pydantic models (Joern-specific types)
|
|
14
|
+
|
|
15
|
+
### Dependencies
|
|
16
|
+
- graphrag-document-graph>=3.0.8
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! This project is maintained by [Kanjani AI Research](https://github.com/kanjani-ai-research).
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
1. Clone the repository:
|
|
8
|
+
```bash
|
|
9
|
+
git clone <repo-url>
|
|
10
|
+
cd <project-dir>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. Create a virtual environment and install in editable mode with dev dependencies:
|
|
14
|
+
```bash
|
|
15
|
+
python -m venv .venv
|
|
16
|
+
source .venv/bin/activate
|
|
17
|
+
pip install -e '.[dev]'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Running Tests
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pytest tests/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
For coverage:
|
|
27
|
+
```bash
|
|
28
|
+
pytest tests/ --cov --cov-report=term-missing
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Code Style
|
|
32
|
+
|
|
33
|
+
This project uses:
|
|
34
|
+
- **[Black](https://github.com/psf/black)** for code formatting
|
|
35
|
+
- **[Ruff](https://github.com/astral-sh/ruff)** for linting
|
|
36
|
+
|
|
37
|
+
Before submitting, ensure your code passes:
|
|
38
|
+
```bash
|
|
39
|
+
black .
|
|
40
|
+
ruff check .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Submitting Pull Requests
|
|
44
|
+
|
|
45
|
+
1. Fork the repository
|
|
46
|
+
2. Create a feature branch from `main`:
|
|
47
|
+
```bash
|
|
48
|
+
git checkout -b feature/your-feature
|
|
49
|
+
```
|
|
50
|
+
3. Make your changes, including tests
|
|
51
|
+
4. Ensure all tests pass and code style checks are clean
|
|
52
|
+
5. Commit with a clear, descriptive message
|
|
53
|
+
6. Push and open a Pull Request against `main`
|
|
54
|
+
|
|
55
|
+
### PR Guidelines
|
|
56
|
+
|
|
57
|
+
- Keep PRs focused — one feature or fix per PR
|
|
58
|
+
- Include tests for new functionality
|
|
59
|
+
- Update documentation if behavior changes
|
|
60
|
+
- Reference any related issues in the PR description
|
|
61
|
+
|
|
62
|
+
## Reporting Issues
|
|
63
|
+
|
|
64
|
+
Open an issue on GitHub with:
|
|
65
|
+
- A clear description of the problem
|
|
66
|
+
- Steps to reproduce
|
|
67
|
+
- Expected vs actual behavior
|
|
68
|
+
- Python version and OS
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
|
|
73
|
+
|
|
74
|
+
## Organization
|
|
75
|
+
|
|
76
|
+
This project is part of the [Kanjani AI Research](https://github.com/kanjani-ai-research) organization.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graphrag-codeproperty-graph
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5
|
|
4
4
|
Summary: Code Property Graphs — Joern/Semgrep extraction, AI vulnerability enrichment, risk scoring
|
|
5
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Repository, https://github.com/kanjani-ai-research/codeproperty-graph
|
|
6
6
|
Author-email: Evan Erwee <evan@erwee.com>
|
|
7
7
|
License-Expression: MIT
|
|
8
8
|
License-File: LICENSE
|
|
@@ -13,30 +13,49 @@ Classifier: Intended Audience :: Developers
|
|
|
13
13
|
Classifier: Topic :: Security
|
|
14
14
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
15
15
|
Requires-Python: >=3.11
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist: graphrag-document-graph>=3.0.8
|
|
16
|
+
Requires-Dist: graphrag-document-graph>=3.1.1
|
|
18
17
|
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: black; extra == 'dev'
|
|
19
19
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
20
20
|
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
21
21
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
22
|
-
|
|
23
|
-
Requires-Dist: graphrag-toolkit-lexical-graph>=3.18.0; extra == 'graphrag'
|
|
22
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
24
23
|
Description-Content-Type: text/markdown
|
|
25
24
|
|
|
26
25
|
# Code Property Graph
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
[](https://pypi.org/project/graphrag-codeproperty-graph/)
|
|
28
|
+
[](https://opensource.org/licenses/MIT)
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
**Code Property Graphs — Joern/Semgrep extraction, AI vulnerability enrichment, risk scoring.**
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
> **This package depends on AWS GraphRAG Toolkit (graphrag-toolkit-lexical-graph) for graph storage, vector indexing, and retrieval.**
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
## Installation
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
pip install codeproperty-graph
|
|
37
|
+
pip install graphrag-codeproperty-graph
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
## Dependencies
|
|
41
|
+
|
|
42
|
+
- `graphrag-document-graph>=3.0.8` — Document graph infrastructure (typed nodes, Cypher builders, multi-tenancy)
|
|
43
|
+
|
|
44
|
+
This package does **not** depend on `bona`.
|
|
45
|
+
|
|
46
|
+
## Dependency Chain
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
graphrag-codeproperty-graph
|
|
50
|
+
└── graphrag-document-graph>=3.0.8
|
|
51
|
+
└── graphrag-toolkit-lexical-graph>=3.18.0 (AWS foundation)
|
|
52
|
+
├── Neptune graph storage
|
|
53
|
+
├── OpenSearch Serverless vector indexing
|
|
54
|
+
└── Entity resolution & retrieval
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
40
59
|
### Python API Example
|
|
41
60
|
|
|
42
61
|
```python
|
|
@@ -49,8 +68,8 @@ result = await ingestor.ingest(
|
|
|
49
68
|
repo="my-service",
|
|
50
69
|
job_id="build-456",
|
|
51
70
|
tenant_id="tenant_abc123",
|
|
52
|
-
nodes_data=joern_nodes,
|
|
53
|
-
edges_data=joern_edges,
|
|
71
|
+
nodes_data=joern_nodes,
|
|
72
|
+
edges_data=joern_edges,
|
|
54
73
|
nodes_path="s3://bucket/cpg-exports/my-service/build-456/nodes.json",
|
|
55
74
|
edges_path="s3://bucket/cpg-exports/my-service/build-456/edges.json",
|
|
56
75
|
graph_store=neptune_store,
|
|
@@ -113,7 +132,7 @@ src/codeproperty_graph/
|
|
|
113
132
|
│ codeproperty-graph (this package) │
|
|
114
133
|
│ Joern/Semgrep CPG, delta ingestion, risk scoring │
|
|
115
134
|
├─────────────────────────────────────────────────────┤
|
|
116
|
-
│ document-graph
|
|
135
|
+
│ document-graph │
|
|
117
136
|
│ Node, Edge, CypherBuilder, PipelineExecutor │
|
|
118
137
|
│ Multi-tenancy, batch operations │
|
|
119
138
|
├─────────────────────────────────────────────────────┤
|
|
@@ -144,22 +163,9 @@ from graphrag_toolkit.document_graph import Node, Edge
|
|
|
144
163
|
from codeproperty_graph import CPGNode, CPGEdge
|
|
145
164
|
```
|
|
146
165
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
After CPG ingestion, enrich with AI-powered vulnerability analysis:
|
|
150
|
-
|
|
151
|
-
```python
|
|
152
|
-
# Query Neptune for high-risk patterns
|
|
153
|
-
# Score methods by complexity, dependency depth, and known CVE proximity
|
|
154
|
-
# Annotate graph with risk scores for downstream consumption
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Requirements
|
|
166
|
+
## Contributing
|
|
158
167
|
|
|
159
|
-
|
|
160
|
-
- `document-graph >= 0.1.0`
|
|
161
|
-
- `boto3 >= 1.26.0`
|
|
162
|
-
- Optional: `graphrag-toolkit-lexical-graph >= 3.18.0`
|
|
168
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and PR guidelines.
|
|
163
169
|
|
|
164
170
|
## License
|
|
165
171
|
|
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
# Code Property Graph
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://pypi.org/project/graphrag-codeproperty-graph/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**Code Property Graphs — Joern/Semgrep extraction, AI vulnerability enrichment, risk scoring.**
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
> **This package depends on AWS GraphRAG Toolkit (graphrag-toolkit-lexical-graph) for graph storage, vector indexing, and retrieval.**
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
## Installation
|
|
10
11
|
|
|
11
12
|
```bash
|
|
12
|
-
pip install codeproperty-graph
|
|
13
|
+
pip install graphrag-codeproperty-graph
|
|
13
14
|
```
|
|
14
15
|
|
|
16
|
+
## Dependencies
|
|
17
|
+
|
|
18
|
+
- `graphrag-document-graph>=3.0.8` — Document graph infrastructure (typed nodes, Cypher builders, multi-tenancy)
|
|
19
|
+
|
|
20
|
+
This package does **not** depend on `bona`.
|
|
21
|
+
|
|
22
|
+
## Dependency Chain
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
graphrag-codeproperty-graph
|
|
26
|
+
└── graphrag-document-graph>=3.0.8
|
|
27
|
+
└── graphrag-toolkit-lexical-graph>=3.18.0 (AWS foundation)
|
|
28
|
+
├── Neptune graph storage
|
|
29
|
+
├── OpenSearch Serverless vector indexing
|
|
30
|
+
└── Entity resolution & retrieval
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
15
35
|
### Python API Example
|
|
16
36
|
|
|
17
37
|
```python
|
|
@@ -24,8 +44,8 @@ result = await ingestor.ingest(
|
|
|
24
44
|
repo="my-service",
|
|
25
45
|
job_id="build-456",
|
|
26
46
|
tenant_id="tenant_abc123",
|
|
27
|
-
nodes_data=joern_nodes,
|
|
28
|
-
edges_data=joern_edges,
|
|
47
|
+
nodes_data=joern_nodes,
|
|
48
|
+
edges_data=joern_edges,
|
|
29
49
|
nodes_path="s3://bucket/cpg-exports/my-service/build-456/nodes.json",
|
|
30
50
|
edges_path="s3://bucket/cpg-exports/my-service/build-456/edges.json",
|
|
31
51
|
graph_store=neptune_store,
|
|
@@ -88,7 +108,7 @@ src/codeproperty_graph/
|
|
|
88
108
|
│ codeproperty-graph (this package) │
|
|
89
109
|
│ Joern/Semgrep CPG, delta ingestion, risk scoring │
|
|
90
110
|
├─────────────────────────────────────────────────────┤
|
|
91
|
-
│ document-graph
|
|
111
|
+
│ document-graph │
|
|
92
112
|
│ Node, Edge, CypherBuilder, PipelineExecutor │
|
|
93
113
|
│ Multi-tenancy, batch operations │
|
|
94
114
|
├─────────────────────────────────────────────────────┤
|
|
@@ -119,22 +139,9 @@ from graphrag_toolkit.document_graph import Node, Edge
|
|
|
119
139
|
from codeproperty_graph import CPGNode, CPGEdge
|
|
120
140
|
```
|
|
121
141
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
After CPG ingestion, enrich with AI-powered vulnerability analysis:
|
|
125
|
-
|
|
126
|
-
```python
|
|
127
|
-
# Query Neptune for high-risk patterns
|
|
128
|
-
# Score methods by complexity, dependency depth, and known CVE proximity
|
|
129
|
-
# Annotate graph with risk scores for downstream consumption
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Requirements
|
|
142
|
+
## Contributing
|
|
133
143
|
|
|
134
|
-
|
|
135
|
-
- `document-graph >= 0.1.0`
|
|
136
|
-
- `boto3 >= 1.26.0`
|
|
137
|
-
- Optional: `graphrag-toolkit-lexical-graph >= 3.18.0`
|
|
144
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and PR guidelines.
|
|
138
145
|
|
|
139
146
|
## License
|
|
140
147
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Code Property Graph Examples
|
|
2
|
+
|
|
3
|
+
Example notebooks for `codeproperty-graph` — delta-aware code analysis ingestion into Neptune.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Amazon Neptune (DB or Analytics) for production notebooks
|
|
8
|
+
- Python 3.10+
|
|
9
|
+
- Joern (for generating real CPG exports; sample data provided for demos)
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install codeproperty-graph
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For Neptune integration:
|
|
18
|
+
```bash
|
|
19
|
+
pip install codeproperty-graph graphrag-toolkit-document-graph[graphrag,neptune]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Notebooks
|
|
23
|
+
|
|
24
|
+
| Notebook | Description | Neptune Required? |
|
|
25
|
+
|----------|-------------|-------------------|
|
|
26
|
+
| `01-CPG-Models-and-GraphDiff.ipynb` | Core models, method signatures, and diff comparison | No (local only) |
|
|
27
|
+
| `02-Delta-Ingestion-Pipeline.ipynb` | Full pipeline: manifest → diff → skip/ingest decision | Optional (has mock mode) |
|
|
28
|
+
|
|
29
|
+
## Sample Data
|
|
30
|
+
|
|
31
|
+
`data/sample_nodes.json` — 8 CPG nodes (3 METHOD, 2 CALL, 2 IDENTIFIER, 1 LITERAL)
|
|
32
|
+
`data/sample_edges.json` — 10 edges (AST, CFG, CALL, ARGUMENT)
|
|
33
|
+
|
|
34
|
+
Represents a minimal Java project with `Main.main()`, `Parser.parse()`, and `Validator.validate()`.
|
|
35
|
+
|
|
36
|
+
## Generating Real CPG Data
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Install Joern: https://joern.io
|
|
40
|
+
joern-export --repr cpg14 --format json --out cpg-export/ /path/to/source
|
|
41
|
+
|
|
42
|
+
# Output:
|
|
43
|
+
# cpg-export/nodes.json
|
|
44
|
+
# cpg-export/edges.json
|
|
45
|
+
```
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# CPG Schema, Models, and Graph Diff\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"This notebook demonstrates:\n",
|
|
10
|
+
"- The full Joern CPG schema (20 node types, 14+ edge types, 9 languages)\n",
|
|
11
|
+
"- Parsing Joern exports with `CPGNode.from_joern()` and `CPGEdge.from_joern()`\n",
|
|
12
|
+
"- Graph analysis: type distribution, call graph, structure\n",
|
|
13
|
+
"- Change detection with `GraphDiff`\n",
|
|
14
|
+
"\n",
|
|
15
|
+
"**Prerequisites:**\n",
|
|
16
|
+
"- `pip install graphrag-toolkit-codeproperty-graph`\n",
|
|
17
|
+
"- No Neptune or AWS credentials required\n"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"cell_type": "markdown",
|
|
22
|
+
"metadata": {},
|
|
23
|
+
"source": [
|
|
24
|
+
"## 1. The CPG Schema\n",
|
|
25
|
+
"\n",
|
|
26
|
+
"Explore the complete Joern type system.\n"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"cell_type": "code",
|
|
31
|
+
"metadata": {},
|
|
32
|
+
"source": [
|
|
33
|
+
"from graphrag_toolkit.codeproperty_graph import (\n",
|
|
34
|
+
" NodeType, EdgeType, DELTA_RELEVANT_TYPES, SUPPORTED_LANGUAGES,\n",
|
|
35
|
+
" joern_export_command\n",
|
|
36
|
+
")\n",
|
|
37
|
+
"\n",
|
|
38
|
+
"print(f'Node types ({len(NodeType)}):')\n",
|
|
39
|
+
"for nt in NodeType:\n",
|
|
40
|
+
" marker = ' \u2605' if nt in DELTA_RELEVANT_TYPES else ''\n",
|
|
41
|
+
" print(f' {nt.value:20s}{marker}')\n",
|
|
42
|
+
"\n",
|
|
43
|
+
"print(f'\\nEdge types ({len(EdgeType)}):')\n",
|
|
44
|
+
"for et in EdgeType:\n",
|
|
45
|
+
" print(f' {et.value}')\n",
|
|
46
|
+
"\n",
|
|
47
|
+
"print(f'\\nSupported languages ({len(SUPPORTED_LANGUAGES)}):')\n",
|
|
48
|
+
"for lang, desc in SUPPORTED_LANGUAGES.items():\n",
|
|
49
|
+
" print(f' {lang:12s} {desc}')\n",
|
|
50
|
+
"\n",
|
|
51
|
+
"print(f'\\n\u2605 = participates in delta comparison')\n"
|
|
52
|
+
],
|
|
53
|
+
"outputs": [],
|
|
54
|
+
"execution_count": null
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"cell_type": "markdown",
|
|
58
|
+
"metadata": {},
|
|
59
|
+
"source": [
|
|
60
|
+
"## 2. Joern Export Command\n",
|
|
61
|
+
"\n",
|
|
62
|
+
"Generate the CLI command to produce a CPG from source code.\n"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"cell_type": "code",
|
|
67
|
+
"metadata": {},
|
|
68
|
+
"source": [
|
|
69
|
+
"# Generate export commands for different languages\n",
|
|
70
|
+
"for lang in ['java', 'python', 'javascript']:\n",
|
|
71
|
+
" cmd = joern_export_command('/src/my-service', output_dir='cpg-out', language=lang)\n",
|
|
72
|
+
" print(f'{lang:12s} {cmd}')\n",
|
|
73
|
+
"\n",
|
|
74
|
+
"# Auto-detect language\n",
|
|
75
|
+
"print(f'{\"auto\":12s} {joern_export_command(\"/src/my-service\")}')\n"
|
|
76
|
+
],
|
|
77
|
+
"outputs": [],
|
|
78
|
+
"execution_count": null
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"cell_type": "markdown",
|
|
82
|
+
"metadata": {},
|
|
83
|
+
"source": [
|
|
84
|
+
"## 3. Parse Joern Export with `from_joern()`\n",
|
|
85
|
+
"\n",
|
|
86
|
+
"Load raw Joern JSON and parse into typed CPGNode/CPGEdge objects.\n",
|
|
87
|
+
"The factory methods handle both UPPER_CASE and camelCase property formats.\n"
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"cell_type": "code",
|
|
92
|
+
"metadata": {},
|
|
93
|
+
"source": [
|
|
94
|
+
"import json\n",
|
|
95
|
+
"from graphrag_toolkit.codeproperty_graph import CPGNode, CPGEdge\n",
|
|
96
|
+
"\n",
|
|
97
|
+
"with open('data/sample_nodes.json') as f:\n",
|
|
98
|
+
" raw_nodes = json.load(f)\n",
|
|
99
|
+
"with open('data/sample_edges.json') as f:\n",
|
|
100
|
+
" raw_edges = json.load(f)\n",
|
|
101
|
+
"\n",
|
|
102
|
+
"# Parse with from_joern()\n",
|
|
103
|
+
"nodes = [CPGNode.from_joern(raw) for raw in raw_nodes]\n",
|
|
104
|
+
"edges = [CPGEdge.from_joern(raw) for raw in raw_edges]\n",
|
|
105
|
+
"\n",
|
|
106
|
+
"print(f'Parsed {len(nodes)} nodes, {len(edges)} edges')\n"
|
|
107
|
+
],
|
|
108
|
+
"outputs": [],
|
|
109
|
+
"execution_count": null
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"cell_type": "markdown",
|
|
113
|
+
"metadata": {},
|
|
114
|
+
"source": [
|
|
115
|
+
"## 4. Graph Analysis: Type Distribution\n",
|
|
116
|
+
"\n",
|
|
117
|
+
"See what node and edge types are in the graph.\n"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"cell_type": "code",
|
|
122
|
+
"metadata": {},
|
|
123
|
+
"source": [
|
|
124
|
+
"from collections import Counter\n",
|
|
125
|
+
"\n",
|
|
126
|
+
"# Node type distribution\n",
|
|
127
|
+
"node_types = Counter(n.node_type for n in nodes)\n",
|
|
128
|
+
"print('Node types:')\n",
|
|
129
|
+
"for ntype, count in node_types.most_common():\n",
|
|
130
|
+
" print(f' {ntype:20s} {count:3d}')\n",
|
|
131
|
+
"\n",
|
|
132
|
+
"# Edge type distribution\n",
|
|
133
|
+
"edge_types = Counter(e.edge_type for e in edges)\n",
|
|
134
|
+
"print(f'\\nEdge types:')\n",
|
|
135
|
+
"for etype, count in edge_types.most_common():\n",
|
|
136
|
+
" print(f' {etype:20s} {count:3d}')\n"
|
|
137
|
+
],
|
|
138
|
+
"outputs": [],
|
|
139
|
+
"execution_count": null
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"cell_type": "markdown",
|
|
143
|
+
"metadata": {},
|
|
144
|
+
"source": [
|
|
145
|
+
"## 5. Explore Methods (Delta-Relevant Nodes)\n",
|
|
146
|
+
"\n",
|
|
147
|
+
"Only METHOD nodes participate in change detection.\n",
|
|
148
|
+
"Their `full_name \u2192 hash` mapping is the fingerprint for delta comparison.\n"
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"cell_type": "code",
|
|
153
|
+
"metadata": {},
|
|
154
|
+
"source": [
|
|
155
|
+
"methods = [n for n in nodes if n.node_type == NodeType.METHOD]\n",
|
|
156
|
+
"\n",
|
|
157
|
+
"print(f'Methods ({len(methods)}):')\n",
|
|
158
|
+
"for m in methods:\n",
|
|
159
|
+
" print(f' {m.full_name}')\n",
|
|
160
|
+
" print(f' signature: {m.signature}')\n",
|
|
161
|
+
" print(f' hash: {m.hash}')\n",
|
|
162
|
+
" print(f' file: {m.filename}:{m.line_number}-{m.line_number_end}')\n",
|
|
163
|
+
" print(f' external: {m.is_external}')\n",
|
|
164
|
+
" print()\n"
|
|
165
|
+
],
|
|
166
|
+
"outputs": [],
|
|
167
|
+
"execution_count": null
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"cell_type": "markdown",
|
|
171
|
+
"metadata": {},
|
|
172
|
+
"source": [
|
|
173
|
+
"## 6. Explore Structure (Classes, Files, Namespaces)\n"
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"cell_type": "code",
|
|
178
|
+
"metadata": {},
|
|
179
|
+
"source": [
|
|
180
|
+
"from graphrag_toolkit.codeproperty_graph.schema import STRUCTURE_TYPES\n",
|
|
181
|
+
"\n",
|
|
182
|
+
"structure_nodes = [n for n in nodes if n.node_type in STRUCTURE_TYPES]\n",
|
|
183
|
+
"print(f'Structure nodes ({len(structure_nodes)}):')\n",
|
|
184
|
+
"for n in structure_nodes:\n",
|
|
185
|
+
" print(f' {n.node_type:16s} {n.full_name or n.name}')\n"
|
|
186
|
+
],
|
|
187
|
+
"outputs": [],
|
|
188
|
+
"execution_count": null
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"cell_type": "markdown",
|
|
192
|
+
"metadata": {},
|
|
193
|
+
"source": [
|
|
194
|
+
"## 7. Call Graph\n",
|
|
195
|
+
"\n",
|
|
196
|
+
"Extract the CALL edges to see which methods call which.\n"
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"cell_type": "code",
|
|
201
|
+
"metadata": {},
|
|
202
|
+
"source": [
|
|
203
|
+
"call_edges = [e for e in edges if e.edge_type == EdgeType.CALL]\n",
|
|
204
|
+
"node_map = {n.id: n for n in nodes}\n",
|
|
205
|
+
"\n",
|
|
206
|
+
"print(f'Call graph ({len(call_edges)} edges):')\n",
|
|
207
|
+
"for e in call_edges:\n",
|
|
208
|
+
" src = node_map.get(e.source_id)\n",
|
|
209
|
+
" tgt = node_map.get(e.target_id)\n",
|
|
210
|
+
" src_name = src.full_name if src else e.source_id\n",
|
|
211
|
+
" tgt_name = tgt.full_name if tgt else e.target_id\n",
|
|
212
|
+
" print(f' {src_name} \u2192 {tgt_name}')\n"
|
|
213
|
+
],
|
|
214
|
+
"outputs": [],
|
|
215
|
+
"execution_count": null
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"cell_type": "markdown",
|
|
219
|
+
"metadata": {},
|
|
220
|
+
"source": [
|
|
221
|
+
"## 8. Delta Comparison (GraphDiff)\n",
|
|
222
|
+
"\n",
|
|
223
|
+
"Compare method signatures between two CPG snapshots.\n",
|
|
224
|
+
"This is how `DeltaIngestor` decides whether to skip or ingest.\n"
|
|
225
|
+
]
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"cell_type": "code",
|
|
229
|
+
"metadata": {},
|
|
230
|
+
"source": [
|
|
231
|
+
"from graphrag_toolkit.codeproperty_graph import GraphDiff\n",
|
|
232
|
+
"\n",
|
|
233
|
+
"# Current state\n",
|
|
234
|
+
"current_sigs = {m.full_name: m.hash for m in methods}\n",
|
|
235
|
+
"print(f'Current signatures:')\n",
|
|
236
|
+
"for name, h in current_sigs.items():\n",
|
|
237
|
+
" print(f' {name}: {h}')\n",
|
|
238
|
+
"\n",
|
|
239
|
+
"# Simulate next commit: parse() refactored, validate() deleted, check() added\n",
|
|
240
|
+
"next_sigs = {\n",
|
|
241
|
+
" 'com.example.Main.main': 'a1b2c3d4e5', # unchanged\n",
|
|
242
|
+
" 'com.example.Parser.parse': 'NEWH4SH999', # MODIFIED\n",
|
|
243
|
+
" 'com.example.Checker.check': 'p6q7r8s9t0', # ADDED\n",
|
|
244
|
+
"}\n",
|
|
245
|
+
"\n",
|
|
246
|
+
"diff = GraphDiff.compare(current_sigs, next_sigs)\n",
|
|
247
|
+
"print(f'\\nDelta: {diff.summary}')\n",
|
|
248
|
+
"print(f' Added: {list(diff.added.keys())}')\n",
|
|
249
|
+
"print(f' Removed: {list(diff.removed.keys())}')\n",
|
|
250
|
+
"print(f' Modified: {list(diff.modified.keys())}')\n",
|
|
251
|
+
"print(f' Unchanged: {diff.unchanged}')\n",
|
|
252
|
+
"print(f'\\n\u2192 DeltaIngestor would: {\"INGEST\" if diff.has_changes else \"SKIP\"}')\n"
|
|
253
|
+
],
|
|
254
|
+
"outputs": [],
|
|
255
|
+
"execution_count": null
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"cell_type": "markdown",
|
|
259
|
+
"metadata": {},
|
|
260
|
+
"source": [
|
|
261
|
+
"## 9. No-Change Case (SKIP)\n",
|
|
262
|
+
"\n",
|
|
263
|
+
"When code hasn't changed, `GraphDiff` returns no delta \u2014 zero Neptune writes.\n"
|
|
264
|
+
]
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"cell_type": "code",
|
|
268
|
+
"metadata": {},
|
|
269
|
+
"source": [
|
|
270
|
+
"no_diff = GraphDiff.compare(current_sigs, current_sigs)\n",
|
|
271
|
+
"print(f'Delta: {no_diff.summary}')\n",
|
|
272
|
+
"print(f'Has changes: {no_diff.has_changes}')\n",
|
|
273
|
+
"print(f'\\n\u2192 DeltaIngestor would: SKIP')\n",
|
|
274
|
+
"print(f' Zero Neptune writes. Zero cost. <1 second.')\n"
|
|
275
|
+
],
|
|
276
|
+
"outputs": [],
|
|
277
|
+
"execution_count": null
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"cell_type": "markdown",
|
|
281
|
+
"metadata": {},
|
|
282
|
+
"source": [
|
|
283
|
+
"## Summary\n",
|
|
284
|
+
"\n",
|
|
285
|
+
"| Feature | What It Does |\n",
|
|
286
|
+
"|---------|-------------|\n",
|
|
287
|
+
"| `NodeType` / `EdgeType` enums | Complete Joern schema (20 + 14 types) |\n",
|
|
288
|
+
"| `CPGNode.from_joern(raw)` | Parse Joern JSON export \u2192 typed objects |\n",
|
|
289
|
+
"| `DELTA_RELEVANT_TYPES` | Which types matter for change detection |\n",
|
|
290
|
+
"| `STRUCTURE_TYPES` | Types for architectural analysis |\n",
|
|
291
|
+
"| `GraphDiff.compare()` | Method signature delta \u2192 skip or ingest |\n",
|
|
292
|
+
"| `joern_export_command()` | Generate the CLI command for any language |\n"
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
],
|
|
296
|
+
"metadata": {
|
|
297
|
+
"kernelspec": {
|
|
298
|
+
"display_name": "Python 3",
|
|
299
|
+
"language": "python",
|
|
300
|
+
"name": "python3"
|
|
301
|
+
},
|
|
302
|
+
"language_info": {
|
|
303
|
+
"name": "python",
|
|
304
|
+
"version": "3.12.0"
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
"nbformat": 4,
|
|
308
|
+
"nbformat_minor": 4
|
|
309
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# 02 \u2014 Delta Ingestion Pipeline\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"This notebook demonstrates the full DeltaIngestor pipeline: manifest comparison, skip-or-ingest decision, and Neptune graph write.\n",
|
|
10
|
+
"\n",
|
|
11
|
+
"**What you'll learn:**\n",
|
|
12
|
+
"- How ManifestManager tracks CPG state in S3\n",
|
|
13
|
+
"- How DeltaIngestor decides to skip or ingest\n",
|
|
14
|
+
"- How tenant-based graph replacement works\n",
|
|
15
|
+
"\n",
|
|
16
|
+
"**Prerequisites:**\n",
|
|
17
|
+
"- Neptune Analytics or Neptune Database endpoint\n",
|
|
18
|
+
"- S3 bucket for manifests\n",
|
|
19
|
+
"- IAM permissions for Neptune and S3"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"cell_type": "code",
|
|
24
|
+
"metadata": {},
|
|
25
|
+
"source": [
|
|
26
|
+
"# %pip install codeproperty-graph graphrag-toolkit-document-graph[graphrag]"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"cell_type": "code",
|
|
31
|
+
"metadata": {},
|
|
32
|
+
"source": [
|
|
33
|
+
"import json\n",
|
|
34
|
+
"import asyncio\n",
|
|
35
|
+
"from codeproperty_graph import DeltaIngestor, ManifestManager, GraphDiff, CPGNode"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"cell_type": "markdown",
|
|
40
|
+
"metadata": {},
|
|
41
|
+
"source": [
|
|
42
|
+
"## Configuration"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"cell_type": "code",
|
|
47
|
+
"metadata": {},
|
|
48
|
+
"source": [
|
|
49
|
+
"# Replace with your values:\n",
|
|
50
|
+
"BUCKET = \"my-graphrag-artifacts\" # S3 bucket for manifests\n",
|
|
51
|
+
"REPO = \"example-service\" # Repository name\n",
|
|
52
|
+
"JOB_ID = \"build-001\" # CI/CD build identifier\n",
|
|
53
|
+
"NEPTUNE_ENDPOINT = \"neptune-analytics://my-graph-id\" # or neptune-db://...\n",
|
|
54
|
+
"\n",
|
|
55
|
+
"# Tenant ID (auto-generated in production, fixed here for demo)\n",
|
|
56
|
+
"TENANT_ID = \"demo123\""
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"cell_type": "markdown",
|
|
61
|
+
"metadata": {},
|
|
62
|
+
"source": [
|
|
63
|
+
"## Load Joern Export"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"cell_type": "code",
|
|
68
|
+
"metadata": {},
|
|
69
|
+
"source": [
|
|
70
|
+
"import json\n",
|
|
71
|
+
"from graphrag_toolkit.codeproperty_graph import CPGNode, CPGEdge, NodeType\n",
|
|
72
|
+
"\n",
|
|
73
|
+
"with open('data/sample_nodes.json') as f:\n",
|
|
74
|
+
" nodes = [CPGNode.from_joern(raw) for raw in json.load(f)]\n",
|
|
75
|
+
"\n",
|
|
76
|
+
"with open('data/sample_edges.json') as f:\n",
|
|
77
|
+
" edges = [CPGEdge.from_joern(raw) for raw in json.load(f)]\n",
|
|
78
|
+
"\n",
|
|
79
|
+
"print(f'Parsed {len(nodes)} nodes ({len(set(n.node_type for n in nodes))} types)')\n",
|
|
80
|
+
"print(f'Parsed {len(edges)} edges ({len(set(e.edge_type for e in edges))} types)')\n"
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"cell_type": "markdown",
|
|
85
|
+
"metadata": {},
|
|
86
|
+
"source": [
|
|
87
|
+
"## Step 1: Extract Method Signatures\n",
|
|
88
|
+
"\n",
|
|
89
|
+
"Only METHOD nodes participate in change detection."
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"cell_type": "code",
|
|
94
|
+
"metadata": {},
|
|
95
|
+
"source": [
|
|
96
|
+
"# Only METHOD nodes participate in delta comparison\n",
|
|
97
|
+
"methods = [n for n in nodes if n.node_type == NodeType.METHOD]\n",
|
|
98
|
+
"current_sigs = {m.full_name: m.hash for m in methods}\n",
|
|
99
|
+
"\n",
|
|
100
|
+
"print(f'Extracted {len(current_sigs)} method signatures (from {len(nodes)} total nodes):')\n",
|
|
101
|
+
"for name, h in current_sigs.items():\n",
|
|
102
|
+
" print(f' {name}: {h}')\n",
|
|
103
|
+
"\n",
|
|
104
|
+
"print(f'\\nOther node types (not in delta): {set(n.node_type for n in nodes if n.node_type != NodeType.METHOD)}')\n"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"cell_type": "markdown",
|
|
109
|
+
"metadata": {},
|
|
110
|
+
"source": [
|
|
111
|
+
"## Step 2: Check Previous Manifest\n",
|
|
112
|
+
"\n",
|
|
113
|
+
"ManifestManager retrieves the last known state from S3."
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"cell_type": "code",
|
|
118
|
+
"metadata": {},
|
|
119
|
+
"source": [
|
|
120
|
+
"manifest_mgr = ManifestManager(bucket=BUCKET)\n",
|
|
121
|
+
"\n",
|
|
122
|
+
"# In production this reads from S3. For demo, simulate:\n",
|
|
123
|
+
"# previous_manifest = manifest_mgr.get(REPO)\n",
|
|
124
|
+
"\n",
|
|
125
|
+
"# Simulate: first run (no previous manifest)\n",
|
|
126
|
+
"previous_manifest = None\n",
|
|
127
|
+
"\n",
|
|
128
|
+
"if previous_manifest is None:\n",
|
|
129
|
+
" print(\"No previous manifest \u2014 this is a fresh ingestion.\")\n",
|
|
130
|
+
" print(\"All methods will be treated as ADDED.\")\n",
|
|
131
|
+
"else:\n",
|
|
132
|
+
" print(f\"Previous manifest: tenant={previous_manifest.tenant_id}, job={previous_manifest.job_id}\")"
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"cell_type": "markdown",
|
|
137
|
+
"metadata": {},
|
|
138
|
+
"source": [
|
|
139
|
+
"## Step 3: Compute Diff"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"cell_type": "code",
|
|
144
|
+
"metadata": {},
|
|
145
|
+
"source": [
|
|
146
|
+
"previous_sigs = previous_manifest.method_signatures if previous_manifest else {}\n",
|
|
147
|
+
"\n",
|
|
148
|
+
"diff = GraphDiff.compare(previous_sigs, current_sigs)\n",
|
|
149
|
+
"print(f\"Diff: {diff.summary}\")\n",
|
|
150
|
+
"print(f\"Has changes: {diff.has_changes}\")\n",
|
|
151
|
+
"\n",
|
|
152
|
+
"if diff.has_changes:\n",
|
|
153
|
+
" print(\"\\n\u2192 Will INGEST to Neptune\")\n",
|
|
154
|
+
"else:\n",
|
|
155
|
+
" print(\"\\n\u2192 Will SKIP (no Neptune writes)\")"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"cell_type": "markdown",
|
|
160
|
+
"metadata": {},
|
|
161
|
+
"source": [
|
|
162
|
+
"## Step 4: Write to Neptune (if changed)\n",
|
|
163
|
+
"\n",
|
|
164
|
+
"In production, this uses the GraphStore. For this demo, we simulate the write."
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"cell_type": "code",
|
|
169
|
+
"metadata": {},
|
|
170
|
+
"source": [
|
|
171
|
+
"# Simulated write function (replace with actual Neptune write in production)\n",
|
|
172
|
+
"write_log = []\n",
|
|
173
|
+
"\n",
|
|
174
|
+
"async def mock_write_fn(query: str, params: dict):\n",
|
|
175
|
+
" \"\"\"Simulates a Neptune write \u2014 logs instead of executing.\"\"\"\n",
|
|
176
|
+
" write_log.append({'query': query[:80], 'param_count': len(params)})\n",
|
|
177
|
+
"\n",
|
|
178
|
+
"if diff.has_changes:\n",
|
|
179
|
+
" print(f\"Writing {len(nodes_data)} nodes and {len(edges_data)} edges...\")\n",
|
|
180
|
+
" print(f\"Tenant: {TENANT_ID}\")\n",
|
|
181
|
+
" print(f\"(Using mock write \u2014 replace with graph_store.execute_query in production)\")\n",
|
|
182
|
+
" \n",
|
|
183
|
+
" # In production:\n",
|
|
184
|
+
" # result = await ingestor.ingest(repo=REPO, job_id=JOB_ID, ...)\n",
|
|
185
|
+
" \n",
|
|
186
|
+
" # Simulated result:\n",
|
|
187
|
+
" result = {\n",
|
|
188
|
+
" \"status\": \"INGESTED\",\n",
|
|
189
|
+
" \"delta\": diff.summary,\n",
|
|
190
|
+
" \"tenant_id\": TENANT_ID,\n",
|
|
191
|
+
" \"nodes_written\": len(nodes_data),\n",
|
|
192
|
+
" \"edges_written\": len(edges_data),\n",
|
|
193
|
+
" }\n",
|
|
194
|
+
" print(f\"\\nResult: {json.dumps(result, indent=2)}\")\n",
|
|
195
|
+
"else:\n",
|
|
196
|
+
" result = {\"status\": \"SKIPPED\", \"reason\": \"no changes detected\"}\n",
|
|
197
|
+
" print(f\"Result: {json.dumps(result, indent=2)}\")"
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"cell_type": "markdown",
|
|
202
|
+
"metadata": {},
|
|
203
|
+
"source": [
|
|
204
|
+
"## Step 5: Simulate Second Run (No Changes)\n",
|
|
205
|
+
"\n",
|
|
206
|
+
"On the next CI/CD run with no code changes, the ingestor skips entirely."
|
|
207
|
+
]
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"cell_type": "code",
|
|
211
|
+
"metadata": {},
|
|
212
|
+
"source": [
|
|
213
|
+
"# Same signatures as before \u2014 simulates \"no code change\" commit\n",
|
|
214
|
+
"second_run_sigs = current_sigs.copy()\n",
|
|
215
|
+
"\n",
|
|
216
|
+
"diff2 = GraphDiff.compare(current_sigs, second_run_sigs)\n",
|
|
217
|
+
"print(f\"Second run diff: {diff2.summary}\")\n",
|
|
218
|
+
"print(f\"Has changes: {diff2.has_changes}\")\n",
|
|
219
|
+
"print()\n",
|
|
220
|
+
"print(\"\u2192 SKIPPED \u2014 zero Neptune writes, zero cost, <1 second\")\n",
|
|
221
|
+
"print(\" This is the common case in CI/CD (most commits don't change method bodies)\")"
|
|
222
|
+
]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"cell_type": "markdown",
|
|
226
|
+
"metadata": {},
|
|
227
|
+
"source": [
|
|
228
|
+
"## Summary\n",
|
|
229
|
+
"\n",
|
|
230
|
+
"| Scenario | Action | Neptune Cost | Time |\n",
|
|
231
|
+
"|----------|--------|-------------|------|\n",
|
|
232
|
+
"| First run (no manifest) | Full ingest | N nodes + M edges | Seconds |\n",
|
|
233
|
+
"| Code changed | Full replace (new tenant, purge old) | N nodes + M edges + purge | Seconds |\n",
|
|
234
|
+
"| No code change | SKIP | Zero | <1s |\n",
|
|
235
|
+
"\n",
|
|
236
|
+
"The delta logic ensures you only pay for Neptune writes when code actually changes."
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"metadata": {
|
|
241
|
+
"kernelspec": {
|
|
242
|
+
"display_name": "Python 3",
|
|
243
|
+
"language": "python",
|
|
244
|
+
"name": "python3"
|
|
245
|
+
},
|
|
246
|
+
"language_info": {
|
|
247
|
+
"name": "python",
|
|
248
|
+
"version": "3.12.0"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"nbformat": 4,
|
|
252
|
+
"nbformat_minor": 4
|
|
253
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"src": "5001", "dst": "4001", "label": "CONTAINS", "properties": {}},
|
|
3
|
+
{"src": "5002", "dst": "4002", "label": "CONTAINS", "properties": {}},
|
|
4
|
+
{"src": "5003", "dst": "4003", "label": "CONTAINS", "properties": {}},
|
|
5
|
+
{"src": "4001", "dst": "1001", "label": "CONTAINS", "properties": {}},
|
|
6
|
+
{"src": "4002", "dst": "1002", "label": "CONTAINS", "properties": {}},
|
|
7
|
+
{"src": "4003", "dst": "1003", "label": "CONTAINS", "properties": {}},
|
|
8
|
+
{"src": "1001", "dst": "2001", "label": "AST", "properties": {"ORDER": 1}},
|
|
9
|
+
{"src": "1001", "dst": "2002", "label": "AST", "properties": {"ORDER": 2}},
|
|
10
|
+
{"src": "1001", "dst": "3001", "label": "AST", "properties": {"ORDER": 3}},
|
|
11
|
+
{"src": "2001", "dst": "3001", "label": "ARGUMENT", "properties": {"ORDER": 1}},
|
|
12
|
+
{"src": "2001", "dst": "3002", "label": "ARGUMENT", "properties": {"ORDER": 2}},
|
|
13
|
+
{"src": "2002", "dst": "3003", "label": "ARGUMENT", "properties": {"ORDER": 1}},
|
|
14
|
+
{"src": "1001", "dst": "1002", "label": "CALL", "properties": {}},
|
|
15
|
+
{"src": "1001", "dst": "1003", "label": "CALL", "properties": {}},
|
|
16
|
+
{"src": "1001", "dst": "1002", "label": "CFG", "properties": {}},
|
|
17
|
+
{"src": "1002", "dst": "1003", "label": "CFG", "properties": {}},
|
|
18
|
+
{"src": "1001", "dst": "6001", "label": "AST", "properties": {"ORDER": 0}},
|
|
19
|
+
{"src": "1002", "dst": "6002", "label": "AST", "properties": {"ORDER": 0}},
|
|
20
|
+
{"src": "1003", "dst": "6003", "label": "AST", "properties": {"ORDER": 0}},
|
|
21
|
+
{"src": "3001", "dst": "6001", "label": "REF", "properties": {}},
|
|
22
|
+
{"src": "7001", "dst": "4001", "label": "CONTAINS", "properties": {}},
|
|
23
|
+
{"src": "7001", "dst": "4002", "label": "CONTAINS", "properties": {}},
|
|
24
|
+
{"src": "7001", "dst": "4003", "label": "CONTAINS", "properties": {}}
|
|
25
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"id": "1001", "label": "METHOD", "properties": {"FULL_NAME": "com.example.Main.main", "SIGNATURE": "void(String[])", "HASH": "a1b2c3d4e5", "FILENAME": "src/Main.java", "LINE_NUMBER": 5, "LINE_NUMBER_END": 12, "CODE": "public static void main(String[] args) { Parser.parse(args[0]); }", "IS_EXTERNAL": false}},
|
|
3
|
+
{"id": "1002", "label": "METHOD", "properties": {"FULL_NAME": "com.example.Parser.parse", "SIGNATURE": "Document(String)", "HASH": "f6g7h8i9j0", "FILENAME": "src/Parser.java", "LINE_NUMBER": 12, "LINE_NUMBER_END": 18, "CODE": "public Document parse(String input) { return new Document(input); }", "IS_EXTERNAL": false}},
|
|
4
|
+
{"id": "1003", "label": "METHOD", "properties": {"FULL_NAME": "com.example.Validator.validate", "SIGNATURE": "boolean(Document)", "HASH": "k1l2m3n4o5", "FILENAME": "src/Validator.java", "LINE_NUMBER": 8, "LINE_NUMBER_END": 11, "CODE": "public boolean validate(Document doc) { return doc != null; }", "IS_EXTERNAL": false}},
|
|
5
|
+
{"id": "2001", "label": "CALL", "properties": {"NAME": "parse", "FULL_NAME": "com.example.Parser.parse", "CODE": "Parser.parse(args[0])", "LINE_NUMBER": 6, "DISPATCH_TYPE": "STATIC_DISPATCH"}},
|
|
6
|
+
{"id": "2002", "label": "CALL", "properties": {"NAME": "validate", "FULL_NAME": "com.example.Validator.validate", "CODE": "validator.validate(doc)", "LINE_NUMBER": 7, "DISPATCH_TYPE": "DYNAMIC_DISPATCH"}},
|
|
7
|
+
{"id": "3001", "label": "IDENTIFIER", "properties": {"NAME": "args", "CODE": "args", "LINE_NUMBER": 6, "ORDER": 1}},
|
|
8
|
+
{"id": "3002", "label": "LITERAL", "properties": {"CODE": "0", "LINE_NUMBER": 6, "ORDER": 2}},
|
|
9
|
+
{"id": "3003", "label": "IDENTIFIER", "properties": {"NAME": "doc", "CODE": "doc", "LINE_NUMBER": 7, "ORDER": 1}},
|
|
10
|
+
{"id": "4001", "label": "TYPE_DECL", "properties": {"FULL_NAME": "com.example.Main", "NAME": "Main", "IS_EXTERNAL": false, "ORDER": 1}},
|
|
11
|
+
{"id": "4002", "label": "TYPE_DECL", "properties": {"FULL_NAME": "com.example.Parser", "NAME": "Parser", "IS_EXTERNAL": false, "ORDER": 2}},
|
|
12
|
+
{"id": "4003", "label": "TYPE_DECL", "properties": {"FULL_NAME": "com.example.Validator", "NAME": "Validator", "IS_EXTERNAL": false, "ORDER": 3}},
|
|
13
|
+
{"id": "5001", "label": "FILE", "properties": {"NAME": "src/Main.java", "ORDER": 1}},
|
|
14
|
+
{"id": "5002", "label": "FILE", "properties": {"NAME": "src/Parser.java", "ORDER": 2}},
|
|
15
|
+
{"id": "5003", "label": "FILE", "properties": {"NAME": "src/Validator.java", "ORDER": 3}},
|
|
16
|
+
{"id": "6001", "label": "PARAMETER", "properties": {"NAME": "args", "CODE": "String[] args", "LINE_NUMBER": 5, "ORDER": 1, "TYPE_FULL_NAME": "java.lang.String[]"}},
|
|
17
|
+
{"id": "6002", "label": "PARAMETER", "properties": {"NAME": "input", "CODE": "String input", "LINE_NUMBER": 12, "ORDER": 1, "TYPE_FULL_NAME": "java.lang.String"}},
|
|
18
|
+
{"id": "6003", "label": "PARAMETER", "properties": {"NAME": "doc", "CODE": "Document doc", "LINE_NUMBER": 8, "ORDER": 1, "TYPE_FULL_NAME": "com.example.Document"}},
|
|
19
|
+
{"id": "7001", "label": "NAMESPACE", "properties": {"NAME": "com.example", "ORDER": 1}},
|
|
20
|
+
{"id": "8001", "label": "META_DATA", "properties": {"LANGUAGE": "java", "VERSION": "1.0"}}
|
|
21
|
+
]
|
|
@@ -7,7 +7,7 @@ packages = ["src/codeproperty_graph"]
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "graphrag-codeproperty-graph"
|
|
10
|
-
version = "0.4.
|
|
10
|
+
version = "0.4.5"
|
|
11
11
|
description = "Code Property Graphs — Joern/Semgrep extraction, AI vulnerability enrichment, risk scoring"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.11"
|
|
@@ -22,22 +22,20 @@ classifiers = [
|
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
dependencies = [
|
|
25
|
-
|
|
26
|
-
"boto3>=1.26.0",
|
|
25
|
+
"graphrag-document-graph>=3.1.1",
|
|
27
26
|
]
|
|
28
27
|
|
|
29
28
|
[project.optional-dependencies]
|
|
30
|
-
graphrag = [
|
|
31
|
-
"graphrag-toolkit-lexical-graph>=3.18.0",
|
|
32
|
-
]
|
|
33
29
|
dev = [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
"pytest>=7.0",
|
|
31
|
+
"pytest-cov>=4.0",
|
|
32
|
+
"pytest-asyncio>=0.21.0",
|
|
33
|
+
"black",
|
|
34
|
+
"ruff",
|
|
37
35
|
]
|
|
38
36
|
|
|
39
37
|
[project.urls]
|
|
40
|
-
Repository = "https://github.com/
|
|
38
|
+
Repository = "https://github.com/kanjani-ai-research/codeproperty-graph"
|
|
41
39
|
|
|
42
40
|
[tool.pytest.ini_options]
|
|
43
41
|
testpaths = ["tests"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/ARCHITECTURE.md
RENAMED
|
File without changes
|
|
File without changes
|
{graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/docs/HYBRID-GRAPH.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphrag_codeproperty_graph-0.4.3 → graphrag_codeproperty_graph-0.4.5}/sbom/AWS-SERVICES.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|