coretrace-python-analyzer 0.18.0__tar.gz → 0.19.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.18.0 → coretrace_python_analyzer-0.19.0}/CHANGELOG.md +17 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/docs/plugins.md +15 -6
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/docs/usage.md +51 -13
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/cache.py +9 -4
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/engine.py +95 -28
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/findings/coverage.py +6 -4
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/interprocedural/__init__.py +2 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/interprocedural/callgraph.py +6 -2
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/interprocedural/modulegraph.py +103 -9
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/semantic/scopes.py +2 -2
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/taint/engine.py +53 -8
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/taint/templates.py +279 -29
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/DjangoGoat.json +28 -4
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/httpie.json +16 -4
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/pygoat.json +35 -4
- coretrace_python_analyzer-0.19.0/tests/test_block_super.py +155 -0
- coretrace_python_analyzer-0.19.0/tests/test_entry_point_aliases.py +160 -0
- coretrace_python_analyzer-0.19.0/tests/test_function_aliases_api.py +84 -0
- coretrace_python_analyzer-0.19.0/tests/test_module_identity.py +384 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.19.0/tests/test_settings_scope.py +109 -0
- coretrace_python_analyzer-0.19.0/tests/test_template_filter_overrides.py +385 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/README.md +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/abstract/strings.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dependency/advisories.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dependency/correlation.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dependency/graph.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/interprocedural/summaries.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/taint/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/taint/models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/taint/urls.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_advisory_url_host.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_base_patterns.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_code_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_declared_entry_points.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_members.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_open_redirect_destinations.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_ssrf_destinations.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_template_context_flows.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_template_filter_calls.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_template_request_processor.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_unreadable_templates.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_url_proof.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.0 (2026-09-29)
|
|
4
|
+
|
|
5
|
+
### Analysis
|
|
6
|
+
|
|
7
|
+
- Two files with the same Python module name, `a/app.py` and `z/app.py` outside any package, no longer overwrite each other: both are discovered, analysed and covered whatever the order they are found in. The engine tells them apart by their path from the root as a module name, `a.app` and `z.app`, in the module graph, the summary index, the cache keys and what project plugins see; a unique name stays what it was. An import of such a name resolves to the file in the importer's own root only, and one no root resolves is not followed. Each colliding file carries an `ambiguous-module` note and is covered as `ambiguous`, since a model or a route naming `python.app.main` applies to every definition of that symbol, and so does an importer whose import stayed unresolved; a VEX statement over such a project stays `under_investigation` (#182).
|
|
8
|
+
|
|
9
|
+
### Precision
|
|
10
|
+
|
|
11
|
+
- A `TEMPLATES` assigned inside a function or a class no longer counts as Django settings: only a module-level assignment can activate the `request` context processor, and only a mention of the module's `TEMPLATES` elsewhere leaves that activation uncertain. A function-local `TEMPLATES`, called or not, used to enable the implicit request source, upgrading a template filter finding to exploitable (#184).
|
|
12
|
+
- A template block that renders `{{ block.super }}` keeps the parent block's content, so the filters the parent applies stay fed by the render context, with the parent template as the place of the call; a block without it still replaces the parent's whole block, and a chain of templates keeps exactly the parents each level renders. The parent's filters used to drop out of the exploitable flow as soon as a child defined the block (#185).
|
|
13
|
+
- A project tag library registering a filter of a built-in's name replaces the built-in in every Django template loading it before applying the filter, as Django's parser does: `{% load custom %}{{ bio|striptags }}` calls the project's `striptags` when `app/templatetags/custom.py` registers one, so it is no longer a reachable or exploitable call to `django.template.defaultfilters.striptags`; the project's function is ordinary code, analysed as any other. A load affects only the filters after it, a later load overrides an earlier one, `{% load striptags from custom %}` replaces only what it names, and Django's own libraries change nothing. The analyzer reads the registrations on `register`, a `django.template.Library` however it is imported, in decorator and call form, at module level or under `if`, `try` or a function. Where it cannot tell what a library registers — no such library file, one it cannot read or parse, a name not written as a constant, `register` passed elsewhere or filled through `filters=` or `register.filters`, or two apps giving the name and registering different filters — a filter of that name applied under the load is neither the built-in nor certainly replaced: no call is claimed, and the load counts among the templates the analyzer could not read (`app/templates/app/profile.html:1 loads 'custom', not read`), so an advisory naming the filter stays `under_investigation` in the VEX document. The settings' `OPTIONS["libraries"]` and `OPTIONS["builtins"]` are not read (#183).
|
|
14
|
+
|
|
15
|
+
### Plugins
|
|
16
|
+
|
|
17
|
+
- `ModuleFunction.aliases` gives a project plugin the module-level names bound to a function by assigning one name to another, transitively and in source order, so an integration declaring a handler by the name a module binds (`handlers.main`) can report a name the engine resolves to no function; a method or a nested function has none. The cache format changes.
|
|
18
|
+
- An `EntryPoint` naming a project function or class applies through a module-level alias of it: `main = actual` makes `python.app.handlers.main` name `actual`, so a deployment declaring `handlers.main` reaches the function doing the work. An alias is an assignment of one name to another at module level, followed transitively and in source order, in a conditional or a `try` as well; a name bound inside a function or class, or to a call, an import or anything but a name, is no alias, and the engine still infers nothing from a name. A model naming a method wins over one naming its class, whichever name it uses.
|
|
19
|
+
|
|
3
20
|
## 0.18.0 (2026-09-28)
|
|
4
21
|
|
|
5
22
|
### Precision
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.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
|
|
@@ -53,7 +53,11 @@ resolved through imports and aliases, so `python.os.system` covers `os.system`,
|
|
|
53
53
|
symbol, so a chain such as `sqlite3.connect(p).cursor().execute` is
|
|
54
54
|
`python.sqlite3.connect.cursor.execute`, and `app = Flask(__name__)` gives `app.route` the
|
|
55
55
|
symbol `python.flask.Flask.route`. Builtins are `python.builtins.<name>`. Symbols are
|
|
56
|
-
`SymbolId` values from `coretrace_python.semantic.symbols`.
|
|
56
|
+
`SymbolId` values from `coretrace_python.semantic.symbols`. A function of the project is
|
|
57
|
+
`python.` followed by its module's import name and its qualified name,
|
|
58
|
+
`python.app.handlers.main`; two files with the same import name, `a/app.py` and
|
|
59
|
+
`z/app.py` outside any package, define the same symbols, so a model naming
|
|
60
|
+
`python.app.main` names the function of each.
|
|
57
61
|
|
|
58
62
|
## Kinds of plugin
|
|
59
63
|
|
|
@@ -255,14 +259,19 @@ module graph, the dependency graph, every advisory the plugins and advisory file
|
|
|
255
259
|
contributed, the policy, the project root, and each module's imports, call graph and
|
|
256
260
|
functions. `ctx.functions(module)` gives every function as the call graph names it
|
|
257
261
|
(`Admin.get`, `outer.inner`, `<module>`), with its span, so a finding can be placed in
|
|
258
|
-
one,
|
|
259
|
-
when the models make it one
|
|
262
|
+
one, the label of the entry point it is (`http` for a route, `argv` for a command)
|
|
263
|
+
when the models make it one, and its `aliases`, the module-level names bound to it by
|
|
264
|
+
assigning one name to another (`main = actual`), transitively and in source order, so
|
|
265
|
+
an integration declaring a handler by the name a module binds can tell a name the
|
|
266
|
+
engine resolves to no function; a method or a nested function has none. `ctx.call_graph(module)` gives each function's call sites
|
|
260
267
|
(`sites(function)`) and the symbols it reads, called or not (`reads(function)`: reading
|
|
261
268
|
`request.form['name']` reads `python.flask.request.form`), each where it first reads it.
|
|
262
269
|
`ctx.templates` gives the templates found under the project's `templates` directories:
|
|
263
|
-
their `names`, the filters they apply as `calls` (each a
|
|
264
|
-
function behind the filter and where the template
|
|
265
|
-
|
|
270
|
+
their `names`, the filters of Django's own libraries they apply as `calls` (each a
|
|
271
|
+
`FilterCall(symbol, span)`, the function behind the filter and where the template
|
|
272
|
+
applies it; a filter a project tag library the template loads replaces is no call),
|
|
273
|
+
and where they name a template the analyzer cannot read, or load a library it cannot
|
|
274
|
+
read while applying a filter of that library's name (`unread`).
|
|
266
275
|
The shipped `vulnerable-dependency`, `reachable-vulnerability` and `dependency-policy`
|
|
267
276
|
plugins are of this kind.
|
|
268
277
|
|
|
@@ -65,7 +65,13 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
65
65
|
## What is analysed
|
|
66
66
|
|
|
67
67
|
Given a directory, every `.py` file below it is a module of one project, named after
|
|
68
|
-
its package (`app/views.py` is `app.views`).
|
|
68
|
+
its package (`app/views.py` is `app.views`). Two files that would share a name, `a/app.py`
|
|
69
|
+
and `z/app.py` outside any package, are told apart by their path from the root, `a.app`
|
|
70
|
+
and `z.app`, and reported: each carries an `ambiguous-module` note, since a symbol
|
|
71
|
+
`python.app.main` may name a function of either and an import of `app` resolves only
|
|
72
|
+
from the importer's own directory, and each is covered as `ambiguous` rather than
|
|
73
|
+
analysed. An import of such a name that no file of the importer's own directory answers
|
|
74
|
+
is noted where it is written and not followed. Hidden directories, `node_modules`,
|
|
69
75
|
`__pycache__`, `build`, `dist` and any virtual environment, recognised by its
|
|
70
76
|
`pyvenv.cfg` whatever its name, are skipped. Dependency files at the root
|
|
71
77
|
(`requirements*.txt`, `pyproject.toml`, `poetry.lock`, `uv.lock`) are read into a
|
|
@@ -241,7 +247,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
241
247
|
```json
|
|
242
248
|
{
|
|
243
249
|
"schema_version": 1,
|
|
244
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
250
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.19.0"},
|
|
245
251
|
"root": "/home/me/project",
|
|
246
252
|
"findings": [
|
|
247
253
|
{
|
|
@@ -261,7 +267,9 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
261
267
|
}
|
|
262
268
|
```
|
|
263
269
|
|
|
264
|
-
`root` is the directory the paths are relative to.
|
|
270
|
+
`root` is the directory the paths are relative to. A file's `status` is `analysed`,
|
|
271
|
+
`ambiguous` (analysed, but another file has its module name, see above), `syntax-error`
|
|
272
|
+
or `unreadable`; only `analysed` files count in `files_analysed`.
|
|
265
273
|
|
|
266
274
|
A directory check also lists, in `tool.components`, what the result was produced with
|
|
267
275
|
besides the engine and the sources: each plugin it loaded, by manifest name and version,
|
|
@@ -273,7 +281,7 @@ bytes. A single-file check lists no components.
|
|
|
273
281
|
|
|
274
282
|
```json
|
|
275
283
|
"tool": {
|
|
276
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
284
|
+
"name": "coretrace-python-analyzer", "version": "0.19.0",
|
|
277
285
|
"components": [
|
|
278
286
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
279
287
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -372,6 +380,28 @@ Django. Most filters are functions of the same name in `django.template.defaultf
|
|
|
372
380
|
are `<name>_filter` there, and the filters of `i18n`, `l10n`, `tz` and `humanize` are in
|
|
373
381
|
their library's module (`timezone` is `django.templatetags.tz.do_timezone`).
|
|
374
382
|
|
|
383
|
+
A project tag library, a module of a `templatetags` directory, that registers a filter
|
|
384
|
+
of that name replaces the built-in in every template loading it, as Django's parser
|
|
385
|
+
does: `{% load custom %}{{ bio|striptags }}` calls the project's function when
|
|
386
|
+
`custom.py` registers a `striptags` filter, which is no call to the built-in; the
|
|
387
|
+
project's function is ordinary code, whose own calls are analysed as any other. A
|
|
388
|
+
load affects only the filters applied after it in the file, a later load overrides an
|
|
389
|
+
earlier one, `{% load striptags from custom %}` replaces only the filters it names, and
|
|
390
|
+
Django's own libraries (`static`, `i18n`, `l10n`, `tz`, `cache`, `humanize`) change
|
|
391
|
+
nothing. The analyzer reads the registrations on the library's `register`, a
|
|
392
|
+
`django.template.Library` however it is imported: `@register.filter`,
|
|
393
|
+
`@register.filter("name")`, `@register.filter(name="name", is_safe=True)`,
|
|
394
|
+
`register.filter("name", function)` and `register.filter(function)`, at module level or
|
|
395
|
+
under `if`, `try` or a function. Where it cannot tell what a library registers, the
|
|
396
|
+
filter is neither the built-in nor certainly replaced: a library found in no
|
|
397
|
+
`templatetags` directory (one an installed package ships, or that the settings'
|
|
398
|
+
`OPTIONS["libraries"]` or `OPTIONS["builtins"]` name, which are not read), a file it
|
|
399
|
+
cannot read or parse, a name given by a variable or unpacked arguments, `register`
|
|
400
|
+
passed to another function, filled through `filters=` or `register.filters`, or bound
|
|
401
|
+
to anything but a `Library`, or two apps giving the same library name and registering
|
|
402
|
+
different filters. The analyzer then claims no call, and counts the template among those
|
|
403
|
+
it could not read (below) when a filter of the library's name is applied under the load.
|
|
404
|
+
|
|
375
405
|
Attacker input in the render context makes the filter exploitable at the render call:
|
|
376
406
|
`render(request, "app/profile.html", {"bio": request.POST["bio"]})` with
|
|
377
407
|
`{{ bio|striptags }}`. The analyzer links a context value to a filter only where it is
|
|
@@ -386,7 +416,8 @@ certain of it:
|
|
|
386
416
|
arguments of the filters before it, or the output of a `{% filter %}` block. A name a
|
|
387
417
|
tag binds shadows the context, and `{% include ... only %}` passes none of it.
|
|
388
418
|
- The filter renders: a template extending another renders only its blocks, which
|
|
389
|
-
replace the blocks of that name in what it extends
|
|
419
|
+
replace the blocks of that name in what it extends, unless the block renders
|
|
420
|
+
`{{ block.super }}`, which keeps the parent's content and the filters it applies.
|
|
390
421
|
- One file has the template's name; of two files of the same name, which one renders
|
|
391
422
|
depends on the loaders.
|
|
392
423
|
|
|
@@ -399,9 +430,12 @@ With the `django.template.context_processors.request` context processor, a templ
|
|
|
399
430
|
rendered with the request reads it as `request`: `{{ request.GET.q|striptags }}` is
|
|
400
431
|
exploitable at the render call too. The analyzer counts it only where all of these hold:
|
|
401
432
|
|
|
402
|
-
- The project's settings assign `TEMPLATES` a literal list, every
|
|
403
|
-
engine of it lists the processor in a literal `context_processors`,
|
|
404
|
-
of the project names `TEMPLATES
|
|
433
|
+
- The project's settings assign `TEMPLATES` a literal list at module level, every
|
|
434
|
+
`DjangoTemplates` engine of it lists the processor in a literal `context_processors`,
|
|
435
|
+
and no other code of the project names the module's `TEMPLATES`, or a `TEMPLATES` it
|
|
436
|
+
does not bind, as a star import gives. A `TEMPLATES` bound in a function or a class
|
|
437
|
+
is another name: it configures nothing and changes nothing.
|
|
438
|
+
A mere mention of the processor proves nothing.
|
|
405
439
|
- The render call passes the request: `render` and `TemplateResponse` always do,
|
|
406
440
|
`render_to_string` when given `request`, `SimpleTemplateResponse` never.
|
|
407
441
|
- The render's context is certain, absent or a dict literal as above, so it cannot hide
|
|
@@ -502,10 +536,14 @@ A template the analyzer cannot read may apply any filter. The project names one
|
|
|
502
536
|
`render`, `render_to_string`, `TemplateResponse`, an `{% include %}` or an `{% extends %}`
|
|
503
537
|
names a template by an expression, or by a name found under no `templates` directory,
|
|
504
538
|
including one an installed package ships; a template file that cannot be read counts
|
|
505
|
-
too
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
a
|
|
539
|
+
too, and so does a template loading a tag library the analyzer cannot read, or find,
|
|
540
|
+
or finds in several apps registering different filters, where it then applies a filter
|
|
541
|
+
of a built-in's name, since the library may replace it. An advisory whose entry points
|
|
542
|
+
include a template filter then stays `under_investigation`, and the notes list where
|
|
543
|
+
the project names each such template (`app.views:12 names a template by an
|
|
544
|
+
expression`, `app/templates/app/profile.html:1 loads 'crispy_forms_tags', not found`).
|
|
545
|
+
A template named elsewhere, such as a class-based view's `template_name`, is not
|
|
546
|
+
checked.
|
|
509
547
|
|
|
510
548
|
`not_affected` needs the lock-file check because the analyzer reads the project's code,
|
|
511
549
|
not the code of installed packages. A framework calling the vulnerable function on the
|
|
@@ -513,7 +551,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
513
551
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
514
552
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
515
553
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
516
|
-
(`coretrace-python-analyzer 0.
|
|
554
|
+
(`coretrace-python-analyzer 0.19.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
517
555
|
`@id` changes with the advisory data a statement was decided with.
|
|
518
556
|
|
|
519
557
|
## Large projects
|
{coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/cache.py
RENAMED
|
@@ -42,7 +42,7 @@ from coretrace_python.interprocedural import (
|
|
|
42
42
|
from coretrace_python.semantic.symbols import SymbolId
|
|
43
43
|
from coretrace_python.source import SourceId, SourceSpan
|
|
44
44
|
|
|
45
|
-
CACHE_FORMAT =
|
|
45
|
+
CACHE_FORMAT = 11
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
@dataclass(frozen=True)
|
|
@@ -134,7 +134,7 @@ class ProjectCache:
|
|
|
134
134
|
def encode(module: CachedModule) -> dict[str, Any]:
|
|
135
135
|
return {
|
|
136
136
|
"format": CACHE_FORMAT,
|
|
137
|
-
"functions": [[f.name, _encode_span(f.span), f.entry_point] for f in module.functions],
|
|
137
|
+
"functions": [[f.name, _encode_span(f.span), f.entry_point, list(f.aliases)] for f in module.functions],
|
|
138
138
|
"summaries": {name: _encode_summary(s) for name, s in module.summaries.items()},
|
|
139
139
|
"sites": [_encode_site(site) for site in module.sites],
|
|
140
140
|
"findings": [_encode_finding(finding) for finding in module.findings],
|
|
@@ -196,8 +196,13 @@ def _decode_span(data: Any) -> SourceSpan:
|
|
|
196
196
|
|
|
197
197
|
|
|
198
198
|
def _decode_function(data: Any) -> ModuleFunction:
|
|
199
|
-
name, span, entry_point = data
|
|
200
|
-
return ModuleFunction(
|
|
199
|
+
name, span, entry_point, aliases = data
|
|
200
|
+
return ModuleFunction(
|
|
201
|
+
_string(name),
|
|
202
|
+
_decode_span(span),
|
|
203
|
+
None if entry_point is None else _string(entry_point),
|
|
204
|
+
tuple(_string(alias) for alias in aliases),
|
|
205
|
+
)
|
|
201
206
|
|
|
202
207
|
|
|
203
208
|
def _decode_read(data: Any) -> SymbolRead:
|
{coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.19.0}/src/coretrace_python/engine.py
RENAMED
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import concurrent.futures
|
|
10
10
|
import hashlib
|
|
11
11
|
import multiprocessing
|
|
12
|
+
from collections import Counter
|
|
12
13
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
13
14
|
from dataclasses import dataclass, field, replace
|
|
14
15
|
from pathlib import Path
|
|
@@ -88,6 +89,7 @@ from coretrace_python.interprocedural import (
|
|
|
88
89
|
TemplateCallsAnalysis,
|
|
89
90
|
build_module_graph,
|
|
90
91
|
discover_sources,
|
|
92
|
+
name_modules,
|
|
91
93
|
project_symbol,
|
|
92
94
|
)
|
|
93
95
|
from coretrace_python.ir import PYIR_SCHEMA_VERSION
|
|
@@ -371,7 +373,11 @@ def analyze_project(
|
|
|
371
373
|
strongly connected components of the module graph, imports first; with ``jobs``
|
|
372
374
|
above one the components of a wave are analysed in that many processes (§29).
|
|
373
375
|
``advisory_files`` add to the ``advisories.json`` at ``root``; ``policy_file``
|
|
374
|
-
replaces the ``coretrace-policy.toml`` there.
|
|
376
|
+
replaces the ``coretrace-policy.toml`` there.
|
|
377
|
+
|
|
378
|
+
Every module is known by the name ``name_modules`` gives its file: its import name,
|
|
379
|
+
or its path from the root when another file has that import name. Such files carry
|
|
380
|
+
an ``ambiguous-module`` note and are covered as ``ambiguous``."""
|
|
375
381
|
|
|
376
382
|
if jobs < 1:
|
|
377
383
|
raise ValueError("jobs must be at least 1")
|
|
@@ -398,18 +404,18 @@ def analyze_project(
|
|
|
398
404
|
files: dict[str, SourceFile] = {}
|
|
399
405
|
unreadable: list[tuple[Path, str]] = []
|
|
400
406
|
coverage: list[FileCoverage] = []
|
|
401
|
-
|
|
407
|
+
named = name_modules(root, discover_sources(root, sources, unreadable))
|
|
402
408
|
for path, reason in unreadable:
|
|
403
409
|
findings.append(_note("syntax-error", f"{path}: {reason}", sources.add_source(str(path), ""), 1))
|
|
404
410
|
coverage.append(FileCoverage(str(path), "unreadable", 0, 0))
|
|
405
|
-
for source in
|
|
411
|
+
for name, source in named.items():
|
|
406
412
|
try:
|
|
407
|
-
modules[
|
|
413
|
+
modules[name] = build_hir(source)
|
|
408
414
|
except (ParseError, HIRBuildError) as error:
|
|
409
415
|
findings.append(_note("syntax-error", str(error), source, 1))
|
|
410
416
|
coverage.append(FileCoverage(str(source.source_id), "syntax-error", 0, 0))
|
|
411
417
|
continue
|
|
412
|
-
files[
|
|
418
|
+
files[name] = source
|
|
413
419
|
|
|
414
420
|
managers = {name: _register_all(module) for name, module in modules.items()}
|
|
415
421
|
probe = next(iter(managers.values()), None) or _register_all(build_hir(sources.add_source("<empty>", "")))
|
|
@@ -439,6 +445,8 @@ def analyze_project(
|
|
|
439
445
|
graph = build_module_graph(
|
|
440
446
|
{name: files[name] for name in analysable}, {name: modules[name] for name in analysable}, imports
|
|
441
447
|
)
|
|
448
|
+
shared = _shared_names(graph)
|
|
449
|
+
findings.extend(_ambiguity_notes(graph, shared, root))
|
|
442
450
|
routes: dict[SymbolId, EntryPoint] = {}
|
|
443
451
|
for name in sorted(analysable):
|
|
444
452
|
for symbol, registered in _routes_of(analysable[name]).items():
|
|
@@ -447,7 +455,8 @@ def analyze_project(
|
|
|
447
455
|
templates = project_templates(root)
|
|
448
456
|
clearing = models.clearing(escaped)
|
|
449
457
|
template_calls = models.template_calls(
|
|
450
|
-
templates.filters,
|
|
458
|
+
templates.filters,
|
|
459
|
+
request_processor((modules[name], analysable[name].get(ScopeAnalysis)) for name in sorted(analysable)),
|
|
451
460
|
)
|
|
452
461
|
for manager in analysable.values():
|
|
453
462
|
manager.provide(RegisteredRoutes, routes)
|
|
@@ -514,15 +523,16 @@ def analyze_project(
|
|
|
514
523
|
if pool is not None:
|
|
515
524
|
pool.shutdown()
|
|
516
525
|
|
|
517
|
-
index =
|
|
526
|
+
index = _index(results, {name: name for name in results})
|
|
518
527
|
call_graphs: dict[str, CallGraph] = {}
|
|
519
528
|
functions: dict[str, tuple[ModuleFunction, ...]] = {}
|
|
520
529
|
for name in sorted(analysable):
|
|
521
530
|
entry = results[name]
|
|
522
531
|
findings.extend(entry.findings)
|
|
523
532
|
unsupported = sum(1 for f in entry.findings if f.rule_id == "unsupported-syntax")
|
|
533
|
+
status = "ambiguous" if files[name].module_name in shared else "analysed"
|
|
524
534
|
coverage.append(
|
|
525
|
-
FileCoverage(str(files[name].source_id),
|
|
535
|
+
FileCoverage(str(files[name].source_id), status, len(entry.functions), len(entry.functions) - unsupported)
|
|
526
536
|
)
|
|
527
537
|
functions[name] = entry.functions
|
|
528
538
|
sites: dict[str, list[CallSite]] = {function.name: [] for function in entry.functions}
|
|
@@ -644,24 +654,76 @@ def _advisory_paths(root: Path, advisory_files: Sequence[Path]) -> tuple[Path, .
|
|
|
644
654
|
return (*([default] if default.is_file() else []), *advisory_files)
|
|
645
655
|
|
|
646
656
|
|
|
657
|
+
def _shared_names(graph: ModuleGraph) -> dict[str, list[str]]:
|
|
658
|
+
"""The import names several analysable files have, each with those files' modules."""
|
|
659
|
+
|
|
660
|
+
by_name: dict[str, list[str]] = {}
|
|
661
|
+
for name in graph.modules:
|
|
662
|
+
by_name.setdefault(graph.source(name).module_name, []).append(name)
|
|
663
|
+
return {import_name: names for import_name, names in by_name.items() if len(names) > 1}
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def _ambiguity_notes(graph: ModuleGraph, shared: Mapping[str, list[str]], root: Path) -> list[Finding]:
|
|
667
|
+
"""One note on each file whose import name another file shares, since a symbol of
|
|
668
|
+
that name may denote a function of either, and one on each importer whose import of
|
|
669
|
+
such a name no root resolved, since calls through it are not followed."""
|
|
670
|
+
|
|
671
|
+
def located(name: str) -> str:
|
|
672
|
+
return _located(_path_of(graph.source(name)), root)
|
|
673
|
+
|
|
674
|
+
notes: list[Finding] = []
|
|
675
|
+
for import_name, names in sorted(shared.items()):
|
|
676
|
+
for name in names:
|
|
677
|
+
others = ", ".join(located(other) for other in names if other != name)
|
|
678
|
+
message = (
|
|
679
|
+
f"{located(name)} is the module '{import_name}' like {others}: a symbol "
|
|
680
|
+
f"python.{import_name}.* may name a function of either, and an import of "
|
|
681
|
+
f"'{import_name}' resolves only from its own root"
|
|
682
|
+
)
|
|
683
|
+
notes.append(_note("ambiguous-module", message, graph.source(name), 1))
|
|
684
|
+
for name in graph.modules:
|
|
685
|
+
for import_name in sorted(graph.unresolved(name)):
|
|
686
|
+
candidates = " or ".join(located(other) for other in shared[import_name])
|
|
687
|
+
message = f"'{import_name}' imported here is {candidates}: calls into it are not followed"
|
|
688
|
+
notes.append(_note("ambiguous-module", message, graph.source(name), 1))
|
|
689
|
+
return notes
|
|
690
|
+
|
|
691
|
+
|
|
647
692
|
def _seed(results: Mapping[str, CachedModule], graph: ModuleGraph, component: frozenset[str]) -> SummaryIndex:
|
|
648
693
|
"""The summaries a component starts from: those of every module it imports,
|
|
649
|
-
transitively, all final by the time its wave runs
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
694
|
+
transitively, all final by the time its wave runs, each under the import name the
|
|
695
|
+
component's code refers to it by. Of several files with one import name, only the
|
|
696
|
+
one a member imports directly, resolved in its own root, goes under that name: the
|
|
697
|
+
others are reached through summaries that inline them already, and a name no
|
|
698
|
+
member's root resolved is followed into neither file."""
|
|
699
|
+
|
|
700
|
+
wanted: set[str] = set()
|
|
701
|
+
pending = list(component)
|
|
702
|
+
while pending:
|
|
703
|
+
for imported in graph.imports(pending.pop()):
|
|
704
|
+
if imported in results and imported not in wanted:
|
|
705
|
+
wanted.add(imported)
|
|
706
|
+
pending.append(imported)
|
|
707
|
+
direct = {imported for member in component for imported in graph.imports(member)}
|
|
708
|
+
by_import: dict[str, list[str]] = {}
|
|
709
|
+
for name in wanted:
|
|
710
|
+
by_import.setdefault(graph.source(name).module_name, []).append(name)
|
|
711
|
+
names: dict[str, str] = {}
|
|
712
|
+
for import_name, modules in by_import.items():
|
|
713
|
+
chosen = modules if len(modules) == 1 else [name for name in modules if name in direct]
|
|
714
|
+
if len(chosen) == 1:
|
|
715
|
+
names[chosen[0]] = import_name
|
|
716
|
+
return _index(results, names)
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
def _index(results: Mapping[str, CachedModule], names: Mapping[str, str]) -> SummaryIndex:
|
|
720
|
+
"""The summaries of the modules in ``names``, each function under the project symbol
|
|
721
|
+
of the module name given for its module."""
|
|
722
|
+
|
|
661
723
|
return SummaryIndex(
|
|
662
724
|
{
|
|
663
|
-
project_symbol(
|
|
664
|
-
for name in sorted(
|
|
725
|
+
project_symbol(module, function): summary
|
|
726
|
+
for name, module in sorted(names.items())
|
|
665
727
|
for function, summary in results[name].summaries.items()
|
|
666
728
|
}
|
|
667
729
|
)
|
|
@@ -674,8 +736,12 @@ def _analyse_managers(
|
|
|
674
736
|
affected: Affected,
|
|
675
737
|
) -> dict[str, CachedModule]:
|
|
676
738
|
"""Analyse one component: iterate its summaries to a fixpoint over ``seed`` (§21),
|
|
677
|
-
then extract what the rest of the run needs from each module.
|
|
739
|
+
then extract what the rest of the run needs from each module. Each member registers
|
|
740
|
+
its functions under its import name, what the others' code refers to it by; a name
|
|
741
|
+
two members share, a cycle across roots, is registered by neither."""
|
|
678
742
|
|
|
743
|
+
counts = Counter(manager.module.name for manager in managers.values())
|
|
744
|
+
names = {name: manager.module.name for name, manager in managers.items() if counts[manager.module.name] == 1}
|
|
679
745
|
index = seed
|
|
680
746
|
for manager in managers.values():
|
|
681
747
|
manager.provide(ProjectSummaries, index)
|
|
@@ -684,9 +750,9 @@ def _analyse_managers(
|
|
|
684
750
|
{
|
|
685
751
|
**seed.summaries,
|
|
686
752
|
**{
|
|
687
|
-
project_symbol(
|
|
688
|
-
for name,
|
|
689
|
-
for function, summary in _summaries_of(
|
|
753
|
+
project_symbol(module, function): summary
|
|
754
|
+
for name, module in names.items()
|
|
755
|
+
for function, summary in _summaries_of(managers[name]).items()
|
|
690
756
|
},
|
|
691
757
|
}
|
|
692
758
|
)
|
|
@@ -701,8 +767,9 @@ def _analyse_managers(
|
|
|
701
767
|
|
|
702
768
|
@dataclass(frozen=True)
|
|
703
769
|
class _Batch:
|
|
704
|
-
"""One component handed to a worker process
|
|
705
|
-
|
|
770
|
+
"""One component handed to a worker process, its files under the engine's module
|
|
771
|
+
names: it rebuilds the configuration from the project root and the plugin roots, so
|
|
772
|
+
only paths and the imported summaries travel."""
|
|
706
773
|
|
|
707
774
|
root: Path
|
|
708
775
|
plugin_roots: tuple[Path, ...]
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""Analysis coverage: which files and functions a run actually looked at.
|
|
2
2
|
|
|
3
3
|
"No findings" only means something when the reader knows what was analysed. Each file
|
|
4
|
-
is ``analysed``,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
is ``analysed``, ``ambiguous`` (analysed, but another file has its Python module name,
|
|
5
|
+
so a symbol of that name may denote a function of either and an import of it resolves
|
|
6
|
+
only from its own root), a ``syntax-error`` (the frontend rejected it) or ``unreadable``
|
|
7
|
+
(it could not be decoded); analysed and ambiguous files count their functions and how
|
|
8
|
+
many lowered. The templates the project names but the run could not read are listed,
|
|
9
|
+
each where the project names it: they may call any template filter.
|
|
8
10
|
"""
|
|
9
11
|
|
|
10
12
|
from __future__ import annotations
|
|
@@ -19,6 +19,7 @@ from coretrace_python.interprocedural.modulegraph import (
|
|
|
19
19
|
build_module_graph,
|
|
20
20
|
discover_files,
|
|
21
21
|
discover_sources,
|
|
22
|
+
name_modules,
|
|
22
23
|
project_symbol,
|
|
23
24
|
)
|
|
24
25
|
from coretrace_python.interprocedural.summaries import (
|
|
@@ -73,5 +74,6 @@ __all__ = [
|
|
|
73
74
|
"cleared_by",
|
|
74
75
|
"discover_files",
|
|
75
76
|
"discover_sources",
|
|
77
|
+
"name_modules",
|
|
76
78
|
"project_symbol",
|
|
77
79
|
]
|
|
@@ -132,12 +132,16 @@ class SignaturesAnalysis(Analysis[tuple[Signature, ...]]):
|
|
|
132
132
|
@dataclass(frozen=True)
|
|
133
133
|
class ModuleFunction:
|
|
134
134
|
"""One function of a module as a project plugin sees it: the name the call graph
|
|
135
|
-
gives it (``Class.method``, ``outer.inner``, ``<module>``), where it is,
|
|
136
|
-
of the entry point it is (``http`` for a route, ``argv`` for a command), if any
|
|
135
|
+
gives it (``Class.method``, ``outer.inner``, ``<module>``), where it is, the label
|
|
136
|
+
of the entry point it is (``http`` for a route, ``argv`` for a command), if any, and
|
|
137
|
+
the module-level ``aliases`` bound to it by assigning one name to another
|
|
138
|
+
(``main = actual``), transitively and in source order; a method or a nested
|
|
139
|
+
function has none."""
|
|
137
140
|
|
|
138
141
|
name: str
|
|
139
142
|
span: SourceSpan
|
|
140
143
|
entry_point: str | None = None
|
|
144
|
+
aliases: tuple[str, ...] = ()
|
|
141
145
|
|
|
142
146
|
|
|
143
147
|
class CallGraph:
|