entrygraph 0.1.1__tar.gz → 0.1.3__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.
- entrygraph-0.1.3/.github/workflows/ci.yml +77 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/.github/workflows/release.yml +2 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/PKG-INFO +14 -10
- {entrygraph-0.1.1 → entrygraph-0.1.3}/README.md +6 -6
- {entrygraph-0.1.1 → entrygraph-0.1.3}/RELEASING.md +14 -14
- {entrygraph-0.1.1 → entrygraph-0.1.3}/pyproject.toml +46 -4
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/_version.py +2 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/api.py +71 -45
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/cli/main.py +75 -33
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/cli/render.py +29 -11
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/db/meta.py +2 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/db/models.py +6 -5
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/base.py +2 -3
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/configs.py +31 -10
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/csharp.py +1 -3
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/golang.py +45 -26
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/java.py +53 -35
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/javascript.py +89 -22
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/php.py +19 -12
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/python.py +159 -63
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/ruby.py +42 -24
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/rust.py +27 -16
- entrygraph-0.1.3/src/entrygraph/detect/frameworks.py +593 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/manifests.py +13 -9
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/taint.py +2 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/base.py +3 -3
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/csharp.py +28 -48
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/golang.py +33 -21
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/java.py +70 -37
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/javascript.py +151 -55
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/php.py +101 -85
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/python.py +58 -27
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/registry.py +0 -1
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/ruby.py +11 -11
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/rust.py +30 -28
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/fs/hashing.py +1 -5
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/fs/lang.py +1 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/fs/walker.py +27 -8
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/graph/adjacency.py +19 -6
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/graph/cte.py +11 -4
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/pipeline/scanner.py +122 -60
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/pipeline/worker.py +5 -4
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/pipeline/writer.py +3 -3
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/resolve/externals.py +1 -1
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/resolve/hierarchy.py +8 -3
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/resolve/resolver.py +43 -11
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/python/flask_app/app/routes.py +1 -1
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/python/flask_app/cli.py +0 -1
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_api.py +2 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_cli.py +19 -9
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_entrypoint_expansion.py +97 -37
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_entrypoints.py +1 -1
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_csharp.py +7 -8
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_go.py +3 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_java.py +32 -18
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_javascript.py +12 -10
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_php.py +12 -29
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_python.py +7 -8
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_ruby.py +21 -19
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_extract_rust.py +8 -5
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_frameworks.py +16 -7
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_hardening.py +0 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_incremental.py +13 -12
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_indexer.py +29 -14
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_manifests.py +11 -6
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_models.py +85 -27
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_reachability.py +18 -22
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_render.py +1 -2
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_resolver.py +46 -18
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_scoring.py +53 -19
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_taint.py +6 -3
- {entrygraph-0.1.1 → entrygraph-0.1.3}/uv.lock +229 -134
- entrygraph-0.1.1/.github/workflows/ci.yml +0 -27
- entrygraph-0.1.1/src/entrygraph/detect/frameworks.py +0 -369
- {entrygraph-0.1.1 → entrygraph-0.1.3}/.gitignore +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/LICENSE +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/__main__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/cli/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/csharp.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/go.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/java.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/javascript.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/lib_javascript.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/lib_python.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/php.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/python.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/ruby.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/data/sinks/rust.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/db/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/db/engine.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/db/queries.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/detect/entrypoints/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/errors.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/extract/ir.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/fs/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/graph/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/graph/scoring.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/kinds.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/parsing/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/parsing/parsers.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/parsing/queries.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/pipeline/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/py.typed +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/csharp/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/csharp/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/csharp/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/go/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/go/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/go/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/java/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/java/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/java/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/javascript/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/javascript/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/javascript/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/php/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/php/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/php/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/python/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/python/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/python/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/ruby/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/ruby/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/ruby/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/rust/calls.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/rust/definitions.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/queries/rust/imports.scm +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/resolve/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/resolve/symbol_table.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/src/entrygraph/results.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/conftest.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/csharp/aspnet_app/Controllers/ReportsController.cs +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/csharp/aspnet_app/Program.cs +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/csharp/aspnet_app/Services/ReportService.cs +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/csharp/aspnet_app/app.csproj +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/go/gin_app/go.mod +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/go/gin_app/main.go +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/go/gin_app/service.go +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/java/spring_app/pom.xml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/java/spring_app/src/main/java/com/example/Application.java +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportRunner.java +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportService.java +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/java/spring_app/src/main/java/com/example/UserController.java +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/javascript/express_app/package.json +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/javascript/express_app/src/routes.js +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/javascript/express_app/src/services.js +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/php/laravel_app/app/Http/Controllers/ReportController.php +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/php/laravel_app/artisan +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/php/laravel_app/composer.json +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/php/laravel_app/routes/web.php +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/python/flask_app/app/__init__.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/python/flask_app/app/db.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/python/flask_app/app/services.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/python/flask_app/requirements.txt +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/ruby/sinatra_app/Gemfile +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/ruby/sinatra_app/app.rb +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/ruby/sinatra_app/services/runner.rb +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/rust/axum_app/Cargo.toml +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/rust/axum_app/src/handlers.rs +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/fixtures/rust/axum_app/src/main.rs +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_engine_pragmas.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_lang.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_sink_catalog.py +0 -0
- {entrygraph-0.1.1 → entrygraph-0.1.3}/tests/test_walker.py +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# One job per quality gate. main is covered by release.yml's own test gate, so
|
|
4
|
+
# it's excluded here to avoid duplicate runs.
|
|
5
|
+
on:
|
|
6
|
+
pull_request: {}
|
|
7
|
+
push:
|
|
8
|
+
branches-ignore: [main]
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
format:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
- name: ruff format --check
|
|
23
|
+
run: uv run --extra dev ruff format --check .
|
|
24
|
+
|
|
25
|
+
lint:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: astral-sh/setup-uv@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.13"
|
|
32
|
+
- name: ruff check
|
|
33
|
+
run: uv run --extra dev ruff check .
|
|
34
|
+
|
|
35
|
+
typecheck:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: astral-sh/setup-uv@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.13"
|
|
42
|
+
- name: mypy
|
|
43
|
+
run: uv run --extra dev mypy
|
|
44
|
+
|
|
45
|
+
test:
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
strategy:
|
|
48
|
+
fail-fast: false
|
|
49
|
+
matrix:
|
|
50
|
+
python-version: ["3.13", "3.14"]
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
- uses: astral-sh/setup-uv@v5
|
|
54
|
+
with:
|
|
55
|
+
python-version: ${{ matrix.python-version }}
|
|
56
|
+
- name: pytest + coverage
|
|
57
|
+
run: uv run --extra dev pytest -q --cov=entrygraph --cov-report=term-missing
|
|
58
|
+
|
|
59
|
+
security:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
- uses: astral-sh/setup-uv@v5
|
|
64
|
+
with:
|
|
65
|
+
python-version: "3.13"
|
|
66
|
+
- name: bandit
|
|
67
|
+
run: uv run --extra dev bandit -c pyproject.toml -r src/entrygraph -q
|
|
68
|
+
|
|
69
|
+
mdformat:
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
- uses: astral-sh/setup-uv@v5
|
|
74
|
+
with:
|
|
75
|
+
python-version: "3.13"
|
|
76
|
+
- name: mdformat --check
|
|
77
|
+
run: uv run --extra dev mdformat --check $(git ls-files '*.md')
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
- uses: actions/checkout@v4
|
|
20
20
|
- uses: astral-sh/setup-uv@v5
|
|
21
21
|
with:
|
|
22
|
-
python-version: "3.
|
|
22
|
+
python-version: "3.13"
|
|
23
23
|
- name: Run test suite
|
|
24
24
|
run: uv run --extra dev pytest -q
|
|
25
25
|
|
|
@@ -69,7 +69,7 @@ jobs:
|
|
|
69
69
|
- uses: astral-sh/setup-uv@v5
|
|
70
70
|
if: steps.ver.outputs.release == 'true'
|
|
71
71
|
with:
|
|
72
|
-
python-version: "3.
|
|
72
|
+
python-version: "3.13"
|
|
73
73
|
|
|
74
74
|
- name: Build sdist + wheel
|
|
75
75
|
if: steps.ver.outputs.release == 'true'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: entrygraph
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Language-agnostic code graph: query symbols, entrypoints, and source-to-sink call paths from a SQLite index
|
|
5
5
|
Project-URL: Repository, https://github.com/brettbergin/entrygraph
|
|
6
6
|
Author-email: Brett Bergin <brettberginbc@yahoo.com>
|
|
@@ -30,19 +30,23 @@ Keywords: call-graph,code-analysis,entrypoints,static-analysis,tree-sitter
|
|
|
30
30
|
Classifier: Development Status :: 3 - Alpha
|
|
31
31
|
Classifier: Intended Audience :: Developers
|
|
32
32
|
Classifier: Programming Language :: Python :: 3
|
|
33
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
34
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
35
33
|
Classifier: Programming Language :: Python :: 3.13
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
36
35
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
37
|
-
Requires-Python: >=3.
|
|
36
|
+
Requires-Python: >=3.13
|
|
38
37
|
Requires-Dist: pathspec<1.0,>=0.12
|
|
39
38
|
Requires-Dist: rich>=13.0
|
|
40
39
|
Requires-Dist: sqlalchemy<3.0,>=2.0.30
|
|
41
40
|
Requires-Dist: tree-sitter-language-pack>=1.10
|
|
42
41
|
Requires-Dist: tree-sitter<0.26,>=0.25
|
|
43
42
|
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: bandit>=1.7; extra == 'dev'
|
|
44
|
+
Requires-Dist: mdformat-gfm>=0.3; extra == 'dev'
|
|
45
|
+
Requires-Dist: mdformat>=0.7; extra == 'dev'
|
|
46
|
+
Requires-Dist: mypy<2,>=1.11; extra == 'dev'
|
|
44
47
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
45
48
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
46
50
|
Description-Content-Type: text/markdown
|
|
47
51
|
|
|
48
52
|
# entrygraph
|
|
@@ -70,7 +74,7 @@ Dockerfile).
|
|
|
70
74
|
pip install entrygraph # or: uv pip install entrygraph
|
|
71
75
|
```
|
|
72
76
|
|
|
73
|
-
Requires Python ≥ 3.
|
|
77
|
+
Requires Python ≥ 3.13. Installs the `entrygraph` command (you can also run it as
|
|
74
78
|
`uv run entrygraph …` or `python -m entrygraph …`).
|
|
75
79
|
|
|
76
80
|
## Quick start (CLI)
|
|
@@ -305,20 +309,20 @@ results are safe to hold and trivial to serialize.
|
|
|
305
309
|
1. **Walk** — `os.scandir` with hard-pruned junk dirs (`node_modules`, `.venv`,
|
|
306
310
|
…), `.gitignore` rules, and size/binary/minified gates. Every skip is recorded
|
|
307
311
|
with a reason.
|
|
308
|
-
|
|
312
|
+
1. **Extract** — tree-sitter `.scm` queries harvest definitions/imports/calls;
|
|
309
313
|
small per-language "shaper" modules build qualified names, import maps, and
|
|
310
314
|
receiver info. Parsing runs across a process pool for large repos.
|
|
311
|
-
|
|
315
|
+
1. **Resolve** — a two-pass resolver binds references to symbols with a
|
|
312
316
|
confidence level (`exact` / `import` / `fuzzy` / `unresolved`). External
|
|
313
317
|
callees (`subprocess.run`, `child_process.exec`, …) become placeholder nodes
|
|
314
318
|
so sinks are real graph terminals.
|
|
315
|
-
|
|
319
|
+
1. **Detect** — frameworks are scored from manifest dependencies plus code
|
|
316
320
|
signals (noisy-or); entrypoint rules map framework patterns to route/command
|
|
317
321
|
records.
|
|
318
|
-
|
|
322
|
+
1. **Store** — everything persists to SQLite via the SQLAlchemy 2.0 ORM with
|
|
319
323
|
bulk inserts and app-assigned keys. Re-indexing is incremental and
|
|
320
324
|
content-hash driven.
|
|
321
|
-
|
|
325
|
+
1. **Query** — reachability runs over an in-memory adjacency cache (BFS/DFS with
|
|
322
326
|
cycle handling); a recursive-CTE SQL engine is available as a fallback
|
|
323
327
|
(`engine="sql"`).
|
|
324
328
|
|
|
@@ -23,7 +23,7 @@ Dockerfile).
|
|
|
23
23
|
pip install entrygraph # or: uv pip install entrygraph
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Requires Python ≥ 3.
|
|
26
|
+
Requires Python ≥ 3.13. Installs the `entrygraph` command (you can also run it as
|
|
27
27
|
`uv run entrygraph …` or `python -m entrygraph …`).
|
|
28
28
|
|
|
29
29
|
## Quick start (CLI)
|
|
@@ -258,20 +258,20 @@ results are safe to hold and trivial to serialize.
|
|
|
258
258
|
1. **Walk** — `os.scandir` with hard-pruned junk dirs (`node_modules`, `.venv`,
|
|
259
259
|
…), `.gitignore` rules, and size/binary/minified gates. Every skip is recorded
|
|
260
260
|
with a reason.
|
|
261
|
-
|
|
261
|
+
1. **Extract** — tree-sitter `.scm` queries harvest definitions/imports/calls;
|
|
262
262
|
small per-language "shaper" modules build qualified names, import maps, and
|
|
263
263
|
receiver info. Parsing runs across a process pool for large repos.
|
|
264
|
-
|
|
264
|
+
1. **Resolve** — a two-pass resolver binds references to symbols with a
|
|
265
265
|
confidence level (`exact` / `import` / `fuzzy` / `unresolved`). External
|
|
266
266
|
callees (`subprocess.run`, `child_process.exec`, …) become placeholder nodes
|
|
267
267
|
so sinks are real graph terminals.
|
|
268
|
-
|
|
268
|
+
1. **Detect** — frameworks are scored from manifest dependencies plus code
|
|
269
269
|
signals (noisy-or); entrypoint rules map framework patterns to route/command
|
|
270
270
|
records.
|
|
271
|
-
|
|
271
|
+
1. **Store** — everything persists to SQLite via the SQLAlchemy 2.0 ORM with
|
|
272
272
|
bulk inserts and app-assigned keys. Re-indexing is incremental and
|
|
273
273
|
content-hash driven.
|
|
274
|
-
|
|
274
|
+
1. **Query** — reachability runs over an in-memory adjacency cache (BFS/DFS with
|
|
275
275
|
cycle handling); a recursive-CTE SQL engine is available as a fallback
|
|
276
276
|
(`engine="sql"`).
|
|
277
277
|
|
|
@@ -7,16 +7,16 @@ PyPI — no manual version edits, no tokens.
|
|
|
7
7
|
## How it works
|
|
8
8
|
|
|
9
9
|
1. A merge lands on `main` → [`.github/workflows/release.yml`](.github/workflows/release.yml) runs.
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
1. The test suite must pass (gate).
|
|
11
|
+
1. The workflow finds the latest `vX.Y.Z` tag and computes the next **patch**
|
|
12
12
|
version (`v0.1.4` → `v0.1.5`). With no tags yet, the first release is `v0.1.0`.
|
|
13
|
-
|
|
13
|
+
1. It creates and pushes that tag. [`hatch-vcs`](https://github.com/ofek/hatch-vcs)
|
|
14
14
|
derives the package version from the tag, so nothing is committed back to `main`.
|
|
15
|
-
|
|
15
|
+
1. `uv build` produces the sdist + wheel, which are published to PyPI via
|
|
16
16
|
**Trusted Publishing (OIDC)** and attached to a GitHub Release.
|
|
17
17
|
|
|
18
18
|
Pull requests are tested separately by [`.github/workflows/ci.yml`](.github/workflows/ci.yml)
|
|
19
|
-
across Python 3.
|
|
19
|
+
across Python 3.13–3.14, so broken code never reaches `main`.
|
|
20
20
|
|
|
21
21
|
## One-time setup (required before the first release)
|
|
22
22
|
|
|
@@ -26,15 +26,15 @@ This lets GitHub Actions publish without an API token. Because the project
|
|
|
26
26
|
doesn't exist on PyPI yet, add it as a **pending** publisher:
|
|
27
27
|
|
|
28
28
|
1. Log in to PyPI → <https://pypi.org/manage/account/publishing/>.
|
|
29
|
-
|
|
30
|
-
| Field
|
|
31
|
-
|
|
|
32
|
-
| PyPI Project Name | `entrygraph`
|
|
33
|
-
| Owner
|
|
34
|
-
| Repository name
|
|
35
|
-
| Workflow name
|
|
36
|
-
| Environment name
|
|
37
|
-
|
|
29
|
+
1. Under "Add a new pending publisher", enter:
|
|
30
|
+
| Field | Value |
|
|
31
|
+
| ----------------- | --------------- |
|
|
32
|
+
| PyPI Project Name | `entrygraph` |
|
|
33
|
+
| Owner | `brettbergin` |
|
|
34
|
+
| Repository name | `entrygraph` |
|
|
35
|
+
| Workflow name | `release.yml` |
|
|
36
|
+
| Environment name | *(leave blank)* |
|
|
37
|
+
1. Save. The first successful run creates the project and binds the publisher.
|
|
38
38
|
|
|
39
39
|
> The PyPI project name `entrygraph` must be available (or already owned by you).
|
|
40
40
|
> If it's taken, rename the project in `pyproject.toml` before the first release.
|
|
@@ -8,16 +8,15 @@ dynamic = ["version"] # derived from git tags by hatch-vcs (see [tool.hatch.ver
|
|
|
8
8
|
description = "Language-agnostic code graph: query symbols, entrypoints, and source-to-sink call paths from a SQLite index"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
11
|
-
requires-python = ">=3.
|
|
11
|
+
requires-python = ">=3.13"
|
|
12
12
|
authors = [{ name = "Brett Bergin", email = "brettberginbc@yahoo.com" }]
|
|
13
13
|
keywords = ["code-analysis", "call-graph", "tree-sitter", "static-analysis", "entrypoints"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 3 - Alpha",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"Programming Language :: Python :: 3",
|
|
18
|
-
"Programming Language :: Python :: 3.11",
|
|
19
|
-
"Programming Language :: Python :: 3.12",
|
|
20
18
|
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Programming Language :: Python :: 3.14",
|
|
21
20
|
"Topic :: Software Development :: Quality Assurance",
|
|
22
21
|
]
|
|
23
22
|
dependencies = [
|
|
@@ -29,7 +28,17 @@ dependencies = [
|
|
|
29
28
|
]
|
|
30
29
|
|
|
31
30
|
[project.optional-dependencies]
|
|
32
|
-
dev = [
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=8.0",
|
|
33
|
+
"pytest-cov",
|
|
34
|
+
"ruff>=0.15",
|
|
35
|
+
# mypy 2.x needs pathspec>=1.0 (imports pathspec.patterns.gitignore), which
|
|
36
|
+
# conflicts with our runtime pin pathspec<1.0; 1.x is compatible.
|
|
37
|
+
"mypy>=1.11,<2",
|
|
38
|
+
"bandit>=1.7",
|
|
39
|
+
"mdformat>=0.7",
|
|
40
|
+
"mdformat-gfm>=0.3",
|
|
41
|
+
]
|
|
33
42
|
|
|
34
43
|
[project.scripts]
|
|
35
44
|
entrygraph = "entrygraph.cli.main:main"
|
|
@@ -55,3 +64,36 @@ packages = ["src/entrygraph"]
|
|
|
55
64
|
[tool.pytest.ini_options]
|
|
56
65
|
testpaths = ["tests"]
|
|
57
66
|
markers = ["slow: performance guardrail tests (deselect with '-m \"not slow\"')"]
|
|
67
|
+
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 100
|
|
70
|
+
target-version = "py313"
|
|
71
|
+
extend-exclude = ["src/entrygraph/_version.py"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint]
|
|
74
|
+
# pyflakes, pycodestyle, isort, pyupgrade, bugbear, comprehensions, simplify
|
|
75
|
+
select = ["F", "E", "W", "I", "UP", "B", "C4", "SIM"]
|
|
76
|
+
ignore = [
|
|
77
|
+
"SIM105", # contextlib.suppress over try/except/pass — try/except is clearer here
|
|
78
|
+
"B905", # zip() without strict= — fine for equal-length paired iterables
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[tool.ruff.lint.per-file-ignores]
|
|
82
|
+
"tests/*" = ["B011", "E402", "SIM"] # tests: allow assert False, late imports, verbose forms
|
|
83
|
+
# B023: the DFS helper closes over loop-scoped stack/on_path but is invoked
|
|
84
|
+
# synchronously within the same iteration, so the late-binding concern is moot.
|
|
85
|
+
"src/entrygraph/graph/adjacency.py" = ["B023"]
|
|
86
|
+
|
|
87
|
+
[tool.mypy]
|
|
88
|
+
python_version = "3.13"
|
|
89
|
+
mypy_path = "src"
|
|
90
|
+
packages = ["entrygraph"]
|
|
91
|
+
ignore_missing_imports = true # tree-sitter, sqlalchemy internals, etc. ship partial stubs
|
|
92
|
+
exclude = ["_version\\.py$"]
|
|
93
|
+
|
|
94
|
+
[tool.bandit]
|
|
95
|
+
# Scan library code only; tests/fixtures contain deliberately vulnerable sample apps.
|
|
96
|
+
exclude_dirs = ["tests", "src/entrygraph/_version.py"]
|
|
97
|
+
# B314/B405: ElementTree parses local project manifests (pom.xml/.csproj) the user is
|
|
98
|
+
# already indexing — not attacker-controlled network XML, so XXE risk is not applicable.
|
|
99
|
+
skips = ["B314", "B405"]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
21
|
+
__version__ = version = '0.1.3'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 3)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -8,8 +8,8 @@ frozen dataclasses; the Session never escapes (except via the explicit
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
|
+
from collections.abc import Iterator
|
|
11
12
|
from pathlib import Path
|
|
12
|
-
from typing import Iterator
|
|
13
13
|
|
|
14
14
|
from sqlalchemy import Engine, func, select, text
|
|
15
15
|
from sqlalchemy.orm import Session
|
|
@@ -23,7 +23,7 @@ from entrygraph.errors import (
|
|
|
23
23
|
RepositoryNotIndexedError,
|
|
24
24
|
SymbolNotFoundError,
|
|
25
25
|
)
|
|
26
|
-
from entrygraph.graph.adjacency import AdjacencyCache
|
|
26
|
+
from entrygraph.graph.adjacency import AdjacencyCache
|
|
27
27
|
from entrygraph.graph.scoring import is_constant_args, score_path
|
|
28
28
|
from entrygraph.kinds import Confidence
|
|
29
29
|
from entrygraph.results import (
|
|
@@ -42,7 +42,7 @@ from entrygraph.results import (
|
|
|
42
42
|
|
|
43
43
|
DEFAULT_DB_NAME = ".entrygraph.db"
|
|
44
44
|
|
|
45
|
-
SourceSpec =
|
|
45
|
+
type SourceSpec = str | Symbol | Entrypoint | list[str | Symbol | Entrypoint]
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def _traversal_params(
|
|
@@ -76,11 +76,12 @@ class CodeGraph:
|
|
|
76
76
|
self._engine = engine
|
|
77
77
|
self._session_factory = make_session_factory(engine)
|
|
78
78
|
self._adjacency: dict[tuple[frozenset[str], int], AdjacencyCache] = {}
|
|
79
|
+
self._last_index_stats: IndexStats | None = None
|
|
79
80
|
|
|
80
81
|
# ---------------- construction ----------------
|
|
81
82
|
|
|
82
83
|
@classmethod
|
|
83
|
-
def index(cls, root: str | Path, db: str | Path | None = None) ->
|
|
84
|
+
def index(cls, root: str | Path, db: str | Path | None = None) -> CodeGraph:
|
|
84
85
|
"""Index (or fully re-index) a repository and return an open graph."""
|
|
85
86
|
from entrygraph.pipeline.scanner import index_repository
|
|
86
87
|
|
|
@@ -92,7 +93,7 @@ class CodeGraph:
|
|
|
92
93
|
return graph
|
|
93
94
|
|
|
94
95
|
@classmethod
|
|
95
|
-
def open(cls, db: str | Path) ->
|
|
96
|
+
def open(cls, db: str | Path) -> CodeGraph:
|
|
96
97
|
db_path = Path(db)
|
|
97
98
|
if not db_path.exists():
|
|
98
99
|
raise DatabaseNotFoundError(f"no index database at {db_path}")
|
|
@@ -103,7 +104,7 @@ class CodeGraph:
|
|
|
103
104
|
def close(self) -> None:
|
|
104
105
|
self._engine.dispose()
|
|
105
106
|
|
|
106
|
-
def __enter__(self) ->
|
|
107
|
+
def __enter__(self) -> CodeGraph:
|
|
107
108
|
return self
|
|
108
109
|
|
|
109
110
|
def __exit__(self, *exc) -> None:
|
|
@@ -124,8 +125,14 @@ class CodeGraph:
|
|
|
124
125
|
) -> list[Symbol]:
|
|
125
126
|
with self._session_factory() as session:
|
|
126
127
|
return q.select_symbols(
|
|
127
|
-
session,
|
|
128
|
-
|
|
128
|
+
session,
|
|
129
|
+
kind=kind,
|
|
130
|
+
name=name,
|
|
131
|
+
qname=qname,
|
|
132
|
+
file=file,
|
|
133
|
+
include_external=include_external,
|
|
134
|
+
limit=limit,
|
|
135
|
+
offset=offset,
|
|
129
136
|
)
|
|
130
137
|
|
|
131
138
|
def symbol(self, qname: str) -> Symbol:
|
|
@@ -152,9 +159,13 @@ class CodeGraph:
|
|
|
152
159
|
|
|
153
160
|
def detect(self) -> DetectionReport:
|
|
154
161
|
with self._session_factory() as session:
|
|
155
|
-
rows =
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
rows = (
|
|
163
|
+
session.execute(
|
|
164
|
+
select(models.Detection).order_by(models.Detection.confidence.desc())
|
|
165
|
+
)
|
|
166
|
+
.scalars()
|
|
167
|
+
.all()
|
|
168
|
+
)
|
|
158
169
|
languages = []
|
|
159
170
|
frameworks = []
|
|
160
171
|
for row in rows:
|
|
@@ -177,7 +188,7 @@ class CodeGraph:
|
|
|
177
188
|
evidence=tuple(evidence.get("signals", [])),
|
|
178
189
|
)
|
|
179
190
|
)
|
|
180
|
-
languages.sort(key=lambda
|
|
191
|
+
languages.sort(key=lambda lang: lang.byte_count, reverse=True)
|
|
181
192
|
return DetectionReport(languages=tuple(languages), frameworks=tuple(frameworks))
|
|
182
193
|
|
|
183
194
|
# ---------------- entrypoints ----------------
|
|
@@ -197,23 +208,27 @@ class CodeGraph:
|
|
|
197
208
|
|
|
198
209
|
# ---------------- traversal ----------------
|
|
199
210
|
|
|
200
|
-
def callers(
|
|
201
|
-
|
|
211
|
+
def callers(
|
|
212
|
+
self, target: SourceSpec, *, depth: int = 1, edge_kinds: tuple[str, ...] = ("calls",)
|
|
213
|
+
) -> list[Symbol]:
|
|
202
214
|
return self._neighbors(target, depth, "in", edge_kinds)
|
|
203
215
|
|
|
204
|
-
def callees(
|
|
205
|
-
|
|
216
|
+
def callees(
|
|
217
|
+
self, target: SourceSpec, *, depth: int = 1, edge_kinds: tuple[str, ...] = ("calls",)
|
|
218
|
+
) -> list[Symbol]:
|
|
206
219
|
return self._neighbors(target, depth, "out", edge_kinds)
|
|
207
220
|
|
|
208
|
-
def references(self, target:
|
|
221
|
+
def references(self, target: SourceSpec) -> list[Edge]:
|
|
209
222
|
"""All inbound edges (any kind) to the matching symbols."""
|
|
210
223
|
with self._session_factory() as session:
|
|
211
224
|
ids = self._spec_to_ids(session, target)
|
|
212
225
|
if not ids:
|
|
213
226
|
return []
|
|
214
|
-
rows =
|
|
215
|
-
select(models.Edge).where(models.Edge.dst_symbol_id.in_(ids))
|
|
216
|
-
|
|
227
|
+
rows = (
|
|
228
|
+
session.execute(select(models.Edge).where(models.Edge.dst_symbol_id.in_(ids)))
|
|
229
|
+
.scalars()
|
|
230
|
+
.all()
|
|
231
|
+
)
|
|
217
232
|
src_map = q.symbols_by_ids(session, {r.src_symbol_id for r in rows})
|
|
218
233
|
return [
|
|
219
234
|
Edge(
|
|
@@ -236,8 +251,8 @@ class CodeGraph:
|
|
|
236
251
|
def paths(
|
|
237
252
|
self,
|
|
238
253
|
*,
|
|
239
|
-
source:
|
|
240
|
-
sink:
|
|
254
|
+
source: SourceSpec,
|
|
255
|
+
sink: SourceSpec | None = None,
|
|
241
256
|
sink_category: str | None = None,
|
|
242
257
|
max_depth: int = 25,
|
|
243
258
|
max_paths: int = 10,
|
|
@@ -282,15 +297,16 @@ class CodeGraph:
|
|
|
282
297
|
for path in raw_paths
|
|
283
298
|
]
|
|
284
299
|
results = [cp for cp in built if not (prune_sanitized and _fully_sanitized(cp))]
|
|
285
|
-
results.sort(
|
|
286
|
-
|
|
300
|
+
results.sort(
|
|
301
|
+
key=lambda p: (-(p.risk_score or 0.0), len(p.symbols), [s.id for s in p.symbols])
|
|
302
|
+
)
|
|
287
303
|
return results
|
|
288
304
|
|
|
289
305
|
def reachable(
|
|
290
306
|
self,
|
|
291
307
|
*,
|
|
292
|
-
source:
|
|
293
|
-
sink:
|
|
308
|
+
source: SourceSpec,
|
|
309
|
+
sink: SourceSpec | None = None,
|
|
294
310
|
sink_category: str | None = None,
|
|
295
311
|
max_depth: int = 25,
|
|
296
312
|
edge_kinds: tuple[str, ...] = ("calls",),
|
|
@@ -320,8 +336,7 @@ class CodeGraph:
|
|
|
320
336
|
repo = session.execute(select(models.Repository)).scalars().first()
|
|
321
337
|
if repo is None:
|
|
322
338
|
raise RepositoryNotIndexedError("database has no indexed repository")
|
|
323
|
-
stats = index_repository(repo.root_path, self._engine, incremental=True,
|
|
324
|
-
paranoid=paranoid)
|
|
339
|
+
stats = index_repository(repo.root_path, self._engine, incremental=True, paranoid=paranoid)
|
|
325
340
|
self._adjacency.clear()
|
|
326
341
|
return stats
|
|
327
342
|
|
|
@@ -341,9 +356,7 @@ class CodeGraph:
|
|
|
341
356
|
symbols=count(select(func.count(models.Symbol.id))),
|
|
342
357
|
edges=count(select(func.count(models.Edge.id))),
|
|
343
358
|
resolved_edges=count(
|
|
344
|
-
select(func.count(models.Edge.id)).where(
|
|
345
|
-
models.Edge.dst_symbol_id.is_not(None)
|
|
346
|
-
)
|
|
359
|
+
select(func.count(models.Edge.id)).where(models.Edge.dst_symbol_id.is_not(None))
|
|
347
360
|
),
|
|
348
361
|
entrypoints=count(select(func.count(models.Entrypoint.id))),
|
|
349
362
|
sink_edges=count(
|
|
@@ -366,8 +379,14 @@ class CodeGraph:
|
|
|
366
379
|
gen = session.execute(select(models.Repository.index_generation)).scalar()
|
|
367
380
|
return gen or 0
|
|
368
381
|
|
|
369
|
-
def _traverser(
|
|
370
|
-
|
|
382
|
+
def _traverser(
|
|
383
|
+
self,
|
|
384
|
+
session: Session,
|
|
385
|
+
engine: str,
|
|
386
|
+
edge_kinds: tuple[str, ...],
|
|
387
|
+
min_confidence: int,
|
|
388
|
+
include_cha: bool = True,
|
|
389
|
+
):
|
|
371
390
|
if engine == "sql":
|
|
372
391
|
from entrygraph.graph.cte import CteEngine
|
|
373
392
|
|
|
@@ -376,8 +395,13 @@ class CodeGraph:
|
|
|
376
395
|
raise ValueError(f"unknown reachability engine {engine!r} (use 'memory' or 'sql')")
|
|
377
396
|
return self._cache(session, edge_kinds, min_confidence, include_cha)
|
|
378
397
|
|
|
379
|
-
def _cache(
|
|
380
|
-
|
|
398
|
+
def _cache(
|
|
399
|
+
self,
|
|
400
|
+
session: Session,
|
|
401
|
+
edge_kinds: tuple[str, ...],
|
|
402
|
+
min_confidence: int,
|
|
403
|
+
include_cha: bool = True,
|
|
404
|
+
) -> AdjacencyCache:
|
|
381
405
|
kinds = frozenset(edge_kinds)
|
|
382
406
|
generation = self._generation(session)
|
|
383
407
|
key = (kinds | {f"minconf:{min_confidence}", f"cha:{include_cha}"}, generation)
|
|
@@ -417,11 +441,12 @@ class CodeGraph:
|
|
|
417
441
|
models.Edge.dst_symbol_id.is_not(None),
|
|
418
442
|
)
|
|
419
443
|
).scalars()
|
|
420
|
-
ids |=
|
|
444
|
+
ids |= {r for r in rows if r is not None}
|
|
421
445
|
return ids
|
|
422
446
|
|
|
423
|
-
def _neighbors(
|
|
424
|
-
|
|
447
|
+
def _neighbors(
|
|
448
|
+
self, target, depth: int, direction: str, edge_kinds: tuple[str, ...]
|
|
449
|
+
) -> list[Symbol]:
|
|
425
450
|
with self._session_factory() as session:
|
|
426
451
|
ids = self._spec_to_ids(session, target)
|
|
427
452
|
if not ids:
|
|
@@ -438,13 +463,13 @@ class CodeGraph:
|
|
|
438
463
|
return registry_for_repo(repo.root_path if repo else None)
|
|
439
464
|
|
|
440
465
|
@staticmethod
|
|
441
|
-
def _edge_rows(session: Session, raw_paths) -> dict[int,
|
|
466
|
+
def _edge_rows(session: Session, raw_paths) -> dict[int, models.Edge]:
|
|
442
467
|
edge_ids = {hop.edge_id for path in raw_paths for _, hop in path if hop and hop.edge_id}
|
|
443
468
|
if not edge_ids:
|
|
444
469
|
return {}
|
|
445
|
-
rows =
|
|
446
|
-
select(models.Edge).where(models.Edge.id.in_(edge_ids))
|
|
447
|
-
)
|
|
470
|
+
rows = (
|
|
471
|
+
session.execute(select(models.Edge).where(models.Edge.id.in_(edge_ids))).scalars().all()
|
|
472
|
+
)
|
|
448
473
|
return {r.id: r for r in rows}
|
|
449
474
|
|
|
450
475
|
@staticmethod
|
|
@@ -522,15 +547,16 @@ class CodeGraph:
|
|
|
522
547
|
|
|
523
548
|
risk = score_path(
|
|
524
549
|
hop_confidences=[h.confidence for h in hops],
|
|
525
|
-
hop_vias=[(
|
|
550
|
+
hop_vias=[(row.via if row is not None else h.via) for row, h in zip(rows, hops)],
|
|
526
551
|
sink_severity=registry.severity_of(sink_id),
|
|
527
552
|
sanitized_effect=sanitized_effect,
|
|
528
553
|
constant_args=terminal_const,
|
|
529
554
|
source_tainted=path[0][0] in tainted_sources,
|
|
530
555
|
)
|
|
531
556
|
may_continue = any(node in excluded_nodes for node, _ in path)
|
|
532
|
-
return CallPath(
|
|
533
|
-
|
|
557
|
+
return CallPath(
|
|
558
|
+
symbols=symbols, edges=tuple(path_edges), risk_score=risk, may_continue=may_continue
|
|
559
|
+
)
|
|
534
560
|
|
|
535
561
|
@staticmethod
|
|
536
562
|
def _path_sanitizers(symbols, rows, registry, sink_category):
|