coretrace-python-analyzer 0.4.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/CHANGELOG.md +11 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/docs/plugins.md +7 -4
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/docs/usage.md +25 -9
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +5 -2
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +32 -7
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cache.py +37 -8
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/advisories.py +22 -9
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/correlation.py +81 -13
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/graph.py +7 -2
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/engine.py +9 -4
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/__init__.py +4 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/callgraph.py +62 -9
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/summaries.py +8 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/api.py +18 -4
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/__init__.py +2 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/engine.py +76 -14
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_advisory_evidence.py +8 -1
- coretrace_python_analyzer-0.5.0/tests/test_argument_conditions.py +217 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_cache.py +15 -4
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_dependencies.py +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_heap.py +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_project.py +99 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_release.py +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/README.md +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/pygoat.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_tornado_bottle_models.py +0 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 (2026-09-24)
|
|
4
|
+
|
|
5
|
+
### Dependencies
|
|
6
|
+
|
|
7
|
+
- Argument conditions of advisory entry points are decided at each call, with a `position` for positional arguments and a `default` for absent ones: a call meeting them is reported with `conditions_met`, a call passing another known value does not reach the vulnerability and is listed in the requirement's `ruled_out`, and a call the analyzer cannot decide leaves them pending review (#110).
|
|
8
|
+
|
|
9
|
+
### Plugins
|
|
10
|
+
|
|
11
|
+
- `ProjectContext.functions(module)` gives project plugins every function of a module as its call graph names it, with its span and the label of the entry point it is (`http` for a route, `argv` for a command), decided by the taint engine's own rule; modules served from the cache keep them, and the cache format is bumped (#116).
|
|
12
|
+
- Call sites and external calls record what each argument denotes — a symbol, a constant as Python writes it, or nothing known — in an `Arguments` record: `CallSite.arguments` (formerly the argument counts), `ExternalCall.arguments`, and `TaintFlow.sink_arguments` for the sink call of a flow, through callees in other modules too (#110).
|
|
13
|
+
|
|
3
14
|
## 0.4.0 (2026-09-24)
|
|
4
15
|
|
|
5
16
|
### Analysis
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Static security analysis infrastructure for Python code
|
|
5
5
|
Project-URL: Homepage, https://github.com/CoreTrace/coretrace-python-analyzer
|
|
6
6
|
Project-URL: Repository, https://github.com/CoreTrace/coretrace-python-analyzer
|
|
@@ -222,9 +222,12 @@ rule. Findings carry a redacted preview only.
|
|
|
222
222
|
|
|
223
223
|
Runs once per project after every module is analysed, with a `ProjectContext`: the
|
|
224
224
|
module graph, the dependency graph, every advisory the plugins and advisory files
|
|
225
|
-
contributed, the policy, the project root, and each module's imports
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
contributed, the policy, the project root, and each module's imports, call graph and
|
|
226
|
+
functions. `ctx.functions(module)` gives every function as the call graph names it
|
|
227
|
+
(`Admin.get`, `outer.inner`, `<module>`), with its span, so a finding can be placed in
|
|
228
|
+
one, and the label of the entry point it is (`http` for a route, `argv` for a command)
|
|
229
|
+
when the models make it one. The shipped `vulnerable-dependency`,
|
|
230
|
+
`reachable-vulnerability` and `dependency-policy` plugins are of this kind.
|
|
228
231
|
|
|
229
232
|
```python
|
|
230
233
|
from collections.abc import Sequence
|
|
@@ -238,7 +241,7 @@ class RequirementsPresent(ProjectPlugin):
|
|
|
238
241
|
name: ClassVar[str] = "requirements-present"
|
|
239
242
|
|
|
240
243
|
def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
|
|
241
|
-
return () # inspect ctx.dependencies, ctx.modules(), ctx.
|
|
244
|
+
return () # inspect ctx.dependencies, ctx.modules, ctx.call_graph(module), ctx.functions(module)
|
|
242
245
|
```
|
|
243
246
|
|
|
244
247
|
### Anything else: `Plugin`
|
|
@@ -201,7 +201,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
201
201
|
```json
|
|
202
202
|
{
|
|
203
203
|
"schema_version": 1,
|
|
204
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
204
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.5.0"},
|
|
205
205
|
"root": "/home/me/project",
|
|
206
206
|
"findings": [
|
|
207
207
|
{
|
|
@@ -271,7 +271,8 @@ affected APIs that feed reachability and correlation:
|
|
|
271
271
|
"symbol": "python.yaml.load",
|
|
272
272
|
"justification": "same path when Loader is FullLoader",
|
|
273
273
|
"conditions": [
|
|
274
|
-
{"kind": "argument", "text": "Loader is FullLoader", "argument": "Loader", "
|
|
274
|
+
{"kind": "argument", "text": "Loader is FullLoader, the default", "argument": "Loader", "position": 1,
|
|
275
|
+
"values": ["python.yaml.FullLoader", "python.yaml.loader.FullLoader"], "default": true},
|
|
275
276
|
{"kind": "semantic", "text": "the document carries a python/object/apply tag"}
|
|
276
277
|
]
|
|
277
278
|
}
|
|
@@ -285,17 +286,32 @@ affected APIs that feed reachability and correlation:
|
|
|
285
286
|
|
|
286
287
|
`affected_symbols` are the functions the fix changed; `entry_points` the public APIs
|
|
287
288
|
through which a project reaches them, each with the justification that ties it to the
|
|
288
|
-
fix — the commit, or the call path — and the `conditions` under which it is affected
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
289
|
+
fix — the commit, or the call path — and the `conditions` under which it is affected. A
|
|
290
|
+
call to either kind of symbol is reachable. `modules` names the top-level modules the
|
|
291
|
+
package installs, so the analyzer can tell an imported package from a merely required
|
|
292
|
+
one; it defaults to the package name.
|
|
293
|
+
|
|
294
|
+
An `argument` condition is decided at each call. It names the argument by keyword and,
|
|
295
|
+
when it may be passed positionally, by `position` (counted from 0, the receiver of a
|
|
296
|
+
method excluded); `values` are what makes the call affected, written as the analyzer
|
|
297
|
+
sees arguments: a symbol by its canonical name (`python.yaml.FullLoader`, and every
|
|
298
|
+
spelling a project may use, such as `python.yaml.loader.FullLoader`), a constant as
|
|
299
|
+
Python writes it (`True`, `None`, `'/static'`); `default` says an absent argument means
|
|
300
|
+
an affected value. A call passing another known value does not reach the vulnerability;
|
|
301
|
+
a call passing a variable, or unpacking `*args` or `**kwargs`, leaves the condition
|
|
302
|
+
pending review. A `semantic` condition — the document's tags, the platform, the
|
|
303
|
+
template's origin — cannot be decided and is always pending review, never dropped.
|
|
293
304
|
|
|
294
305
|
Every dependency finding records the highest level of evidence established in its
|
|
295
306
|
`level` metadata: `declared` (the requirement allows a vulnerable version), `imported`
|
|
296
307
|
(a module of the package is imported somewhere), `reachable` (an entry point or affected
|
|
297
|
-
symbol is called) or `exploitable` (attacker input reaches it)
|
|
298
|
-
`justification`, `conditions
|
|
308
|
+
symbol is called) or `exploitable` (attacker input reaches it). The last two carry
|
|
309
|
+
`entry_point`, `justification`, `conditions`, and which of them the call meets
|
|
310
|
+
(`conditions_met`) or leaves to review (`conditions_pending_review`). A requirement whose
|
|
311
|
+
entry points are only called with arguments that contradict a condition stays
|
|
312
|
+
`imported`, and its finding lists those calls in `ruled_out`
|
|
313
|
+
(`app.views:12 python.yaml.load(Loader=python.yaml.SafeLoader)`), the evidence that
|
|
314
|
+
they do not reach the vulnerability.
|
|
299
315
|
|
|
300
316
|
A `coretrace-policy.toml` at the root, or the file passed with `--policy`, denies
|
|
301
317
|
packages, requires pins and lists accepted advisories whose findings are dropped:
|
|
@@ -7,7 +7,7 @@ from typing import ClassVar
|
|
|
7
7
|
|
|
8
8
|
from coretrace_python.analysis import AnyAnalysis
|
|
9
9
|
from coretrace_python.dependency import DependencyAnalysis
|
|
10
|
-
from coretrace_python.dependency.correlation import affected_symbols, evidence
|
|
10
|
+
from coretrace_python.dependency.correlation import affected_symbols, check_conditions, evidence
|
|
11
11
|
from coretrace_python.findings import Confidence, Finding
|
|
12
12
|
from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
|
|
13
13
|
from coretrace_python.plugins import ProjectContext, ProjectPlugin
|
|
@@ -29,6 +29,9 @@ class ReachableVulnerabilityPlugin(ProjectPlugin):
|
|
|
29
29
|
if not isinstance(site.target, ExternalSymbol):
|
|
30
30
|
continue
|
|
31
31
|
for advisory in affected.get(site.target.symbol, ()):
|
|
32
|
+
check = check_conditions(advisory.entry_point(site.target.symbol), site.arguments)
|
|
33
|
+
if check.contradicted is not None:
|
|
34
|
+
continue
|
|
32
35
|
findings.append(
|
|
33
36
|
Finding(
|
|
34
37
|
rule_id="reachable-vulnerability",
|
|
@@ -40,7 +43,7 @@ class ReachableVulnerabilityPlugin(ProjectPlugin):
|
|
|
40
43
|
confidence=Confidence.HIGH,
|
|
41
44
|
span=site.location,
|
|
42
45
|
function=function,
|
|
43
|
-
metadata=evidence(advisory, site.target.symbol, "reachable"),
|
|
46
|
+
metadata=evidence(advisory, site.target.symbol, "reachable", check),
|
|
44
47
|
)
|
|
45
48
|
)
|
|
46
49
|
return findings
|
|
@@ -7,23 +7,34 @@ from typing import ClassVar
|
|
|
7
7
|
|
|
8
8
|
from coretrace_python.analysis import AnyAnalysis
|
|
9
9
|
from coretrace_python.dependency import DependencyAnalysis
|
|
10
|
+
from coretrace_python.dependency.correlation import affected_symbols, check_conditions, ruled_out
|
|
10
11
|
from coretrace_python.findings import Confidence, Finding
|
|
12
|
+
from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
|
|
11
13
|
from coretrace_python.plugins import ProjectContext, ProjectPlugin
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class VulnerableDependencyPlugin(ProjectPlugin):
|
|
15
17
|
name: ClassVar[str] = "vulnerable-dependency"
|
|
16
|
-
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis})
|
|
18
|
+
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis, CallGraphAnalysis})
|
|
17
19
|
|
|
18
20
|
def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
|
|
19
21
|
findings: list[Finding] = []
|
|
20
22
|
imported = [s for module in ctx.modules for s in ctx.imports(module).all_symbols()]
|
|
23
|
+
excluded = _ruled_out(ctx)
|
|
21
24
|
for requirement in ctx.dependencies.requirements:
|
|
22
25
|
for advisory in ctx.advisories:
|
|
23
26
|
if not advisory.affects(requirement):
|
|
24
27
|
continue
|
|
25
28
|
pinned = requirement.pinned is not None
|
|
26
29
|
level = "imported" if advisory.imported_by(imported) else "declared"
|
|
30
|
+
metadata = {
|
|
31
|
+
"advisory": advisory.id,
|
|
32
|
+
"package": advisory.package,
|
|
33
|
+
"specifier": requirement.specifier,
|
|
34
|
+
"level": level,
|
|
35
|
+
}
|
|
36
|
+
if excluded.get(advisory.id):
|
|
37
|
+
metadata["ruled_out"] = "; ".join(excluded[advisory.id])
|
|
27
38
|
findings.append(
|
|
28
39
|
Finding(
|
|
29
40
|
rule_id="vulnerable-dependency",
|
|
@@ -35,12 +46,26 @@ class VulnerableDependencyPlugin(ProjectPlugin):
|
|
|
35
46
|
severity=advisory.severity,
|
|
36
47
|
confidence=Confidence.HIGH if pinned else Confidence.MEDIUM,
|
|
37
48
|
span=requirement.span,
|
|
38
|
-
metadata=
|
|
39
|
-
"advisory": advisory.id,
|
|
40
|
-
"package": advisory.package,
|
|
41
|
-
"specifier": requirement.specifier,
|
|
42
|
-
"level": level,
|
|
43
|
-
},
|
|
49
|
+
metadata=metadata,
|
|
44
50
|
)
|
|
45
51
|
)
|
|
46
52
|
return findings
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _ruled_out(ctx: ProjectContext) -> dict[str, list[str]]:
|
|
56
|
+
"""By advisory, the calls to its entry points whose arguments contradict one of its
|
|
57
|
+
conditions: the evidence that the requirement, imported, is not reached there."""
|
|
58
|
+
|
|
59
|
+
affected = affected_symbols(ctx.dependencies, ctx.advisories)
|
|
60
|
+
excluded: dict[str, list[str]] = {}
|
|
61
|
+
for module in sorted(ctx.modules):
|
|
62
|
+
graph = ctx.call_graph(module)
|
|
63
|
+
for function in graph.functions:
|
|
64
|
+
for site in graph.sites(function):
|
|
65
|
+
if not isinstance(site.target, ExternalSymbol):
|
|
66
|
+
continue
|
|
67
|
+
for advisory in affected.get(site.target.symbol, ()):
|
|
68
|
+
check = check_conditions(advisory.entry_point(site.target.symbol), site.arguments)
|
|
69
|
+
if check.contradicted is not None:
|
|
70
|
+
excluded.setdefault(advisory.id, []).append(ruled_out(module, site, check))
|
|
71
|
+
return excluded
|
{coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cache.py
RENAMED
|
@@ -22,11 +22,13 @@ from typing import Any
|
|
|
22
22
|
|
|
23
23
|
from coretrace_python.findings import Confidence, Finding, Severity
|
|
24
24
|
from coretrace_python.interprocedural import (
|
|
25
|
+
Arguments,
|
|
25
26
|
CallSite,
|
|
26
27
|
ExternalCall,
|
|
27
28
|
ExternalSymbol,
|
|
28
29
|
FunctionSummary,
|
|
29
30
|
KnownFunction,
|
|
31
|
+
ModuleFunction,
|
|
30
32
|
ModuleGraph,
|
|
31
33
|
Mutation,
|
|
32
34
|
NonlocalWrite,
|
|
@@ -37,14 +39,14 @@ from coretrace_python.interprocedural import (
|
|
|
37
39
|
from coretrace_python.semantic.symbols import SymbolId
|
|
38
40
|
from coretrace_python.source import SourceId, SourceSpan
|
|
39
41
|
|
|
40
|
-
CACHE_FORMAT =
|
|
42
|
+
CACHE_FORMAT = 7
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
@dataclass(frozen=True)
|
|
44
46
|
class CachedModule:
|
|
45
47
|
"""Everything a later run needs from one module without lowering it again."""
|
|
46
48
|
|
|
47
|
-
functions: tuple[
|
|
49
|
+
functions: tuple[ModuleFunction, ...]
|
|
48
50
|
summaries: Mapping[str, FunctionSummary]
|
|
49
51
|
sites: tuple[CallSite, ...]
|
|
50
52
|
findings: tuple[Finding, ...]
|
|
@@ -128,7 +130,7 @@ class ProjectCache:
|
|
|
128
130
|
def encode(module: CachedModule) -> dict[str, Any]:
|
|
129
131
|
return {
|
|
130
132
|
"format": CACHE_FORMAT,
|
|
131
|
-
"functions":
|
|
133
|
+
"functions": [[f.name, _encode_span(f.span), f.entry_point] for f in module.functions],
|
|
132
134
|
"summaries": {name: _encode_summary(s) for name, s in module.summaries.items()},
|
|
133
135
|
"sites": [_encode_site(site) for site in module.sites],
|
|
134
136
|
"findings": [_encode_finding(finding) for finding in module.findings],
|
|
@@ -139,7 +141,7 @@ def decode(data: Mapping[str, Any]) -> CachedModule:
|
|
|
139
141
|
if data["format"] != CACHE_FORMAT:
|
|
140
142
|
raise ValueError(f"unsupported cache format {data['format']!r}")
|
|
141
143
|
return CachedModule(
|
|
142
|
-
tuple(
|
|
144
|
+
tuple(_decode_function(function) for function in data["functions"]),
|
|
143
145
|
{_string(name): _decode_summary(s) for name, s in data["summaries"].items()},
|
|
144
146
|
tuple(_decode_site(site) for site in data["sites"]),
|
|
145
147
|
tuple(_decode_finding(finding) for finding in data["findings"]),
|
|
@@ -187,6 +189,11 @@ def _decode_span(data: Any) -> SourceSpan:
|
|
|
187
189
|
)
|
|
188
190
|
|
|
189
191
|
|
|
192
|
+
def _decode_function(data: Any) -> ModuleFunction:
|
|
193
|
+
name, span, entry_point = data
|
|
194
|
+
return ModuleFunction(_string(name), _decode_span(span), None if entry_point is None else _string(entry_point))
|
|
195
|
+
|
|
196
|
+
|
|
190
197
|
def _encode_finding(finding: Finding) -> dict[str, Any]:
|
|
191
198
|
return {
|
|
192
199
|
"rule": finding.rule_id,
|
|
@@ -219,6 +226,7 @@ def _encode_call(call: ExternalCall) -> dict[str, Any]:
|
|
|
219
226
|
"keywords": sorted(call.keyword_dependencies),
|
|
220
227
|
"location": _encode_span(call.location),
|
|
221
228
|
"call_site": None if call.call_site is None else _encode_span(call.call_site),
|
|
229
|
+
"given": _encode_arguments(call.arguments),
|
|
222
230
|
}
|
|
223
231
|
|
|
224
232
|
|
|
@@ -230,6 +238,7 @@ def _decode_call(data: Mapping[str, Any]) -> ExternalCall:
|
|
|
230
238
|
_indices(data["keywords"]),
|
|
231
239
|
_decode_span(data["location"]),
|
|
232
240
|
None if site is None else _decode_span(site),
|
|
241
|
+
_decode_arguments(data["given"]),
|
|
233
242
|
)
|
|
234
243
|
|
|
235
244
|
|
|
@@ -311,8 +320,7 @@ def _encode_site(site: CallSite) -> dict[str, Any]:
|
|
|
311
320
|
"caller": site.caller,
|
|
312
321
|
"location": _encode_span(site.location),
|
|
313
322
|
"target": _encode_target(site.target),
|
|
314
|
-
"arguments": site.arguments,
|
|
315
|
-
"keywords": site.keywords,
|
|
323
|
+
"arguments": _encode_arguments(site.arguments),
|
|
316
324
|
}
|
|
317
325
|
|
|
318
326
|
|
|
@@ -321,6 +329,27 @@ def _decode_site(data: Mapping[str, Any]) -> CallSite:
|
|
|
321
329
|
_string(data["caller"]),
|
|
322
330
|
_decode_span(data["location"]),
|
|
323
331
|
_decode_target(data["target"]),
|
|
324
|
-
|
|
325
|
-
|
|
332
|
+
_decode_arguments(data["arguments"]),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _encode_arguments(arguments: Arguments) -> dict[str, Any]:
|
|
337
|
+
return {
|
|
338
|
+
"positional": list(arguments.positional),
|
|
339
|
+
"keywords": [[name, value] for name, value in arguments.keywords],
|
|
340
|
+
"unpacked": arguments.unpacked,
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _decode_arguments(data: Mapping[str, Any]) -> Arguments:
|
|
345
|
+
def denoted(value: Any) -> str | None:
|
|
346
|
+
return None if value is None else _string(value)
|
|
347
|
+
|
|
348
|
+
unpacked = data["unpacked"]
|
|
349
|
+
if not isinstance(unpacked, bool):
|
|
350
|
+
raise TypeError(f"expected a boolean, got {unpacked!r}")
|
|
351
|
+
return Arguments(
|
|
352
|
+
tuple(denoted(value) for value in data["positional"]),
|
|
353
|
+
tuple((_string(name), denoted(value)) for name, value in data["keywords"]),
|
|
354
|
+
unpacked,
|
|
326
355
|
)
|
|
@@ -160,6 +160,10 @@ def _condition_entry(condition: Condition) -> dict[str, Any]:
|
|
|
160
160
|
entry["argument"] = condition.argument
|
|
161
161
|
if condition.values:
|
|
162
162
|
entry["values"] = list(condition.values)
|
|
163
|
+
if condition.position is not None:
|
|
164
|
+
entry["position"] = condition.position
|
|
165
|
+
if condition.default:
|
|
166
|
+
entry["default"] = True
|
|
163
167
|
return entry
|
|
164
168
|
|
|
165
169
|
|
|
@@ -193,13 +197,22 @@ def _entry_point(entry: Mapping[str, Any]) -> AdvisoryEntryPoint:
|
|
|
193
197
|
return AdvisoryEntryPoint(
|
|
194
198
|
SymbolId(str(entry["symbol"])),
|
|
195
199
|
str(entry["justification"]),
|
|
196
|
-
tuple(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
tuple(_condition(c) for c in entry.get("conditions") or []),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _condition(entry: Mapping[str, Any]) -> Condition:
|
|
205
|
+
position = entry.get("position")
|
|
206
|
+
if position is not None and (not isinstance(position, int) or isinstance(position, bool) or position < 0):
|
|
207
|
+
raise TypeError(f"condition position must be a non-negative integer, got {position!r}")
|
|
208
|
+
default = entry.get("default", False)
|
|
209
|
+
if not isinstance(default, bool):
|
|
210
|
+
raise TypeError(f"condition default must be true or false, got {default!r}")
|
|
211
|
+
return Condition(
|
|
212
|
+
str(entry["kind"]),
|
|
213
|
+
str(entry["text"]),
|
|
214
|
+
None if entry.get("argument") is None else str(entry["argument"]),
|
|
215
|
+
tuple(str(v) for v in entry.get("values") or []),
|
|
216
|
+
position,
|
|
217
|
+
default,
|
|
205
218
|
)
|
|
@@ -10,10 +10,18 @@ are correlated here into one high-confidence ``exploitable-vulnerability`` findi
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
from collections.abc import Iterable, Mapping
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
from coretrace_python.dependency.graph import (
|
|
16
|
+
DIRECT,
|
|
17
|
+
Advisory,
|
|
18
|
+
AdvisoryEntryPoint,
|
|
19
|
+
Condition,
|
|
20
|
+
DependencyGraph,
|
|
21
|
+
)
|
|
15
22
|
from coretrace_python.findings import Confidence, Finding, Severity
|
|
16
23
|
from coretrace_python.findings.refutation import Status, Verdict, Verdicts
|
|
24
|
+
from coretrace_python.interprocedural import Arguments, CallSite, ExternalSymbol
|
|
17
25
|
from coretrace_python.semantic.symbols import SymbolId
|
|
18
26
|
from coretrace_python.taint import Sink, TaintFlow, TaintKind
|
|
19
27
|
|
|
@@ -40,10 +48,68 @@ def advisory_sinks(affected: Affected) -> tuple[Sink, ...]:
|
|
|
40
48
|
return tuple(Sink(symbol, TaintKind.ADVISORY) for symbol in affected)
|
|
41
49
|
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class ConditionCheck:
|
|
53
|
+
"""What one call tells of an entry point's conditions: those it meets, those left to
|
|
54
|
+
review (every semantic one, and every argument one the call does not decide), and
|
|
55
|
+
the argument condition it contradicts with what it passes instead (None: absent),
|
|
56
|
+
in which case the call does not reach the vulnerability."""
|
|
57
|
+
|
|
58
|
+
met: tuple[Condition, ...] = ()
|
|
59
|
+
pending: tuple[Condition, ...] = ()
|
|
60
|
+
contradicted: Condition | None = None
|
|
61
|
+
passed: str | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def check_conditions(entry: AdvisoryEntryPoint | None, arguments: Arguments | None) -> ConditionCheck:
|
|
65
|
+
"""Decide ``entry``'s conditions against what the call's ``arguments`` denote."""
|
|
66
|
+
|
|
67
|
+
if entry is None:
|
|
68
|
+
return ConditionCheck()
|
|
69
|
+
met: list[Condition] = []
|
|
70
|
+
pending: list[Condition] = []
|
|
71
|
+
for condition in entry.conditions:
|
|
72
|
+
given = _given(condition, arguments) if condition.checkable and arguments is not None else None
|
|
73
|
+
if given is None:
|
|
74
|
+
pending.append(condition)
|
|
75
|
+
continue
|
|
76
|
+
explicit, value = given
|
|
77
|
+
if explicit and value is None:
|
|
78
|
+
pending.append(condition)
|
|
79
|
+
continue
|
|
80
|
+
affected = value in condition.values if explicit else condition.default
|
|
81
|
+
if not affected:
|
|
82
|
+
return ConditionCheck(tuple(met), tuple(pending), condition, value)
|
|
83
|
+
met.append(condition)
|
|
84
|
+
return ConditionCheck(tuple(met), tuple(pending))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _given(condition: Condition, arguments: Arguments) -> tuple[bool, str | None] | None:
|
|
88
|
+
"""Whether the call gives the condition's argument, and what it denotes: ``(True,
|
|
89
|
+
value)`` when given, ``(False, None)`` when surely absent, None when it cannot tell."""
|
|
90
|
+
|
|
91
|
+
for name, value in arguments.keywords:
|
|
92
|
+
if name == condition.argument:
|
|
93
|
+
return True, value
|
|
94
|
+
if condition.position is not None and condition.position < len(arguments.positional):
|
|
95
|
+
return True, arguments.positional[condition.position]
|
|
96
|
+
return None if arguments.unpacked else (False, None)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def ruled_out(module: str, site: CallSite, check: ConditionCheck) -> str:
|
|
100
|
+
"""Which call a contradicted condition rules out, and why: ``app:12
|
|
101
|
+
python.yaml.load(Loader=python.yaml.SafeLoader)``."""
|
|
102
|
+
|
|
103
|
+
assert check.contradicted is not None and isinstance(site.target, ExternalSymbol)
|
|
104
|
+
argument = check.contradicted.argument or f"#{check.contradicted.position}"
|
|
105
|
+
passed = f"{argument} absent" if check.passed is None else f"{argument}={check.passed}"
|
|
106
|
+
return f"{module}:{site.location.start_line} {site.target.symbol}({passed})"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def evidence(advisory: Advisory, symbol: SymbolId, level: str, check: ConditionCheck) -> dict[str, str]:
|
|
44
110
|
"""What a finding keeps of the advisory for ``symbol``: the level of evidence
|
|
45
|
-
established, how the symbol relates to the vulnerability and
|
|
46
|
-
|
|
111
|
+
established, how the symbol relates to the vulnerability, and its conditions — those
|
|
112
|
+
the call meets and those left to review."""
|
|
47
113
|
|
|
48
114
|
metadata = {"advisory": advisory.id, "package": advisory.package, "symbol": str(symbol), "level": level}
|
|
49
115
|
entry = advisory.entry_point(symbol)
|
|
@@ -54,9 +120,10 @@ def evidence(advisory: Advisory, symbol: SymbolId, level: str) -> dict[str, str]
|
|
|
54
120
|
metadata["justification"] = entry.justification
|
|
55
121
|
if entry.conditions:
|
|
56
122
|
metadata["conditions"] = "; ".join(c.text for c in entry.conditions)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
123
|
+
if check.met:
|
|
124
|
+
metadata["conditions_met"] = "; ".join(c.text for c in check.met)
|
|
125
|
+
if check.pending:
|
|
126
|
+
metadata["conditions_pending_review"] = "; ".join(c.text for c in check.pending)
|
|
60
127
|
return metadata
|
|
61
128
|
|
|
62
129
|
|
|
@@ -77,21 +144,22 @@ def correlate(
|
|
|
77
144
|
if verdict is not None and verdict.status is Status.REFUTED:
|
|
78
145
|
continue
|
|
79
146
|
hotspot = verdict is not None and verdict.status is Status.HOTSPOT
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
147
|
+
for advisory in affected.get(flow.sink.symbol, ()):
|
|
148
|
+
check = check_conditions(advisory.entry_point(flow.sink.symbol), flow.sink_arguments)
|
|
149
|
+
if check.contradicted is None:
|
|
150
|
+
findings.append(_exploitable(function, flow, advisory, verdict, hotspot, check))
|
|
83
151
|
return tuple(findings)
|
|
84
152
|
|
|
85
153
|
|
|
86
154
|
def _exploitable(
|
|
87
|
-
function: str, flow: TaintFlow, advisory: Advisory, verdict: Verdict | None, hotspot: bool
|
|
155
|
+
function: str, flow: TaintFlow, advisory: Advisory, verdict: Verdict | None, hotspot: bool, check: ConditionCheck
|
|
88
156
|
) -> Finding:
|
|
89
157
|
message = (
|
|
90
158
|
f"{advisory.id}: {flow.source.label} input reaches {flow.sink.symbol}, affected in "
|
|
91
159
|
f"the required {advisory.package} {advisory.vulnerable}: {advisory.summary}"
|
|
92
160
|
)
|
|
93
161
|
metadata = {
|
|
94
|
-
**evidence(advisory, flow.sink.symbol, "exploitable"),
|
|
162
|
+
**evidence(advisory, flow.sink.symbol, "exploitable", check),
|
|
95
163
|
"source": str(flow.source.symbol),
|
|
96
164
|
"source_label": flow.source.label,
|
|
97
165
|
"verdict": "hotspot" if hotspot else "vulnerability",
|
|
@@ -147,13 +147,18 @@ class Condition:
|
|
|
147
147
|
"""What must hold for an entry point to reach, or exploit, the affected code: the
|
|
148
148
|
value of an argument, the loader or algorithm used, the format decoded, the kind of
|
|
149
149
|
input, a configuration. ``kind`` says whether the engine can check it: an
|
|
150
|
-
``argument`` condition names the argument and
|
|
151
|
-
|
|
150
|
+
``argument`` condition names the argument by keyword, and by ``position`` when it may
|
|
151
|
+
be passed positionally, and lists the ``values`` that satisfy it — symbols
|
|
152
|
+
(``python.yaml.FullLoader``) or constants as Python writes them (``True``);
|
|
153
|
+
``default`` says an absent argument means a vulnerable value. A ``semantic``
|
|
154
|
+
condition cannot be checked and is reported as pending review."""
|
|
152
155
|
|
|
153
156
|
kind: str
|
|
154
157
|
text: str
|
|
155
158
|
argument: str | None = None
|
|
156
159
|
values: tuple[str, ...] = ()
|
|
160
|
+
position: int | None = None
|
|
161
|
+
default: bool = False
|
|
157
162
|
|
|
158
163
|
@property
|
|
159
164
|
def checkable(self) -> bool:
|
{coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/engine.py
RENAMED
|
@@ -71,6 +71,7 @@ from coretrace_python.interprocedural import (
|
|
|
71
71
|
CallGraphAnalysis,
|
|
72
72
|
CallSite,
|
|
73
73
|
FunctionSummary,
|
|
74
|
+
ModuleFunction,
|
|
74
75
|
ModuleGraph,
|
|
75
76
|
ProjectSummaries,
|
|
76
77
|
SummaryAnalysis,
|
|
@@ -106,6 +107,7 @@ from coretrace_python.semantic.symbols import SymbolAnalysis, SymbolId
|
|
|
106
107
|
from coretrace_python.source import SourceFile, SourceId, SourceManager, SourceSpan, decode_text
|
|
107
108
|
from coretrace_python.taint import (
|
|
108
109
|
EntryPoint,
|
|
110
|
+
EntryPointAnalysis,
|
|
109
111
|
ModelTable,
|
|
110
112
|
RegisteredRoutes,
|
|
111
113
|
Routes,
|
|
@@ -139,6 +141,7 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
|
|
|
139
141
|
ProjectSummaries,
|
|
140
142
|
SecurityModelAnalysis,
|
|
141
143
|
TaintAnalysis,
|
|
144
|
+
EntryPointAnalysis,
|
|
142
145
|
RefutationAnalysis,
|
|
143
146
|
DependencyAnalysis,
|
|
144
147
|
RegisteredRoutes,
|
|
@@ -147,7 +150,7 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
|
|
|
147
150
|
|
|
148
151
|
# Named so the set stays right whichever analyses a build registers.
|
|
149
152
|
_PROJECT_DEPENDANT_NAMES = frozenset(
|
|
150
|
-
{"interprocedural.project", "interprocedural.summaries", "taint.flows", "findings.refutation"}
|
|
153
|
+
{"interprocedural.project", "interprocedural.summaries", "taint.flows", "taint.entry_points", "findings.refutation"}
|
|
151
154
|
)
|
|
152
155
|
_PROJECT_DEPENDANTS: frozenset[AnyAnalysis] = frozenset(
|
|
153
156
|
a for a in ALL_ANALYSES if a.name in _PROJECT_DEPENDANT_NAMES
|
|
@@ -451,6 +454,7 @@ def analyze_project(
|
|
|
451
454
|
|
|
452
455
|
index = _seed(results, graph, frozenset())
|
|
453
456
|
call_graphs: dict[str, CallGraph] = {}
|
|
457
|
+
functions: dict[str, tuple[ModuleFunction, ...]] = {}
|
|
454
458
|
for name in sorted(analysable):
|
|
455
459
|
entry = results[name]
|
|
456
460
|
findings.extend(entry.findings)
|
|
@@ -458,11 +462,12 @@ def analyze_project(
|
|
|
458
462
|
coverage.append(
|
|
459
463
|
FileCoverage(str(files[name].source_id), "analysed", len(entry.functions), len(entry.functions) - unsupported)
|
|
460
464
|
)
|
|
461
|
-
|
|
465
|
+
functions[name] = entry.functions
|
|
466
|
+
sites: dict[str, list[CallSite]] = {function.name: [] for function in entry.functions}
|
|
462
467
|
for site in entry.sites:
|
|
463
468
|
sites.setdefault(site.caller, []).append(site)
|
|
464
469
|
call_graphs[name] = CallGraph({}, {f: tuple(s) for f, s in sites.items()}, frozenset())
|
|
465
|
-
context = ProjectContext(graph, dependencies, advisories, analysable, call_graphs, policy, root)
|
|
470
|
+
context = ProjectContext(graph, dependencies, advisories, analysable, call_graphs, policy, root, functions)
|
|
466
471
|
for plugin in all_plugins:
|
|
467
472
|
if isinstance(plugin, ProjectPlugin):
|
|
468
473
|
findings.extend(plugin.analyze_project(context))
|
|
@@ -662,7 +667,7 @@ def _analyse_module(
|
|
|
662
667
|
)
|
|
663
668
|
)
|
|
664
669
|
return CachedModule(
|
|
665
|
-
|
|
670
|
+
manager.get(EntryPointAnalysis),
|
|
666
671
|
_summaries_of(manager),
|
|
667
672
|
tuple(site for function in graph.functions for site in graph.sites(function)),
|
|
668
673
|
(*findings, *correlated),
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"""Call graph and function summaries (architecture §19, §20)."""
|
|
2
2
|
|
|
3
3
|
from coretrace_python.interprocedural.callgraph import (
|
|
4
|
+
Arguments,
|
|
4
5
|
CallGraph,
|
|
5
6
|
CallGraphAnalysis,
|
|
6
7
|
CallSite,
|
|
7
8
|
ExternalSymbol,
|
|
8
9
|
KnownFunction,
|
|
10
|
+
ModuleFunction,
|
|
9
11
|
Target,
|
|
10
12
|
UnknownTarget,
|
|
11
13
|
)
|
|
@@ -28,6 +30,7 @@ from coretrace_python.interprocedural.summaries import (
|
|
|
28
30
|
)
|
|
29
31
|
|
|
30
32
|
__all__ = [
|
|
33
|
+
"Arguments",
|
|
31
34
|
"CallGraph",
|
|
32
35
|
"CallGraphAnalysis",
|
|
33
36
|
"CallSite",
|
|
@@ -35,6 +38,7 @@ __all__ = [
|
|
|
35
38
|
"ExternalSymbol",
|
|
36
39
|
"FunctionSummary",
|
|
37
40
|
"KnownFunction",
|
|
41
|
+
"ModuleFunction",
|
|
38
42
|
"ModuleGraph",
|
|
39
43
|
"Mutation",
|
|
40
44
|
"NonlocalWrite",
|