graphify-php 0.0.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.
- graphify_php-0.0.5/.github/workflows/ci.yml +40 -0
- graphify_php-0.0.5/.github/workflows/pages.yml +62 -0
- graphify_php-0.0.5/.github/workflows/release.yml +72 -0
- graphify_php-0.0.5/.github/workflows/stage-build.yml +99 -0
- graphify_php-0.0.5/.github/workflows/stage-security.yml +146 -0
- graphify_php-0.0.5/.github/workflows/stage-tests.yml +65 -0
- graphify_php-0.0.5/.gitignore +17 -0
- graphify_php-0.0.5/.gitleaks.toml +14 -0
- graphify_php-0.0.5/.graphifyignore +29 -0
- graphify_php-0.0.5/.trufflehog-exclude.txt +19 -0
- graphify_php-0.0.5/LICENSE +201 -0
- graphify_php-0.0.5/PKG-INFO +218 -0
- graphify_php-0.0.5/README.md +197 -0
- graphify_php-0.0.5/docs/_config.yml +3 -0
- graphify_php-0.0.5/docs/architecture.md +121 -0
- graphify_php-0.0.5/docs/assets/css/style.scss +442 -0
- graphify_php-0.0.5/docs/configuration.md +98 -0
- graphify_php-0.0.5/docs/evaluation.md +122 -0
- graphify_php-0.0.5/docs/index.md +60 -0
- graphify_php-0.0.5/docs/install.md +188 -0
- graphify_php-0.0.5/docs/phpstan.md +93 -0
- graphify_php-0.0.5/docs/setup.md +89 -0
- graphify_php-0.0.5/eval/README.md +163 -0
- graphify_php-0.0.5/eval/__init__.py +6 -0
- graphify_php-0.0.5/eval/canaries.py +172 -0
- graphify_php-0.0.5/eval/fixtures/chained.php +30 -0
- graphify_php-0.0.5/eval/fixtures/ctor_body_assign.php +32 -0
- graphify_php-0.0.5/eval/fixtures/declared_property.php +27 -0
- graphify_php-0.0.5/eval/fixtures/dynamic.php +51 -0
- graphify_php-0.0.5/eval/fixtures/handler_shapes.php +124 -0
- graphify_php-0.0.5/eval/fixtures/inheritance.php +29 -0
- graphify_php-0.0.5/eval/fixtures/interfaces.php +34 -0
- graphify_php-0.0.5/eval/fixtures/locals_new.php +28 -0
- graphify_php-0.0.5/eval/fixtures/mail.php +19 -0
- graphify_php-0.0.5/eval/fixtures/nullable_union.php +25 -0
- graphify_php-0.0.5/eval/fixtures/promoted.php +22 -0
- graphify_php-0.0.5/eval/fixtures/same_name_traps.php +56 -0
- graphify_php-0.0.5/eval/fixtures/secrets.php +39 -0
- graphify_php-0.0.5/eval/fixtures/services.yaml +10 -0
- graphify_php-0.0.5/eval/fixtures/traits.php +30 -0
- graphify_php-0.0.5/eval/reference_set.py +694 -0
- graphify_php-0.0.5/eval/score.py +435 -0
- graphify_php-0.0.5/php/README.md +106 -0
- graphify_php-0.0.5/php/autoload.php +27 -0
- graphify_php-0.0.5/php/src/CallGraph/CallEdge.php +91 -0
- graphify_php-0.0.5/php/src/CallGraph/CallEdgeRule.php +61 -0
- graphify_php-0.0.5/php/src/CallGraph/CallEdgeWriter.php +19 -0
- graphify_php-0.0.5/php/src/CallGraph/JsonLinesWriter.php +64 -0
- graphify_php-0.0.5/php/src/CallGraph/MethodCallCollector.php +163 -0
- graphify_php-0.0.5/pyproject.toml +76 -0
- graphify_php-0.0.5/src/graphify_php/__init__.py +144 -0
- graphify_php-0.0.5/src/graphify_php/cli.py +355 -0
- graphify_php-0.0.5/src/graphify_php/config/__init__.py +47 -0
- graphify_php-0.0.5/src/graphify_php/config/class_lookup.py +85 -0
- graphify_php-0.0.5/src/graphify_php/config/discovery.py +55 -0
- graphify_php-0.0.5/src/graphify_php/config/document.py +29 -0
- graphify_php-0.0.5/src/graphify_php/config/emit.py +117 -0
- graphify_php-0.0.5/src/graphify_php/config/environment.py +108 -0
- graphify_php-0.0.5/src/graphify_php/config/facts.py +97 -0
- graphify_php-0.0.5/src/graphify_php/config/messaging.py +142 -0
- graphify_php-0.0.5/src/graphify_php/config/ports.py +23 -0
- graphify_php-0.0.5/src/graphify_php/config/safe_yaml.py +127 -0
- graphify_php-0.0.5/src/graphify_php/config_resolver.py +164 -0
- graphify_php-0.0.5/src/graphify_php/container/__init__.py +50 -0
- graphify_php-0.0.5/src/graphify_php/container/facts.py +178 -0
- graphify_php-0.0.5/src/graphify_php/container/layer.py +71 -0
- graphify_php-0.0.5/src/graphify_php/container/locator.py +74 -0
- graphify_php-0.0.5/src/graphify_php/container/overlay.py +149 -0
- graphify_php-0.0.5/src/graphify_php/container/ports.py +40 -0
- graphify_php-0.0.5/src/graphify_php/container/reader.py +330 -0
- graphify_php-0.0.5/src/graphify_php/container/reasons.py +31 -0
- graphify_php-0.0.5/src/graphify_php/graph_adapter.py +520 -0
- graphify_php-0.0.5/src/graphify_php/ports.py +174 -0
- graphify_php-0.0.5/src/graphify_php/resolver.py +519 -0
- graphify_php-0.0.5/src/graphify_php/shrink.py +413 -0
- graphify_php-0.0.5/src/graphify_php/shrink_cli.py +150 -0
- graphify_php-0.0.5/src/graphify_php/sources/phpstan_source.py +641 -0
- graphify_php-0.0.5/src/graphify_php/sources/route_source.py +105 -0
- graphify_php-0.0.5/src/graphify_php/sources/tree_sitter_source.py +428 -0
- graphify_php-0.0.5/src/graphify_php/status.py +136 -0
- graphify_php-0.0.5/src/graphify_php/syntax/call_sites.py +117 -0
- graphify_php-0.0.5/src/graphify_php/syntax/class_index.py +121 -0
- graphify_php-0.0.5/src/graphify_php/syntax/collectors.py +351 -0
- graphify_php-0.0.5/src/graphify_php/syntax/facts.py +171 -0
- graphify_php-0.0.5/src/graphify_php/syntax/file_scan.py +158 -0
- graphify_php-0.0.5/src/graphify_php/syntax/names.py +49 -0
- graphify_php-0.0.5/src/graphify_php/syntax/nodes.py +209 -0
- graphify_php-0.0.5/src/graphify_php/syntax/routes.py +288 -0
- graphify_php-0.0.5/tests/fixtures/shrink/ckeditor/new_graph.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/ckeditor/new_manifest.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/ckeditor/old_graph.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/ckeditor/old_manifest.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/ckeditor/present.txt +24 -0
- graphify_php-0.0.5/tests/fixtures/shrink/games/new_graph.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/games/new_manifest.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/games/old_graph.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/games/old_manifest.json +1 -0
- graphify_php-0.0.5/tests/fixtures/shrink/games/present.txt +33 -0
- graphify_php-0.0.5/tests/test_adapter_graph.py +337 -0
- graphify_php-0.0.5/tests/test_cli_end_to_end.py +281 -0
- graphify_php-0.0.5/tests/test_cli_gate.py +319 -0
- graphify_php-0.0.5/tests/test_cli_shrink_gate.py +239 -0
- graphify_php-0.0.5/tests/test_config_readers.py +316 -0
- graphify_php-0.0.5/tests/test_config_resolver.py +251 -0
- graphify_php-0.0.5/tests/test_container_integration.py +94 -0
- graphify_php-0.0.5/tests/test_container_layer.py +98 -0
- graphify_php-0.0.5/tests/test_container_overlay.py +138 -0
- graphify_php-0.0.5/tests/test_container_reader.py +309 -0
- graphify_php-0.0.5/tests/test_eval_canaries.py +96 -0
- graphify_php-0.0.5/tests/test_eval_reference_set.py +91 -0
- graphify_php-0.0.5/tests/test_eval_score.py +285 -0
- graphify_php-0.0.5/tests/test_phpstan_integration.py +368 -0
- graphify_php-0.0.5/tests/test_phpstan_source.py +669 -0
- graphify_php-0.0.5/tests/test_resolver_precedence.py +639 -0
- graphify_php-0.0.5/tests/test_shrink.py +680 -0
- graphify_php-0.0.5/tests/test_shrink_cli.py +213 -0
- graphify_php-0.0.5/tests/test_syntax_routes.py +457 -0
- graphify_php-0.0.5/tests/test_syntax_scan.py +576 -0
- graphify_php-0.0.5/tests/test_tree_sitter_source.py +1704 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
# pip-audit answers a question no push can ask: a CVE gets published against
|
|
8
|
+
# a dependency tree that has not changed. The nightly run keeps that check
|
|
9
|
+
# alive on a repository that stands still.
|
|
10
|
+
schedule:
|
|
11
|
+
- cron: '0 3 * * *'
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
# A newer push to the same branch or pull request makes the run in flight
|
|
18
|
+
# pointless, so cancel it.
|
|
19
|
+
concurrency:
|
|
20
|
+
group: ci-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
# Three stages in order: security, tests, build. Jobs inside a stage run in
|
|
24
|
+
# parallel; the stages are sequential, so a leaked secret stops the run before
|
|
25
|
+
# anything is tested and a failing test stops it before anything is built.
|
|
26
|
+
#
|
|
27
|
+
# No smoke stage: tests/test_cli_end_to_end.py already runs the real graphify
|
|
28
|
+
# in-process against a generated PHP project, so a separate end-to-end job
|
|
29
|
+
# would repeat what the test matrix proves on every Python version.
|
|
30
|
+
jobs:
|
|
31
|
+
security:
|
|
32
|
+
uses: ./.github/workflows/stage-security.yml
|
|
33
|
+
|
|
34
|
+
tests:
|
|
35
|
+
needs: security
|
|
36
|
+
uses: ./.github/workflows/stage-tests.yml
|
|
37
|
+
|
|
38
|
+
build:
|
|
39
|
+
needs: tests
|
|
40
|
+
uses: ./.github/workflows/stage-build.yml
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Pages
|
|
2
|
+
|
|
3
|
+
# The site is the Jekyll build of docs/ alone. Only changes to docs/ or to this
|
|
4
|
+
# workflow can change it, so other pushes skip the deploy.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- master
|
|
9
|
+
paths:
|
|
10
|
+
- "docs/**"
|
|
11
|
+
- ".github/workflows/pages.yml"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
# Default deny; each job below requests only what it needs.
|
|
15
|
+
permissions: {}
|
|
16
|
+
|
|
17
|
+
# One deployment at a time, and never cancel one in flight: a cancelled deploy
|
|
18
|
+
# can leave the site half-published. Queued runs still collapse.
|
|
19
|
+
concurrency:
|
|
20
|
+
group: pages
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
steps:
|
|
29
|
+
# Always master: a manual dispatch from another branch would otherwise
|
|
30
|
+
# publish unreviewed docs to the public site.
|
|
31
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
32
|
+
with:
|
|
33
|
+
ref: master
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
|
|
36
|
+
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
|
|
37
|
+
|
|
38
|
+
- name: Build the site
|
|
39
|
+
# docs/kb is a symlink to a private vault; _config.yml excludes it so
|
|
40
|
+
# the build cannot publish it even if the link is ever committed.
|
|
41
|
+
uses: actions/jekyll-build-pages@44a6e6beabd48582f863aeeb6cb2151cc1716697 # v1.0.13
|
|
42
|
+
with:
|
|
43
|
+
source: ./docs
|
|
44
|
+
destination: ./_site
|
|
45
|
+
|
|
46
|
+
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
|
47
|
+
with:
|
|
48
|
+
path: ./_site
|
|
49
|
+
|
|
50
|
+
deploy:
|
|
51
|
+
needs: build
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
permissions:
|
|
54
|
+
pages: write
|
|
55
|
+
id-token: write
|
|
56
|
+
contents: read
|
|
57
|
+
environment:
|
|
58
|
+
name: github-pages
|
|
59
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
60
|
+
steps:
|
|
61
|
+
- id: deployment
|
|
62
|
+
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
# Default deny; each job below requests only what it needs.
|
|
9
|
+
permissions: {}
|
|
10
|
+
|
|
11
|
+
# No `concurrency` block, unlike ci.yml: cancelling a publish half-way is worse
|
|
12
|
+
# than paying for the runner.
|
|
13
|
+
|
|
14
|
+
# The same three stage workflows ci.yml runs, with the publish on top, so a
|
|
15
|
+
# release passes exactly the checks a pull request does.
|
|
16
|
+
jobs:
|
|
17
|
+
security:
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
uses: ./.github/workflows/stage-security.yml
|
|
21
|
+
|
|
22
|
+
tests:
|
|
23
|
+
needs: security
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
uses: ./.github/workflows/stage-tests.yml
|
|
27
|
+
|
|
28
|
+
# stage-build takes a bare PEP 440 version, not a tag, so that it stays usable
|
|
29
|
+
# from ci.yml where no tag exists. Stripping the leading "v" needs a shell:
|
|
30
|
+
# GitHub's expression syntax has no substring operator.
|
|
31
|
+
version:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
permissions: {}
|
|
34
|
+
outputs:
|
|
35
|
+
value: ${{ steps.strip.outputs.value }}
|
|
36
|
+
steps:
|
|
37
|
+
- id: strip
|
|
38
|
+
env:
|
|
39
|
+
TAG: ${{ github.ref_name }}
|
|
40
|
+
run: echo "value=${TAG#v}" >> "$GITHUB_OUTPUT"
|
|
41
|
+
|
|
42
|
+
build:
|
|
43
|
+
needs: [tests, version]
|
|
44
|
+
permissions:
|
|
45
|
+
contents: read
|
|
46
|
+
uses: ./.github/workflows/stage-build.yml
|
|
47
|
+
with:
|
|
48
|
+
# Turns on the check that the version hatch-vcs computed from git history
|
|
49
|
+
# equals the tag being released.
|
|
50
|
+
expected_version: ${{ needs.version.outputs.value }}
|
|
51
|
+
|
|
52
|
+
# Trusted publishing: PyPI trusts this workflow and the `pypi` environment
|
|
53
|
+
# through OIDC, so no API token is stored in the repository.
|
|
54
|
+
pypi:
|
|
55
|
+
needs: build
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
environment: pypi
|
|
58
|
+
permissions:
|
|
59
|
+
id-token: write
|
|
60
|
+
steps:
|
|
61
|
+
# The files stage-build produced and installed from a clean venv. Nothing
|
|
62
|
+
# is rebuilt here: a second build could differ from the one the gates ran.
|
|
63
|
+
- name: Download dist artifacts
|
|
64
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
65
|
+
with:
|
|
66
|
+
name: dist
|
|
67
|
+
path: dist
|
|
68
|
+
|
|
69
|
+
- name: Publish to PyPI
|
|
70
|
+
run: |
|
|
71
|
+
python3 -m pip install "uv==0.12.5"
|
|
72
|
+
python3 -m uv publish --trusted-publishing always dist/*
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
name: stage-build
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
expected_version:
|
|
7
|
+
description: >
|
|
8
|
+
PEP 440 version the built wheel must carry, without a leading "v".
|
|
9
|
+
Empty (the default) skips the check: a pull request has no tag.
|
|
10
|
+
type: string
|
|
11
|
+
default: ""
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
# Every other job installs with `pip install -e .`, which imports straight
|
|
18
|
+
# from src/ and never builds anything. A package left out of
|
|
19
|
+
# [tool.hatch.build.targets.wheel] or a console script removed from
|
|
20
|
+
# [project.scripts] produces a wheel that installs cleanly and does nothing.
|
|
21
|
+
# This job is the gate for that.
|
|
22
|
+
build:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
28
|
+
with:
|
|
29
|
+
# hatch-vcs derives the version from `git describe`. A shallow clone
|
|
30
|
+
# has no tags and would build the fallback version instead.
|
|
31
|
+
fetch-depth: 0
|
|
32
|
+
|
|
33
|
+
- name: Set up Python
|
|
34
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.12"
|
|
37
|
+
|
|
38
|
+
- name: Build sdist + wheel
|
|
39
|
+
run: |
|
|
40
|
+
python -m pip install --upgrade build
|
|
41
|
+
python -m build
|
|
42
|
+
|
|
43
|
+
- name: Built version matches the release tag
|
|
44
|
+
# PyPI never accepts the same version twice, so a wheel that does not
|
|
45
|
+
# carry the tag's version must stop here, before anything ships.
|
|
46
|
+
if: inputs.expected_version != ''
|
|
47
|
+
env:
|
|
48
|
+
EXPECTED: ${{ inputs.expected_version }}
|
|
49
|
+
run: |
|
|
50
|
+
built=$(ls dist/*.whl | grep -oP 'graphify_php-\K[^-]+(?=-py3)')
|
|
51
|
+
echo "expected=${EXPECTED} built=${built}"
|
|
52
|
+
if [ "$built" != "$EXPECTED" ]; then
|
|
53
|
+
echo "::error::Built version '${built}' does not match the tag version '${EXPECTED}'."
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
- name: Install the built wheel into a clean venv and run the entry point
|
|
58
|
+
# A fresh venv outside the checkout has no src/ on sys.path, so the
|
|
59
|
+
# import and the console script can only resolve against what the wheel
|
|
60
|
+
# shipped. Run from $RUNNER_TEMP for the same reason.
|
|
61
|
+
#
|
|
62
|
+
# The wheel is installed alone, without graphify, so `--help` proves
|
|
63
|
+
# the console script and the package import stand on their own. A real
|
|
64
|
+
# build still needs graphify, which the test matrix covers.
|
|
65
|
+
run: |
|
|
66
|
+
set -euo pipefail
|
|
67
|
+
python -m venv "$RUNNER_TEMP/wheelenv"
|
|
68
|
+
"$RUNNER_TEMP/wheelenv/bin/pip" install --upgrade pip
|
|
69
|
+
"$RUNNER_TEMP/wheelenv/bin/pip" install dist/*.whl
|
|
70
|
+
cd "$RUNNER_TEMP"
|
|
71
|
+
"$RUNNER_TEMP/wheelenv/bin/python" -c "import graphify_php; print(graphify_php.__name__)"
|
|
72
|
+
"$RUNNER_TEMP/wheelenv/bin/graphify-php-build" --help
|
|
73
|
+
|
|
74
|
+
- name: The wheel carries the PHPStan extension
|
|
75
|
+
# Without php/ in the wheel the PHPStan source finds no autoload file and
|
|
76
|
+
# drops out quietly, leaving tree-sitter alone. The lookup must resolve
|
|
77
|
+
# inside the installed package, not in this checkout.
|
|
78
|
+
run: |
|
|
79
|
+
set -euo pipefail
|
|
80
|
+
cd "$RUNNER_TEMP"
|
|
81
|
+
"$RUNNER_TEMP/wheelenv/bin/python" - <<'PY'
|
|
82
|
+
from graphify_php.sources.phpstan_source import extension_autoload_file
|
|
83
|
+
|
|
84
|
+
path = extension_autoload_file()
|
|
85
|
+
assert path is not None, "php/autoload.php is missing from the wheel"
|
|
86
|
+
assert "site-packages" in str(path), path
|
|
87
|
+
assert (path.parent / "src" / "CallGraph").is_dir(), path
|
|
88
|
+
print(path)
|
|
89
|
+
PY
|
|
90
|
+
|
|
91
|
+
- name: Upload dist artifacts
|
|
92
|
+
# release.yml publishes these files rather than building its own copy,
|
|
93
|
+
# so what a release ships is the artifact this job just proved
|
|
94
|
+
# installable.
|
|
95
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
96
|
+
with:
|
|
97
|
+
name: dist
|
|
98
|
+
path: dist/
|
|
99
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
name: stage-security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
gitleaks:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- name: gitleaks — scan git history for hardcoded secrets
|
|
20
|
+
# Plain OSS gitleaks CLI, not gitleaks/gitleaks-action: that Action now
|
|
21
|
+
# requires a paid GITLEAKS_LICENSE for GitHub Organizations. The CLI
|
|
22
|
+
# itself stays free/open-source.
|
|
23
|
+
# The sha256 below is pinned from the official release checksums file:
|
|
24
|
+
# https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_checksums.txt
|
|
25
|
+
# .gitleaks.toml at the repository root extends the default rules with
|
|
26
|
+
# a path allowlist for the planted canaries (see that file).
|
|
27
|
+
run: |
|
|
28
|
+
curl -sSfL -o gitleaks_8.30.1_linux_x64.tar.gz \
|
|
29
|
+
https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz
|
|
30
|
+
echo "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb gitleaks_8.30.1_linux_x64.tar.gz" | sha256sum -c -
|
|
31
|
+
tar -xzf gitleaks_8.30.1_linux_x64.tar.gz gitleaks
|
|
32
|
+
./gitleaks detect --source . --no-banner --redact
|
|
33
|
+
|
|
34
|
+
trufflehog:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
permissions:
|
|
37
|
+
contents: read
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
40
|
+
with:
|
|
41
|
+
fetch-depth: 0
|
|
42
|
+
|
|
43
|
+
# The pull-request scan reports the `unknown` result class, the history
|
|
44
|
+
# scan does not. An `unknown` result is a string that looks like a
|
|
45
|
+
# credential and cannot be verified. On a pull request it is in the diff
|
|
46
|
+
# under review and can still be changed; in old history it can only be
|
|
47
|
+
# silenced by rewriting the commits that contain it, which would fail this
|
|
48
|
+
# job on every future run of a repository that leaks nothing.
|
|
49
|
+
#
|
|
50
|
+
# Both scans report every verified result, from every detector. A real
|
|
51
|
+
# leak fails this job wherever it lands.
|
|
52
|
+
- name: trufflehog — scan PR commits for verified and unknown secrets (pull_request)
|
|
53
|
+
if: github.event_name == 'pull_request'
|
|
54
|
+
uses: trufflesecurity/trufflehog@363923b901c911a9164f50b6c423f47c15372b1c # v3.97.4
|
|
55
|
+
with:
|
|
56
|
+
path: ./
|
|
57
|
+
base: ${{ github.event.pull_request.base.sha }}
|
|
58
|
+
head: ${{ github.event.pull_request.head.sha }}
|
|
59
|
+
# --exclude-paths names the two files that plant fake credentials on
|
|
60
|
+
# purpose; .trufflehog-exclude.txt explains which and why. Excluding
|
|
61
|
+
# those paths is what keeps the `unknown` class switched on for every
|
|
62
|
+
# other file in the diff.
|
|
63
|
+
extra_args: --results=verified,unknown --exclude-paths=.trufflehog-exclude.txt
|
|
64
|
+
|
|
65
|
+
# Runs on push, and on the nightly schedule and manual dispatch too.
|
|
66
|
+
- name: trufflehog — scan full git history for verified secrets
|
|
67
|
+
if: github.event_name != 'pull_request'
|
|
68
|
+
uses: trufflesecurity/trufflehog@363923b901c911a9164f50b6c423f47c15372b1c # v3.97.4
|
|
69
|
+
with:
|
|
70
|
+
path: ./
|
|
71
|
+
base: ""
|
|
72
|
+
head: ${{ github.ref_name }}
|
|
73
|
+
# No --exclude-paths here: this scan reports only verified results,
|
|
74
|
+
# and a verified credential must fail the job whatever file it sits
|
|
75
|
+
# in, canary fixture included.
|
|
76
|
+
extra_args: --results=verified
|
|
77
|
+
|
|
78
|
+
pip-audit:
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
permissions:
|
|
81
|
+
contents: read
|
|
82
|
+
steps:
|
|
83
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
84
|
+
|
|
85
|
+
- name: Set up Python
|
|
86
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
87
|
+
with:
|
|
88
|
+
python-version: "3.12"
|
|
89
|
+
cache: "pip"
|
|
90
|
+
|
|
91
|
+
- name: Install graphify-php with test and lint extras
|
|
92
|
+
# The package has no runtime dependencies; the test extra is what pulls
|
|
93
|
+
# graphifyy and its tree-sitter grammars, so this is the tree worth
|
|
94
|
+
# auditing.
|
|
95
|
+
run: pip install -e ".[test,lint]"
|
|
96
|
+
|
|
97
|
+
- name: pip-audit — audit installed Python dependencies for known CVEs
|
|
98
|
+
# pip-audit exits 1 when it finds known vulnerabilities — that MUST
|
|
99
|
+
# fail the job. Only other nonzero exit codes (infra/network errors,
|
|
100
|
+
# e.g. the PyPI advisory DB being unreachable) are downgraded to a
|
|
101
|
+
# logged warning so a transient hiccup doesn't block the pipeline.
|
|
102
|
+
run: |
|
|
103
|
+
pip install pip-audit
|
|
104
|
+
set +e
|
|
105
|
+
pip-audit
|
|
106
|
+
code=$?
|
|
107
|
+
set -e
|
|
108
|
+
case "$code" in
|
|
109
|
+
0)
|
|
110
|
+
echo "pip-audit: no known vulnerabilities found."
|
|
111
|
+
;;
|
|
112
|
+
1)
|
|
113
|
+
echo "::error::pip-audit found known vulnerabilities in installed dependencies."
|
|
114
|
+
exit 1
|
|
115
|
+
;;
|
|
116
|
+
*)
|
|
117
|
+
echo "::warning::pip-audit exited with code $code (infra/network error, not a vulnerability finding); not failing the job."
|
|
118
|
+
;;
|
|
119
|
+
esac
|
|
120
|
+
|
|
121
|
+
php-lint:
|
|
122
|
+
runs-on: ubuntu-latest
|
|
123
|
+
permissions:
|
|
124
|
+
contents: read
|
|
125
|
+
steps:
|
|
126
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
127
|
+
|
|
128
|
+
- name: Set up PHP
|
|
129
|
+
# The SHA is the one symfony/symfony's own workflows pin for 2.37.2; it
|
|
130
|
+
# was copied from a local checkout, not verified against the release
|
|
131
|
+
# page from this machine.
|
|
132
|
+
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2
|
|
133
|
+
with:
|
|
134
|
+
# php/README.md verifies the collector against PHP 8.4.
|
|
135
|
+
php-version: "8.4"
|
|
136
|
+
coverage: none
|
|
137
|
+
tools: none
|
|
138
|
+
|
|
139
|
+
- name: php -l — syntax-check the PHPStan collector
|
|
140
|
+
# The Python suite only exercises php/ through PHPStan, which CI does
|
|
141
|
+
# not have (see stage-tests.yml), so this is the one check that runs
|
|
142
|
+
# against the PHP files on every push.
|
|
143
|
+
run: |
|
|
144
|
+
set -euo pipefail
|
|
145
|
+
find php/src -name '*.php' -print0 | xargs -0 -n1 php -l
|
|
146
|
+
php -l php/autoload.php
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: stage-tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
cache: "pip"
|
|
22
|
+
|
|
23
|
+
- name: Install graphify-php with lint extras
|
|
24
|
+
run: pip install -e ".[lint]"
|
|
25
|
+
|
|
26
|
+
- name: ruff — lint
|
|
27
|
+
run: ruff check .
|
|
28
|
+
|
|
29
|
+
- name: ruff — format check
|
|
30
|
+
run: ruff format --check .
|
|
31
|
+
|
|
32
|
+
test:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
permissions:
|
|
35
|
+
contents: read
|
|
36
|
+
strategy:
|
|
37
|
+
# One Python version failing must not hide the state of the others.
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
44
|
+
|
|
45
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
46
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
47
|
+
with:
|
|
48
|
+
python-version: ${{ matrix.python-version }}
|
|
49
|
+
cache: "pip"
|
|
50
|
+
|
|
51
|
+
- name: Install graphify-php with test extras
|
|
52
|
+
# graphifyy brings tree-sitter and tree-sitter-php with it; without
|
|
53
|
+
# them syntax/nodes.py sets the parser modules to None and the
|
|
54
|
+
# tree-sitter tests fail rather than skip.
|
|
55
|
+
run: pip install -e ".[test]"
|
|
56
|
+
|
|
57
|
+
- name: Run the test suite
|
|
58
|
+
# Two files skip themselves here because GRAPHIFY_PHP_REFERENCE_PROJECT
|
|
59
|
+
# is unset: tests/test_phpstan_integration.py needs
|
|
60
|
+
# <project>/vendor/bin/phpstan plus php on PATH, and
|
|
61
|
+
# tests/test_container_integration.py needs that project's compiled
|
|
62
|
+
# container at <project>/var/cache/dev/App_KernelDevDebugContainer.xml.
|
|
63
|
+
# Running them in CI would need a checked-out Symfony project with a
|
|
64
|
+
# composer-installed PHPStan to point the variable at.
|
|
65
|
+
run: python -m pytest -rs
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.pytest_cache/
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Written by the resolver during a build and read by the wrapper's publish gate.
|
|
9
|
+
# It is per-build state, not source: committing it would let yesterday's record
|
|
10
|
+
# satisfy today's gate, which is the failure the gate exists to catch.
|
|
11
|
+
.graphify-php-status.json
|
|
12
|
+
php-call-graph.jsonl
|
|
13
|
+
graphify-out
|
|
14
|
+
graphify-out/
|
|
15
|
+
docs/kb
|
|
16
|
+
.serena
|
|
17
|
+
CLAUDE.md
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Keeps gitleaks' default rule set and allowlists the two files that plant fake
|
|
2
|
+
# credentials on purpose: eval/fixtures/secrets.php and eval/canaries.py (see
|
|
3
|
+
# .trufflehog-exclude.txt for what they hold and why). Allowlisting by path
|
|
4
|
+
# rather than by .gitleaksignore fingerprint, because a fingerprint names one
|
|
5
|
+
# commit and one line, and every edit to those files would mint a new one.
|
|
6
|
+
[extend]
|
|
7
|
+
useDefault = true
|
|
8
|
+
|
|
9
|
+
[[allowlists]]
|
|
10
|
+
description = "Planted CANARY values that tests/test_eval_canaries.py checks never leak"
|
|
11
|
+
paths = [
|
|
12
|
+
'''^eval/fixtures/secrets\.php$''',
|
|
13
|
+
'''^eval/canaries\.py$''',
|
|
14
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
.idea/
|
|
2
|
+
.claude/
|
|
3
|
+
.github/
|
|
4
|
+
.git/
|
|
5
|
+
.cache/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
.serena/
|
|
8
|
+
|
|
9
|
+
docs/
|
|
10
|
+
eval/fixtures/
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
|
|
14
|
+
graphify-out
|
|
15
|
+
graphify-out/
|
|
16
|
+
|
|
17
|
+
*.png
|
|
18
|
+
*.jpg
|
|
19
|
+
*.jpeg
|
|
20
|
+
*.gif
|
|
21
|
+
*.svg
|
|
22
|
+
*.generated.py
|
|
23
|
+
*.min.js
|
|
24
|
+
*.pem
|
|
25
|
+
.env
|
|
26
|
+
.env.*
|
|
27
|
+
.npmrc
|
|
28
|
+
auth.json
|
|
29
|
+
LICENSE
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Paths the pull-request trufflehog scan must not read. One Go regex per line,
|
|
2
|
+
# matched against the file path relative to the repository root. Only the
|
|
3
|
+
# pull-request scan uses this file; the full-history scan reports verified
|
|
4
|
+
# results from every path, these two included.
|
|
5
|
+
#
|
|
6
|
+
# eval/fixtures/secrets.php plants fake credentials — an API token constant, a
|
|
7
|
+
# DSN with an inline password, an environment-variable fallback and an
|
|
8
|
+
# attribute argument — and eval/canaries.py lists the same values so that
|
|
9
|
+
# tests/test_eval_canaries.py can prove none of them ever reaches a graph, a
|
|
10
|
+
# status record or a log. Every value carries the marker CANARY, points at a
|
|
11
|
+
# host that does not exist and opens nothing. Without this exclusion trufflehog
|
|
12
|
+
# reports them as unknown-class results and fails the job on a repository that
|
|
13
|
+
# leaks nothing. The values are deliberately not repeated here, so that
|
|
14
|
+
# excluding them does not turn this file into the next finding.
|
|
15
|
+
#
|
|
16
|
+
# Only these two paths are excluded, and only for the unknown class. Every
|
|
17
|
+
# other file in a pull request keeps both the verified and the unknown class.
|
|
18
|
+
(^|/)eval/fixtures/secrets\.php$
|
|
19
|
+
(^|/)eval/canaries\.py$
|