coretrace-python-analyzer 0.16.0__tar.gz → 0.18.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.16.0 → coretrace_python_analyzer-0.18.0}/CHANGELOG.md +16 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/docs/plugins.md +2 -1
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/docs/usage.md +6 -6
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/engine.py +7 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/interprocedural/__init__.py +4 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/interprocedural/callgraph.py +52 -7
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/interprocedural/summaries.py +6 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/taint/__init__.py +2 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/taint/engine.py +49 -11
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/taint/models.py +50 -3
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/taint/templates.py +11 -2
- coretrace_python_analyzer-0.18.0/tests/test_base_patterns.py +326 -0
- coretrace_python_analyzer-0.18.0/tests/test_declared_entry_points.py +155 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.18.0/tests/test_unreadable_templates.py +70 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/README.md +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/abstract/strings.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/cache.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dependency/advisories.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dependency/correlation.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dependency/graph.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/taint/urls.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/pygoat.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_advisory_url_host.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_code_injection.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_members.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_open_redirect_destinations.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_ssrf_destinations.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_template_context_flows.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_template_filter_calls.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_template_request_processor.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_url_proof.py +0 -0
- {coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.0 (2026-09-28)
|
|
4
|
+
|
|
5
|
+
### Precision
|
|
6
|
+
|
|
7
|
+
- A Django template file the analyzer cannot read, because of its permissions, no longer counts as escaping everything it renders: it read the file as empty text, and an empty template escapes. The flow through `render_to_string` stays `xss`, and a template extending or including the unreadable one is not established to escape either, as for a template the analyzer cannot find. An unreadable file was already listed among the templates the project names but the run could not read.
|
|
8
|
+
|
|
9
|
+
### Plugins
|
|
10
|
+
|
|
11
|
+
- An `EntryPoint` may name a project function or class by its own symbol, `python.app.handlers.main`, so an integration reading a deployment declaration, a SAM template or a `serverless.yml`, can make a bare Lambda handler an entry point; the engine itself infers nothing from a function's name. `EntryPoint.inputs` limits the parameters that are input to the given positions, after `self` for a method: the handler's event, not its context (#180).
|
|
12
|
+
|
|
13
|
+
## 0.17.0 (2026-09-28)
|
|
14
|
+
|
|
15
|
+
### Plugins
|
|
16
|
+
|
|
17
|
+
- `BasePattern(pattern, label, parameters, kinds)` makes the methods of a class whose base's canonical symbol matches a regular expression entry points, for bases generated per project such as a gRPC servicer's, whatever the module's prefix and however the base is imported. Each parameter after `self` is, by position, either input of the model's kinds or an object of a class the model names, which the sources on that class taint without the object being input: a `Source` on `ServicerContext.invocation_metadata` gives the client's metadata, and `peer()` gives nothing. When the project holds the base, only the methods the base defines are entry points, so a servicer's utility methods are not; otherwise every method with the listed number of parameters is. `async` methods and the messages of an iterated parameter are covered (#176).
|
|
18
|
+
|
|
3
19
|
## 0.16.0 (2026-09-27)
|
|
4
20
|
|
|
5
21
|
### Advisories
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.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
|
|
@@ -151,9 +151,10 @@ class BottleModels(ModelPlugin):
|
|
|
151
151
|
| `Source(symbol, label, kinds=ALL)` | A call or attribute whose value is attacker-controlled; `label` names it in messages (`http`, `stdin`, `argv`, `http-response`). `kinds` restricts what it can inject. Text input, such as a form field, a header or a URL parameter, carries `TEXT_KINDS`, every kind but `NOSQL`: a string cannot hold a query operator. |
|
|
152
152
|
| `Sink(symbol, kinds, positions=(), keywords=())` | A call dangerous for `kinds`. `positions` limits a kind to the given argument indexes, and `keywords` to the given keyword names; a restricted kind reaches only the arguments listed there. SQL sinks read the statement only, and the SSRF sinks of HTTP clients read the URL, by position or as `url=`. A starred argument or `**kwargs` reaches no restricted kind. |
|
|
153
153
|
| `Sanitizer(symbol, kinds)` | A call whose result is safe for `kinds`, including when the call is inside a project function the data goes through. |
|
|
154
|
-
| `EntryPoint(symbol, label, kinds=ALL)` | A decorator or a class base whose functions receive their parameters as `label` input, such as `flask.Flask.route
|
|
154
|
+
| `EntryPoint(symbol, label, kinds=ALL, inputs=None)` | A decorator or a class base whose functions receive their parameters as `label` input, such as `flask.Flask.route`; or a project function or class named by its own symbol (`python.app.handlers.main`), as an integration reading a deployment declaration names a Lambda handler. `inputs` limits the parameters that are input to those positions, after `self` for a method: a handler's event, not its context. |
|
|
155
155
|
| `TypedParameter(symbol, label, kinds=ALL)` | A class whose annotated parameters carry `label` input, such as Django's `HttpRequest`. |
|
|
156
156
|
| `NamedParameter(pattern, label, kinds)` | A regular expression on parameter names; matching parameters carry `kinds`, as `password` carries `CREDENTIAL`. |
|
|
157
|
+
| `BasePattern(pattern, label, parameters=(None,), kinds=ALL)` | A regular expression on the canonical symbol of a class's base, for bases generated per project such as a gRPC servicer's (`(?:^|\.)\w+_pb2_grpc\.\w+Servicer$`), whatever the module's prefix and however the base is imported. The methods taking the parameters listed, by position after `self`, are entry points: a position left `None` is `label` input carrying `kinds`; one holding a class symbol denotes an object of that class, which the sources on the class taint, as `invocation_metadata()` on a `ServicerContext`, without the object itself being input. When the project holds the base, only the methods the base defines count; otherwise every method with that number of parameters. Inheritance identifies a potential service, not its registration on a server. |
|
|
157
158
|
| `RouteRegistrar(symbol, argument, label, kinds=ALL, keyword=None)` | A call registering a view at argument `argument` (or `keyword`), such as Django's `path`; the view receives `label` input. |
|
|
158
159
|
| `SuffixSink(suffix, kinds, positions=())` | A sink matched by the end of the symbol, for methods of any class such as `objects.raw`. |
|
|
159
160
|
| `SafeArgument(symbol, argument, values, position=None, kinds=ALL)` | A call to the sink `symbol` whose `argument` (by keyword, or at `position`) denotes one of `values` is not a sink for `kinds`, such as `yaml.load` with `Loader=yaml.SafeLoader`. Values are written as the analyzer records arguments: a symbol by its canonical name, a constant as Python writes it (`True`). Only a value given explicitly makes the call safe. |
|
|
@@ -156,9 +156,9 @@ It establishes escaping only when all of the following hold:
|
|
|
156
156
|
- No Python file of the project sets `autoescape` to `False`.
|
|
157
157
|
|
|
158
158
|
The analyzer never assumes escaping where it cannot read the template. That covers a
|
|
159
|
-
template
|
|
160
|
-
`templates`, one shipped by an installed package, one
|
|
161
|
-
and Flask's Jinja2 templates. The flow is then reported as before. A single-file check
|
|
159
|
+
template file it cannot read, a template named by an expression, a template found
|
|
160
|
+
through a `DIRS` entry not named `templates`, one shipped by an installed package, one
|
|
161
|
+
rendered through `get_template`, and Flask's Jinja2 templates. The flow is then reported as before. A single-file check
|
|
162
162
|
reads no template.
|
|
163
163
|
|
|
164
164
|
### Dangerous API usage
|
|
@@ -241,7 +241,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
241
241
|
```json
|
|
242
242
|
{
|
|
243
243
|
"schema_version": 1,
|
|
244
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
244
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.18.0"},
|
|
245
245
|
"root": "/home/me/project",
|
|
246
246
|
"findings": [
|
|
247
247
|
{
|
|
@@ -273,7 +273,7 @@ bytes. A single-file check lists no components.
|
|
|
273
273
|
|
|
274
274
|
```json
|
|
275
275
|
"tool": {
|
|
276
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
276
|
+
"name": "coretrace-python-analyzer", "version": "0.18.0",
|
|
277
277
|
"components": [
|
|
278
278
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
279
279
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -513,7 +513,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
513
513
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
514
514
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
515
515
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
516
|
-
(`coretrace-python-analyzer 0.
|
|
516
|
+
(`coretrace-python-analyzer 0.18.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
517
517
|
`@id` changes with the advisory data a statement was decided with.
|
|
518
518
|
|
|
519
519
|
## Large projects
|
{coretrace_python_analyzer-0.16.0 → coretrace_python_analyzer-0.18.0}/src/coretrace_python/engine.py
RENAMED
|
@@ -80,6 +80,7 @@ from coretrace_python.interprocedural import (
|
|
|
80
80
|
ModuleFunction,
|
|
81
81
|
ModuleGraph,
|
|
82
82
|
ProjectSummaries,
|
|
83
|
+
SignaturesAnalysis,
|
|
83
84
|
SummaryAnalysis,
|
|
84
85
|
SummaryIndex,
|
|
85
86
|
SymbolRead,
|
|
@@ -164,6 +165,7 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
|
|
|
164
165
|
EscapedTemplates,
|
|
165
166
|
ClearingAnalysis,
|
|
166
167
|
TemplateCallsAnalysis,
|
|
168
|
+
SignaturesAnalysis,
|
|
167
169
|
)
|
|
168
170
|
|
|
169
171
|
|
|
@@ -205,6 +207,7 @@ class ResultsEvicted(TransformationPass):
|
|
|
205
207
|
EscapedTemplates,
|
|
206
208
|
ClearingAnalysis,
|
|
207
209
|
TemplateCallsAnalysis,
|
|
210
|
+
SignaturesAnalysis,
|
|
208
211
|
}
|
|
209
212
|
)
|
|
210
213
|
|
|
@@ -268,6 +271,7 @@ def build_manager(
|
|
|
268
271
|
table = (models or SecurityModelRegistry()).freeze()
|
|
269
272
|
manager.provide(SecurityModelAnalysis, table)
|
|
270
273
|
manager.provide(MembersAnalysis, table.members_by_class())
|
|
274
|
+
manager.provide(SignaturesAnalysis, table.signatures())
|
|
271
275
|
manager.provide(ClearingAnalysis, table.clearing())
|
|
272
276
|
manager.provide(ProjectSummaries, SummaryIndex())
|
|
273
277
|
manager.provide(RegisteredRoutes, _routes_of(manager))
|
|
@@ -323,6 +327,7 @@ def analyze_file(source: SourceFile, plugin_roots: Sequence[Path]) -> FileAnalys
|
|
|
323
327
|
table = plugin_models(loaded.plugin for loaded in registry)
|
|
324
328
|
manager.provide(SecurityModelAnalysis, table)
|
|
325
329
|
manager.provide(MembersAnalysis, table.members_by_class())
|
|
330
|
+
manager.provide(SignaturesAnalysis, table.signatures())
|
|
326
331
|
manager.provide(ClearingAnalysis, table.clearing())
|
|
327
332
|
manager.provide(ProjectSummaries, SummaryIndex())
|
|
328
333
|
manager.provide(RegisteredRoutes, _routes_of(manager))
|
|
@@ -418,6 +423,7 @@ def analyze_project(
|
|
|
418
423
|
for manager in managers.values():
|
|
419
424
|
manager.provide(SecurityModelAnalysis, models)
|
|
420
425
|
manager.provide(MembersAnalysis, members)
|
|
426
|
+
manager.provide(SignaturesAnalysis, models.signatures())
|
|
421
427
|
manager.provide(DependencyAnalysis, dependencies)
|
|
422
428
|
|
|
423
429
|
imports: dict[str, ImportTable] = {}
|
|
@@ -730,6 +736,7 @@ def _analyse_batch(batch: _Batch) -> dict[str, dict[str, Any]]:
|
|
|
730
736
|
for manager in managers.values():
|
|
731
737
|
manager.provide(SecurityModelAnalysis, models)
|
|
732
738
|
manager.provide(MembersAnalysis, members)
|
|
739
|
+
manager.provide(SignaturesAnalysis, models.signatures())
|
|
733
740
|
manager.provide(DependencyAnalysis, dependencies)
|
|
734
741
|
manager.provide(RegisteredRoutes, routes)
|
|
735
742
|
manager.provide(EscapedTemplates, escaped)
|
|
@@ -8,6 +8,8 @@ from coretrace_python.interprocedural.callgraph import (
|
|
|
8
8
|
ExternalSymbol,
|
|
9
9
|
KnownFunction,
|
|
10
10
|
ModuleFunction,
|
|
11
|
+
Signature,
|
|
12
|
+
SignaturesAnalysis,
|
|
11
13
|
SymbolRead,
|
|
12
14
|
Target,
|
|
13
15
|
UnknownTarget,
|
|
@@ -56,6 +58,8 @@ __all__ = [
|
|
|
56
58
|
"Mutation",
|
|
57
59
|
"NonlocalWrite",
|
|
58
60
|
"ProjectSummaries",
|
|
61
|
+
"Signature",
|
|
62
|
+
"SignaturesAnalysis",
|
|
59
63
|
"SummaryAnalysis",
|
|
60
64
|
"SummaryIndex",
|
|
61
65
|
"SummaryTable",
|
|
@@ -9,6 +9,7 @@ reads, called or not: reading ``request.form`` runs its getter.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
+
import re
|
|
12
13
|
from collections.abc import Mapping
|
|
13
14
|
from dataclasses import dataclass, field
|
|
14
15
|
from types import MappingProxyType
|
|
@@ -104,6 +105,30 @@ class SymbolRead:
|
|
|
104
105
|
symbol: SymbolId
|
|
105
106
|
|
|
106
107
|
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class Signature:
|
|
110
|
+
"""What the parameters of the methods of a class denote when the canonical symbol
|
|
111
|
+
of its base matches ``pattern``, by position after ``self``: a class symbol, or None
|
|
112
|
+
for a parameter denoting none. A method taking another number of parameters has
|
|
113
|
+
none. The engine derives signatures from the security models (§16)."""
|
|
114
|
+
|
|
115
|
+
pattern: str
|
|
116
|
+
parameters: tuple[SymbolId | None, ...]
|
|
117
|
+
|
|
118
|
+
def matches(self, base: SymbolId) -> bool:
|
|
119
|
+
return re.search(self.pattern, base.canonical_name) is not None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class SignaturesAnalysis(Analysis[tuple[Signature, ...]]):
|
|
123
|
+
"""The signatures the models declare, provided by the engine; none on its own."""
|
|
124
|
+
|
|
125
|
+
name: ClassVar[str] = "interprocedural.signatures"
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def compute(cls, ctx: AnalysisContext) -> tuple[Signature, ...]:
|
|
129
|
+
return ()
|
|
130
|
+
|
|
131
|
+
|
|
107
132
|
@dataclass(frozen=True)
|
|
108
133
|
class ModuleFunction:
|
|
109
134
|
"""One function of a module as a project plugin sees it: the name the call graph
|
|
@@ -357,6 +382,20 @@ def _annotated(
|
|
|
357
382
|
return found
|
|
358
383
|
|
|
359
384
|
|
|
385
|
+
def _declared(
|
|
386
|
+
function: nodes.Function, ssa: FunctionIR, bases: tuple[SymbolId, ...], signatures: tuple[Signature, ...]
|
|
387
|
+
) -> dict[Value, SymbolId]:
|
|
388
|
+
"""Parameters a signature types by their position, for a method of a class one of
|
|
389
|
+
whose ``bases`` matches the signature and which takes the parameters it lists."""
|
|
390
|
+
|
|
391
|
+
given = function.parameters[1:]
|
|
392
|
+
for signature in signatures:
|
|
393
|
+
if any(signature.matches(base) for base in bases) and len(given) == len(signature.parameters):
|
|
394
|
+
typed = zip(ssa.parameters[1:], signature.parameters, strict=False)
|
|
395
|
+
return {value: symbol for value, symbol in typed if symbol is not None}
|
|
396
|
+
return {}
|
|
397
|
+
|
|
398
|
+
|
|
360
399
|
def _typed_with_module_classes(
|
|
361
400
|
function: nodes.Function, ssa: FunctionIR, classes: Mapping[str, frozenset[str]]
|
|
362
401
|
) -> dict[Value, str]:
|
|
@@ -372,12 +411,15 @@ def _typed_with_module_classes(
|
|
|
372
411
|
|
|
373
412
|
class CallGraphAnalysis(Analysis[CallGraph]):
|
|
374
413
|
name: ClassVar[str] = "interprocedural.callgraph"
|
|
375
|
-
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset(
|
|
414
|
+
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset(
|
|
415
|
+
{SSAAnalysis, ScopeAnalysis, SymbolAnalysis, SignaturesAnalysis}
|
|
416
|
+
)
|
|
376
417
|
|
|
377
418
|
@classmethod
|
|
378
419
|
def compute(cls, ctx: AnalysisContext) -> CallGraph:
|
|
379
420
|
scopes = ctx.get(ScopeAnalysis)
|
|
380
421
|
table = ctx.get(SymbolAnalysis)
|
|
422
|
+
signatures = ctx.get(SignaturesAnalysis)
|
|
381
423
|
definitions: dict[str, nodes.Function] = {}
|
|
382
424
|
for function in analyzable_functions(ctx.module):
|
|
383
425
|
# A property and its setter, or a redefinition, share a qualified name; each
|
|
@@ -396,14 +438,15 @@ class CallGraphAnalysis(Analysis[CallGraph]):
|
|
|
396
438
|
for statement in ctx.module.body
|
|
397
439
|
if isinstance(statement, nodes.Class)
|
|
398
440
|
}
|
|
441
|
+
# Inheritance follows the first base that resolves; a signature may match any.
|
|
399
442
|
bases: dict[str, SymbolId] = {}
|
|
443
|
+
ancestry: dict[str, tuple[SymbolId, ...]] = {}
|
|
400
444
|
for statement in ctx.module.body:
|
|
401
445
|
if isinstance(statement, nodes.Class):
|
|
402
|
-
for
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
break
|
|
446
|
+
resolved = [table.resolve_expression(scopes.module_scope.id, base) for base in statement.bases]
|
|
447
|
+
ancestry[statement.name] = tuple(symbol for symbol in resolved if symbol is not None)
|
|
448
|
+
if ancestry[statement.name]:
|
|
449
|
+
bases[statement.name] = ancestry[statement.name][0]
|
|
407
450
|
sites: dict[str, tuple[CallSite, ...]] = {}
|
|
408
451
|
symbols: dict[str, Mapping[Value, SymbolId]] = {}
|
|
409
452
|
reads: dict[str, tuple[SymbolRead, ...]] = {}
|
|
@@ -418,11 +461,13 @@ class CallGraphAnalysis(Analysis[CallGraph]):
|
|
|
418
461
|
owner = name.rsplit(".", 1)[0] if "." in name and name.rsplit(".", 1)[0] in classes else None
|
|
419
462
|
if owner is not None and _is_staticmethod(function):
|
|
420
463
|
owner = None # a static method has no receiver
|
|
464
|
+
# A signature types parameters by their position; an annotation says more.
|
|
465
|
+
declared = _declared(function, ssa, ancestry.get(owner, ()) if owner is not None else (), signatures)
|
|
421
466
|
targets, symbols[name] = resolve_targets(
|
|
422
467
|
ssa,
|
|
423
468
|
scopes,
|
|
424
469
|
known,
|
|
425
|
-
_annotated(function, ssa, scopes, table),
|
|
470
|
+
{**declared, **_annotated(function, ssa, scopes, table)},
|
|
426
471
|
frozenset(definitions),
|
|
427
472
|
classes,
|
|
428
473
|
owner,
|
|
@@ -363,6 +363,12 @@ class SummaryIndex:
|
|
|
363
363
|
def summary(self, symbol: SymbolId) -> FunctionSummary | None:
|
|
364
364
|
return self.summaries.get(symbol)
|
|
365
365
|
|
|
366
|
+
def defines(self, owner: SymbolId) -> bool:
|
|
367
|
+
"""Whether the project defines a function of ``owner``, a class or a module."""
|
|
368
|
+
|
|
369
|
+
prefix = owner.canonical_name + "."
|
|
370
|
+
return any(s.canonical_name.startswith(prefix) for s in self.symbols)
|
|
371
|
+
|
|
366
372
|
def __eq__(self, other: object) -> bool:
|
|
367
373
|
return isinstance(other, SummaryIndex) and dict(self.summaries) == dict(other.summaries)
|
|
368
374
|
|
|
@@ -12,6 +12,7 @@ from coretrace_python.taint.engine import (
|
|
|
12
12
|
from coretrace_python.taint.models import (
|
|
13
13
|
TEXT_KINDS,
|
|
14
14
|
AuthorizationGuard,
|
|
15
|
+
BasePattern,
|
|
15
16
|
EntryPoint,
|
|
16
17
|
Model,
|
|
17
18
|
ModelError,
|
|
@@ -47,6 +48,7 @@ __all__ = [
|
|
|
47
48
|
"FILTER_FUNCTIONS",
|
|
48
49
|
"TEXT_KINDS",
|
|
49
50
|
"AuthorizationGuard",
|
|
51
|
+
"BasePattern",
|
|
50
52
|
"EntryPoint",
|
|
51
53
|
"EntryPointAnalysis",
|
|
52
54
|
"EscapedTemplates",
|
|
@@ -80,6 +80,7 @@ from coretrace_python.semantic.symbols import SymbolAnalysis, SymbolId, SymbolTa
|
|
|
80
80
|
from coretrace_python.source import SourceSpan
|
|
81
81
|
from coretrace_python.taint.models import (
|
|
82
82
|
TEXT_KINDS,
|
|
83
|
+
BasePattern,
|
|
83
84
|
EntryPoint,
|
|
84
85
|
ModelTable,
|
|
85
86
|
SecurityModelAnalysis,
|
|
@@ -780,9 +781,11 @@ def entry_point_of(
|
|
|
780
781
|
symbols: SymbolTable,
|
|
781
782
|
owner: nodes.Class | None = None,
|
|
782
783
|
instances: Instances | None = None,
|
|
784
|
+
project: SummaryIndex | None = None,
|
|
783
785
|
) -> EntryPoint | None:
|
|
784
786
|
"""The entry-point model matching one of the function's decorators or, for a
|
|
785
|
-
method, one of the bases of ``owner``, if any
|
|
787
|
+
method, one of the bases of ``owner``, if any: by symbol, or by a base pattern when
|
|
788
|
+
the method is one the pattern covers (``project`` says what the base defines)."""
|
|
786
789
|
|
|
787
790
|
scope = scopes.scope_for(function)
|
|
788
791
|
enclosing = scope.parent if scope.parent is not None else scope.id
|
|
@@ -803,9 +806,24 @@ def entry_point_of(
|
|
|
803
806
|
entry = models.entry_point(candidate)
|
|
804
807
|
if entry is not None:
|
|
805
808
|
return entry
|
|
809
|
+
pattern = models.base_pattern(candidate) if id(expression) in enclosing_of else None
|
|
810
|
+
if pattern is not None and _covered(function, candidate, pattern, project):
|
|
811
|
+
return EntryPoint(candidate, pattern.label, pattern.kinds)
|
|
806
812
|
return None
|
|
807
813
|
|
|
808
814
|
|
|
815
|
+
def _covered(function: nodes.Function, base: SymbolId, pattern: BasePattern, project: SummaryIndex | None) -> bool:
|
|
816
|
+
"""Whether a method of a class deriving from ``base`` is one ``pattern`` covers: it
|
|
817
|
+
takes the parameters the pattern lists and, when the project holds the base, the
|
|
818
|
+
base defines it; a utility method of the class is no entry point."""
|
|
819
|
+
|
|
820
|
+
if len(function.parameters) - 1 != len(pattern.parameters):
|
|
821
|
+
return False
|
|
822
|
+
if project is None or not project.defines(base):
|
|
823
|
+
return True
|
|
824
|
+
return project.summary(base.attribute(function.name)) is not None
|
|
825
|
+
|
|
826
|
+
|
|
809
827
|
def function_entry_point(
|
|
810
828
|
function: nodes.Function,
|
|
811
829
|
module: nodes.Module,
|
|
@@ -814,24 +832,34 @@ def function_entry_point(
|
|
|
814
832
|
symbols: SymbolTable,
|
|
815
833
|
instances: Instances | None = None,
|
|
816
834
|
routes: Routes | None = None,
|
|
835
|
+
project: SummaryIndex | None = None,
|
|
817
836
|
) -> EntryPoint | None:
|
|
818
837
|
"""The entry point ``function`` is: one of its decorators or, for a method, one of its
|
|
819
|
-
class's bases matching an entry-point model,
|
|
838
|
+
class's bases matching an entry-point model, a registration elsewhere (``routes``), or
|
|
839
|
+
a model naming the function, or its class, by its own project symbol."""
|
|
820
840
|
|
|
821
841
|
owner = _owner(module, function)
|
|
822
842
|
enclosing_function = _enclosing(module, function)
|
|
823
843
|
if enclosing_function is not None:
|
|
824
844
|
# ``app = Flask(__name__)`` inside ``create_app``: routes defined there resolve.
|
|
825
845
|
instances = {**(instances or {}), **local_instances(enclosing_function, scopes, symbols)}
|
|
826
|
-
entry = entry_point_of(function, models, scopes, symbols, owner, instances)
|
|
827
|
-
if entry is None
|
|
846
|
+
entry = entry_point_of(function, models, scopes, symbols, owner, instances, project)
|
|
847
|
+
if entry is None:
|
|
828
848
|
qualified = function.name if owner is None else f"{owner.name}.{function.name}"
|
|
829
|
-
entry =
|
|
849
|
+
entry = _declared(project_symbol(module.name, qualified), models, routes)
|
|
830
850
|
if entry is None and owner is not None:
|
|
831
|
-
entry =
|
|
851
|
+
entry = _declared(project_symbol(module.name, owner.name), models, routes)
|
|
832
852
|
return entry
|
|
833
853
|
|
|
834
854
|
|
|
855
|
+
def _declared(symbol: SymbolId, models: ModelTable, routes: Routes | None) -> EntryPoint | None:
|
|
856
|
+
"""The entry point a project ``symbol`` is: registered as a handler, or named by a
|
|
857
|
+
model."""
|
|
858
|
+
|
|
859
|
+
registered = routes.get(symbol) if routes else None
|
|
860
|
+
return registered if registered is not None else models.entry_point(symbol)
|
|
861
|
+
|
|
862
|
+
|
|
835
863
|
def _owner(module: nodes.Module, function: nodes.Function) -> nodes.Class | None:
|
|
836
864
|
"""The top-level class ``function`` is a method of, if any."""
|
|
837
865
|
|
|
@@ -849,23 +877,31 @@ def parameter_sources(
|
|
|
849
877
|
symbols: SymbolTable,
|
|
850
878
|
instances: Instances | None = None,
|
|
851
879
|
routes: Routes | None = None,
|
|
880
|
+
project: SummaryIndex | None = None,
|
|
852
881
|
) -> Mapping[int, Source]:
|
|
853
882
|
"""The attacker-controlled parameters of ``function``, by index: every parameter of an
|
|
854
883
|
entry point (``self`` excepted for a method), including one registered elsewhere
|
|
855
884
|
(``routes``), and every parameter annotated with a typed-parameter symbol. An entry
|
|
856
885
|
point's parameter annotated with scalars is text: FastAPI validates it before the
|
|
857
886
|
handler runs, and a scalar cannot hold a query operator. So are the URL parameters
|
|
858
|
-
a view receives after a request object.
|
|
887
|
+
a view receives after a request object. A parameter a base pattern types with a
|
|
888
|
+
class is not input: the sources on that class say what it gives."""
|
|
859
889
|
|
|
860
890
|
owner = _owner(module, function)
|
|
861
891
|
sources: dict[int, Source] = {}
|
|
862
892
|
scope = scopes.scope_for(function)
|
|
863
893
|
enclosing = scope.parent if scope.parent is not None else scope.id
|
|
864
|
-
entry = function_entry_point(function, module, models, scopes, symbols, instances, routes)
|
|
894
|
+
entry = function_entry_point(function, module, models, scopes, symbols, instances, routes, project)
|
|
865
895
|
if entry is not None:
|
|
866
896
|
first = 1 if owner is not None else 0
|
|
867
897
|
request = models.request_object(entry.symbol)
|
|
898
|
+
pattern = models.base_pattern(entry.symbol) if models.entry_point(entry.symbol) is None else None
|
|
899
|
+
roles = pattern.parameters if pattern is not None else ()
|
|
868
900
|
for index in range(first, len(function.parameters)):
|
|
901
|
+
if index - first < len(roles) and roles[index - first] is not None:
|
|
902
|
+
continue
|
|
903
|
+
if entry.inputs is not None and index - first not in entry.inputs:
|
|
904
|
+
continue
|
|
869
905
|
annotation = function.parameters[index].annotation
|
|
870
906
|
text = (request is not None and index > first) or (
|
|
871
907
|
annotation is not None and _scalar(annotation, symbols, enclosing)
|
|
@@ -1010,11 +1046,12 @@ class TaintAnalysis(FunctionAnalysis[TaintFacts]):
|
|
|
1010
1046
|
graph = ctx.get(CallGraphAnalysis)
|
|
1011
1047
|
models = ctx.get(SecurityModelAnalysis)
|
|
1012
1048
|
scopes, symbols = ctx.get(ScopeAnalysis), ctx.get(SymbolAnalysis)
|
|
1013
|
-
|
|
1049
|
+
project = ctx.get(ProjectSummaries)
|
|
1050
|
+
instances = factory_instances(ctx.module, scopes, symbols, ctx.get(SummaryAnalysis), project)
|
|
1014
1051
|
routes = ctx.get(RegisteredRoutes)
|
|
1015
1052
|
|
|
1016
1053
|
def sources_of(member: nodes.Function) -> Mapping[int, Source]:
|
|
1017
|
-
return parameter_sources(member, ctx.module, models, scopes, symbols, instances, routes)
|
|
1054
|
+
return parameter_sources(member, ctx.module, models, scopes, symbols, instances, routes, project)
|
|
1018
1055
|
|
|
1019
1056
|
ssa = ctx.get(SSAAnalysis, function)
|
|
1020
1057
|
heap = ctx.get(HeapAnalysis, function)
|
|
@@ -1068,9 +1105,10 @@ class EntryPointAnalysis(Analysis[tuple[ModuleFunction, ...]]):
|
|
|
1068
1105
|
scopes, symbols = ctx.get(ScopeAnalysis), ctx.get(SymbolAnalysis)
|
|
1069
1106
|
instances = factory_instances(ctx.module, scopes, symbols, ctx.get(SummaryAnalysis), ctx.get(ProjectSummaries))
|
|
1070
1107
|
routes = ctx.get(RegisteredRoutes)
|
|
1108
|
+
project = ctx.get(ProjectSummaries)
|
|
1071
1109
|
functions: list[ModuleFunction] = []
|
|
1072
1110
|
for name, function in graph.definitions.items():
|
|
1073
|
-
entry = function_entry_point(function, ctx.module, models, scopes, symbols, instances, routes)
|
|
1111
|
+
entry = function_entry_point(function, ctx.module, models, scopes, symbols, instances, routes, project)
|
|
1074
1112
|
functions.append(ModuleFunction(name, function.span, entry.label if entry is not None else None))
|
|
1075
1113
|
return tuple(functions)
|
|
1076
1114
|
|
|
@@ -17,7 +17,7 @@ from types import MappingProxyType
|
|
|
17
17
|
from typing import ClassVar
|
|
18
18
|
|
|
19
19
|
from coretrace_python.analysis import Analysis, AnalysisContext, MissingInputError
|
|
20
|
-
from coretrace_python.interprocedural import Clearing, TemplateCalls, TemplateFilter
|
|
20
|
+
from coretrace_python.interprocedural import Clearing, Signature, TemplateCalls, TemplateFilter
|
|
21
21
|
from coretrace_python.semantic.symbols import Members, SymbolId
|
|
22
22
|
|
|
23
23
|
|
|
@@ -110,12 +110,17 @@ class Sanitizer:
|
|
|
110
110
|
|
|
111
111
|
@dataclass(frozen=True)
|
|
112
112
|
class EntryPoint:
|
|
113
|
-
"""Functions decorated by ``symbol``,
|
|
114
|
-
|
|
113
|
+
"""Functions decorated by ``symbol``, methods of classes deriving from it, and, when
|
|
114
|
+
``symbol`` names a project function or class, that function or the class's methods
|
|
115
|
+
receive attacker-controlled parameters: those at ``inputs``, by position after
|
|
116
|
+
``self`` for a method, or every parameter when ``inputs`` is None. A deployment
|
|
117
|
+
declaration an integration reads names a Lambda handler this way, whose event is
|
|
118
|
+
input and whose context is not."""
|
|
115
119
|
|
|
116
120
|
symbol: SymbolId
|
|
117
121
|
label: str
|
|
118
122
|
kinds: TaintKind = TaintKind.ALL
|
|
123
|
+
inputs: tuple[int, ...] | None = None
|
|
119
124
|
|
|
120
125
|
|
|
121
126
|
@dataclass(frozen=True)
|
|
@@ -145,6 +150,32 @@ class NamedParameter:
|
|
|
145
150
|
return re.search(self.pattern, name) is not None
|
|
146
151
|
|
|
147
152
|
|
|
153
|
+
@dataclass(frozen=True)
|
|
154
|
+
class BasePattern:
|
|
155
|
+
"""Methods of a class whose base's canonical symbol matches ``pattern`` are entry
|
|
156
|
+
points labelled ``label``, when they take the parameters the model lists, by
|
|
157
|
+
position after ``self``: a position left None is attacker input carrying ``kinds``;
|
|
158
|
+
one holding a class symbol denotes an object of that class, which the sources on
|
|
159
|
+
the class taint. A gRPC servicer's RPCs take the request, or the iterator of the
|
|
160
|
+
client's messages, and a ``ServicerContext``. When the project holds the base,
|
|
161
|
+
only the methods it defines are entry points; otherwise every method with that
|
|
162
|
+
number of parameters. Inheritance identifies a potential service, not its
|
|
163
|
+
registration on a server."""
|
|
164
|
+
|
|
165
|
+
pattern: str
|
|
166
|
+
label: str
|
|
167
|
+
parameters: tuple[SymbolId | None, ...] = (None,)
|
|
168
|
+
kinds: TaintKind = TaintKind.ALL
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def symbol(self) -> SymbolId:
|
|
172
|
+
digest = hashlib.sha1(self.pattern.encode("utf-8")).hexdigest()[:12]
|
|
173
|
+
return SymbolId(f"python.base.p{digest}")
|
|
174
|
+
|
|
175
|
+
def matches(self, base: SymbolId) -> bool:
|
|
176
|
+
return re.search(self.pattern, base.canonical_name) is not None
|
|
177
|
+
|
|
178
|
+
|
|
148
179
|
@dataclass(frozen=True)
|
|
149
180
|
class RouteRegistrar:
|
|
150
181
|
"""A call registering a handler elsewhere (``path('login/', views.log_in)``): the
|
|
@@ -251,6 +282,7 @@ Model = (
|
|
|
251
282
|
| TemplateRender
|
|
252
283
|
| Members
|
|
253
284
|
| RequestObject
|
|
285
|
+
| BasePattern
|
|
254
286
|
)
|
|
255
287
|
|
|
256
288
|
|
|
@@ -270,6 +302,7 @@ class ModelTable:
|
|
|
270
302
|
template_renders: tuple[TemplateRender, ...] = ()
|
|
271
303
|
members: tuple[Members, ...] = ()
|
|
272
304
|
request_objects: tuple[RequestObject, ...] = ()
|
|
305
|
+
base_patterns: tuple[BasePattern, ...] = ()
|
|
273
306
|
_by_symbol: dict[type[Model], dict[SymbolId, Model]] = field(
|
|
274
307
|
init=False, repr=False, compare=False
|
|
275
308
|
)
|
|
@@ -287,6 +320,7 @@ class ModelTable:
|
|
|
287
320
|
SafeArgument: {m.symbol: m for m in self.safe_arguments},
|
|
288
321
|
TemplateRender: {m.symbol: m for m in self.template_renders},
|
|
289
322
|
RequestObject: {m.symbol: m for m in self.request_objects},
|
|
323
|
+
BasePattern: {m.symbol: m for m in self.base_patterns},
|
|
290
324
|
}
|
|
291
325
|
object.__setattr__(self, "_by_symbol", MappingProxyType(index))
|
|
292
326
|
|
|
@@ -356,8 +390,20 @@ class ModelTable:
|
|
|
356
390
|
self.template_renders,
|
|
357
391
|
self.members,
|
|
358
392
|
self.request_objects,
|
|
393
|
+
self.base_patterns,
|
|
359
394
|
)
|
|
360
395
|
|
|
396
|
+
def base_pattern(self, base: SymbolId) -> BasePattern | None:
|
|
397
|
+
"""The pattern model the canonical symbol of a class's ``base`` matches, if any."""
|
|
398
|
+
|
|
399
|
+
return next((m for m in self.base_patterns if m.matches(base)), None)
|
|
400
|
+
|
|
401
|
+
def signatures(self) -> tuple[Signature, ...]:
|
|
402
|
+
"""What the methods of the classes the base patterns match receive, by position,
|
|
403
|
+
for the call graph to type them."""
|
|
404
|
+
|
|
405
|
+
return tuple(Signature(m.pattern, m.parameters) for m in self.base_patterns)
|
|
406
|
+
|
|
361
407
|
def sanitizer(self, symbol: SymbolId) -> Sanitizer | None:
|
|
362
408
|
found = self._by_symbol[Sanitizer].get(symbol)
|
|
363
409
|
return found if isinstance(found, Sanitizer) else None
|
|
@@ -451,6 +497,7 @@ class SecurityModelRegistry:
|
|
|
451
497
|
template_renders=tuple(m for m in models if isinstance(m, TemplateRender)),
|
|
452
498
|
members=tuple(m for m in models if isinstance(m, Members)),
|
|
453
499
|
request_objects=tuple(m for m in models if isinstance(m, RequestObject)),
|
|
500
|
+
base_patterns=tuple(m for m in models if isinstance(m, BasePattern)),
|
|
454
501
|
)
|
|
455
502
|
|
|
456
503
|
|
|
@@ -15,7 +15,8 @@ it extends or includes, can output data unescaped:
|
|
|
15
15
|
|
|
16
16
|
Templates are found under the project's ``templates`` directories, by their path below
|
|
17
17
|
one. A template the engine cannot find — named by an expression, under a ``DIRS`` entry
|
|
18
|
-
of another name, shipped by an installed package — is never assumed to
|
|
18
|
+
of another name, shipped by an installed package — or cannot read is never assumed to
|
|
19
|
+
escape.
|
|
19
20
|
|
|
20
21
|
A template applying a filter of Django's own libraries calls the function behind it
|
|
21
22
|
whenever it is rendered, though no Python call names that function: ``{{ bio|striptags }}``
|
|
@@ -149,7 +150,7 @@ def escaped_templates(root: Path) -> frozenset[str]:
|
|
|
149
150
|
local: dict[str, bool] = {}
|
|
150
151
|
requires: dict[str, set[str]] = {}
|
|
151
152
|
for name, paths in found.items():
|
|
152
|
-
verdicts = [
|
|
153
|
+
verdicts = [_verdict(path) for path in paths]
|
|
153
154
|
local[name] = all(safe for safe, _ in verdicts)
|
|
154
155
|
requires[name] = {needed for _, needs in verdicts for needed in needs}
|
|
155
156
|
escaped = {name for name, safe in local.items() if safe}
|
|
@@ -171,6 +172,14 @@ def _read(path: Path) -> str:
|
|
|
171
172
|
return _text(path) or ""
|
|
172
173
|
|
|
173
174
|
|
|
175
|
+
def _verdict(path: Path) -> tuple[bool, set[str]]:
|
|
176
|
+
"""Whether one template file escapes all it prints, and what it needs: a file the
|
|
177
|
+
engine cannot read is not established to escape, like a template it cannot find."""
|
|
178
|
+
|
|
179
|
+
text = _text(path)
|
|
180
|
+
return (False, set()) if text is None else _inspect(text)
|
|
181
|
+
|
|
182
|
+
|
|
174
183
|
def _text(path: Path) -> str | None:
|
|
175
184
|
"""The text of ``path``, or None when it cannot be read."""
|
|
176
185
|
|