coretrace-python-analyzer 0.19.0__tar.gz → 0.20.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.19.0 → coretrace_python_analyzer-0.20.0}/CHANGELOG.md +14 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/docs/plugins.md +3 -1
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/docs/usage.md +29 -3
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/__init__.py +2 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/heap.py +4 -1
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cache.py +6 -4
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/advisories.py +4 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/correlation.py +16 -3
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/graph.py +4 -1
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/callgraph.py +61 -5
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/__init__.py +2 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/engine.py +53 -1
- coretrace_python_analyzer-0.20.0/tests/test_key_taint.py +229 -0
- coretrace_python_analyzer-0.20.0/tests/test_keyword_name_conditions.py +243 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_release.py +1 -1
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/README.md +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/strings.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/engine.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/summaries.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/templates.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/urls.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/pygoat.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_advisory_url_host.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_base_patterns.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_block_super.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_code_injection.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_declared_entry_points.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_entry_point_aliases.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_function_aliases_api.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_members.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_module_identity.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_open_redirect_destinations.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_settings_scope.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_ssrf_destinations.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_context_flows.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_filter_calls.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_filter_overrides.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_request_processor.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_unreadable_templates.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_url_proof.py +0 -0
- {coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.20.0 (2026-09-30)
|
|
4
|
+
|
|
5
|
+
### Advisories
|
|
6
|
+
|
|
7
|
+
- An advisory condition of kind `keyword_name` says the attacker must choose a keyword name of the call, as Django's ORM takes column aliases and lookups (`annotate(**{alias: expr})`); it carries its text and nothing else. The engine decides it from the keys of what the call expands with `**`, apart from the mapping's values: keys carrying attacker input meet it and the call is exploitable; constant keys, a dict literal with string keys, `dict(total=...)` or plain keywords, contradict it whatever the values carry, so the call is ruled out (`app.views:12 python.app.models.Item.objects.annotate(keywords=total)`, `(keywords absent)` for a call passing no keyword) and a project passing only those stays `not_affected`; keys the engine cannot establish, a `**extra` parameter, a mapping built elsewhere, `dict(zip(...))`, leave it pending review, so the call is reachable, and exploitable when the mapping's values carry input (#193).
|
|
8
|
+
|
|
9
|
+
### Analysis
|
|
10
|
+
|
|
11
|
+
- The keys of a mapping carry taint of their own, apart from its values: a dict literal `{alias: expr}`, `dict([(alias, expr)])`, a comprehension `{k: expr for k in input}` and a subscript assignment `mapping[alias] = expr` taint the `keys` location of the mapping's abstract object, which `{**mapping}`, `dict(mapping)` and `dict(**mapping)` copy; the value taint of a literal is what it was. A call expanding such a mapping with `**` passes its keys as the keyword names of the call, a way of passing only an advisory sink reads. Keys through a project wrapper are not followed; `mapping.update(other)`, `mapping.setdefault(k, v)`, `for k in mapping` and `mapping.keys()` carry no key taint; a mapping bound by a `Phi` is unknown (#193).
|
|
12
|
+
|
|
13
|
+
### Plugins
|
|
14
|
+
|
|
15
|
+
- `Arguments.keyword_unpacked` says a call expands a `**mapping` whose keys it does not write; a `**` literal with constant keys that nothing else uses is recorded as its keywords, so `given()` decides safe arguments and argument conditions for it. The cache format changes.
|
|
16
|
+
|
|
3
17
|
## 0.19.0 (2026-09-29)
|
|
4
18
|
|
|
5
19
|
### Analysis
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.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
|
|
@@ -194,7 +194,9 @@ A model plugin may also carry `advisories`, a tuple of `Advisory` values
|
|
|
194
194
|
(`coretrace_python.dependency`) with the package, the vulnerable range and the affected
|
|
195
195
|
symbols, as the shipped `sample-advisories` plugin does. Requirements matching them are
|
|
196
196
|
reported, calls to the affected symbols become reachable vulnerabilities and tainted
|
|
197
|
-
calls become exploitable ones.
|
|
197
|
+
calls become exploitable ones. An advisory's entry points carry `conditions`, of kind
|
|
198
|
+
`argument`, `host`, `semantic` or `keyword_name`, and `attacker_arguments`, decided as
|
|
199
|
+
the [usage guide](usage.md) describes.
|
|
198
200
|
|
|
199
201
|
### Reclassifying findings: `ProjectPlugin.refine`
|
|
200
202
|
|
|
@@ -247,7 +247,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
247
247
|
```json
|
|
248
248
|
{
|
|
249
249
|
"schema_version": 1,
|
|
250
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
250
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.20.0"},
|
|
251
251
|
"root": "/home/me/project",
|
|
252
252
|
"findings": [
|
|
253
253
|
{
|
|
@@ -281,7 +281,7 @@ bytes. A single-file check lists no components.
|
|
|
281
281
|
|
|
282
282
|
```json
|
|
283
283
|
"tool": {
|
|
284
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
284
|
+
"name": "coretrace-python-analyzer", "version": "0.20.0",
|
|
285
285
|
"components": [
|
|
286
286
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
287
287
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -488,6 +488,32 @@ attacker's input is passed in that argument. The outcome has three cases:
|
|
|
488
488
|
|
|
489
489
|
The other conditions, the environment included, stay pending as before.
|
|
490
490
|
|
|
491
|
+
A `keyword_name` condition says the attacker must choose a keyword name of the call,
|
|
492
|
+
as Django's ORM takes column aliases and lookups (`annotate(**{alias: expr})`):
|
|
493
|
+
`{"kind": "keyword_name", "text": "…"}`, and nothing else. The engine decides it from
|
|
494
|
+
the keys of what the call expands with `**`; the values of the mapping do not decide
|
|
495
|
+
it. The outcome has three cases:
|
|
496
|
+
|
|
497
|
+
- Keys carrying attacker input meet it, and the call is exploitable. Key taint is
|
|
498
|
+
carried, apart from value taint, for a dict literal `{alias: expr}`,
|
|
499
|
+
`dict([(alias, expr)])`, a comprehension `{k: expr for k in request.GET}` and a
|
|
500
|
+
subscript assignment `mapping[alias] = expr`, and copied by `{**mapping}` and
|
|
501
|
+
`dict(mapping)`.
|
|
502
|
+
- Constant keys — a dict literal with string keys, `dict(total=…)`, plain keywords —
|
|
503
|
+
contradict it, whatever the values carry: the call is ruled out
|
|
504
|
+
(`app.views:12 python.app.models.Item.objects.annotate(keywords=total)`, or
|
|
505
|
+
`(keywords absent)` for a call passing no keyword), and a project passing only those
|
|
506
|
+
stays `not_affected`.
|
|
507
|
+
- Keys the engine cannot establish — a `**extra` parameter, a mapping built elsewhere
|
|
508
|
+
or returned by a function, `dict(zip(...))`, `**request.GET` — leave it pending
|
|
509
|
+
review, as any unknown argument does: the call is reachable, and exploitable when the
|
|
510
|
+
mapping's values carry attacker input.
|
|
511
|
+
|
|
512
|
+
Keys passed through a project wrapper stay pending, and `mapping.update(other)`,
|
|
513
|
+
`mapping.setdefault(k, v)`, `for k in mapping` and `mapping.keys()` carry no key
|
|
514
|
+
taint. A literal the function fills after building it (`m = {}; m[alias] = expr`)
|
|
515
|
+
has keys the literal does not write, so the call is not ruled out.
|
|
516
|
+
|
|
491
517
|
Every dependency finding records the highest level of evidence established in its
|
|
492
518
|
`level` metadata: `declared` (the requirement allows a vulnerable version), `imported`
|
|
493
519
|
(a module of the package is imported somewhere), `reachable` (an entry point or affected
|
|
@@ -551,7 +577,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
551
577
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
552
578
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
553
579
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
554
|
-
(`coretrace-python-analyzer 0.
|
|
580
|
+
(`coretrace-python-analyzer 0.20.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
555
581
|
`@id` changes with the advisory data a statement was decided with.
|
|
556
582
|
|
|
557
583
|
## Large projects
|
|
@@ -8,6 +8,7 @@ from coretrace_python.abstract.constants import (
|
|
|
8
8
|
from coretrace_python.abstract.heap import (
|
|
9
9
|
ATTRIBUTES,
|
|
10
10
|
ELEMENTS,
|
|
11
|
+
KEYS,
|
|
11
12
|
MUTATORS,
|
|
12
13
|
AbstractObject,
|
|
13
14
|
AliasSet,
|
|
@@ -24,6 +25,7 @@ from coretrace_python.abstract.values import AbstractValue, Truth
|
|
|
24
25
|
__all__ = [
|
|
25
26
|
"ATTRIBUTES",
|
|
26
27
|
"ELEMENTS",
|
|
28
|
+
"KEYS",
|
|
27
29
|
"MUTATORS",
|
|
28
30
|
"AbstractObject",
|
|
29
31
|
"AbstractValue",
|
|
@@ -6,7 +6,8 @@ globals and imported symbols by name, and the fields loaded from an object by fi
|
|
|
6
6
|
Each value points to an ``AliasSet`` of objects, computed by a flow-insensitive
|
|
7
7
|
points-to fixpoint over the SSA form; each object has two ``HeapLocation`` fields,
|
|
8
8
|
``elements`` for its items and ``attributes`` for its attributes, field-insensitive
|
|
9
|
-
within each
|
|
9
|
+
within each, and a third, ``keys``, for the keys of a mapping, which only the taint
|
|
10
|
+
engine fills. Taint and dependence analyses key their states by these locations, so a
|
|
10
11
|
store, a mutating method call or a load on any alias reads and writes the same place.
|
|
11
12
|
"""
|
|
12
13
|
|
|
@@ -47,6 +48,8 @@ from coretrace_python.source import SourceSpan
|
|
|
47
48
|
|
|
48
49
|
ELEMENTS = "elements"
|
|
49
50
|
ATTRIBUTES = "attributes"
|
|
51
|
+
# The keys of a mapping, stored by the taint engine; the points-to solver never fills it.
|
|
52
|
+
KEYS = "keys"
|
|
50
53
|
|
|
51
54
|
# Method names that store their arguments into the receiver's elements.
|
|
52
55
|
MUTATORS = frozenset(
|
{coretrace_python_analyzer-0.19.0 → coretrace_python_analyzer-0.20.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 = 12
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
@dataclass(frozen=True)
|
|
@@ -368,6 +368,7 @@ def _encode_arguments(arguments: Arguments) -> dict[str, Any]:
|
|
|
368
368
|
"positional": list(arguments.positional),
|
|
369
369
|
"keywords": [[name, value] for name, value in arguments.keywords],
|
|
370
370
|
"unpacked": arguments.unpacked,
|
|
371
|
+
"keyword_unpacked": arguments.keyword_unpacked,
|
|
371
372
|
}
|
|
372
373
|
|
|
373
374
|
|
|
@@ -375,11 +376,12 @@ def _decode_arguments(data: Mapping[str, Any]) -> Arguments:
|
|
|
375
376
|
def denoted(value: Any) -> str | None:
|
|
376
377
|
return None if value is None else _string(value)
|
|
377
378
|
|
|
378
|
-
unpacked = data["unpacked"]
|
|
379
|
-
if not isinstance(unpacked, bool):
|
|
380
|
-
raise TypeError(f"expected
|
|
379
|
+
unpacked, keyword_unpacked = data["unpacked"], data["keyword_unpacked"]
|
|
380
|
+
if not isinstance(unpacked, bool) or not isinstance(keyword_unpacked, bool):
|
|
381
|
+
raise TypeError(f"expected booleans, got {unpacked!r} and {keyword_unpacked!r}")
|
|
381
382
|
return Arguments(
|
|
382
383
|
tuple(denoted(value) for value in data["positional"]),
|
|
383
384
|
tuple((_string(name), denoted(value)) for name, value in data["keywords"]),
|
|
384
385
|
unpacked,
|
|
386
|
+
keyword_unpacked,
|
|
385
387
|
)
|
|
@@ -263,4 +263,8 @@ def _condition(entry: Mapping[str, Any]) -> Condition:
|
|
|
263
263
|
(condition.argument is None and condition.position is None) or condition.values or condition.default
|
|
264
264
|
):
|
|
265
265
|
raise ValueError("a host condition names the URL argument, by keyword or position, and nothing else")
|
|
266
|
+
if condition.kind == "keyword_name" and (
|
|
267
|
+
condition.argument is not None or condition.position is not None or condition.values or condition.default
|
|
268
|
+
):
|
|
269
|
+
raise ValueError("a keyword_name condition carries its text and nothing else")
|
|
266
270
|
return condition
|
|
@@ -23,7 +23,7 @@ from coretrace_python.findings import Confidence, Finding, Severity
|
|
|
23
23
|
from coretrace_python.findings.refutation import Status, Verdict, Verdicts
|
|
24
24
|
from coretrace_python.interprocedural import Arguments, CallSite, ExternalSymbol
|
|
25
25
|
from coretrace_python.semantic.symbols import SymbolId
|
|
26
|
-
from coretrace_python.taint import Sink, TaintFlow, TaintKind
|
|
26
|
+
from coretrace_python.taint import KEYWORD_NAMES, Sink, TaintFlow, TaintKind
|
|
27
27
|
from coretrace_python.taint.urls import UrlProof, redirects_disabled
|
|
28
28
|
|
|
29
29
|
Affected = Mapping[SymbolId, tuple[Advisory, ...]]
|
|
@@ -72,13 +72,25 @@ def check_conditions(
|
|
|
72
72
|
a ``host`` condition, what ``url`` proves of the value the attacker's input is passed
|
|
73
73
|
in (``passed_as``). A host the URL's constant text fixes rules the call out when the
|
|
74
74
|
call disables redirects; otherwise a redirect may still lead to a host the attacker
|
|
75
|
-
controls, and the condition stays pending with that uncertainty.
|
|
75
|
+
controls, and the condition stays pending with that uncertainty. A ``keyword_name``
|
|
76
|
+
condition is met when the attacker's input is passed as the keys of a ``**`` mapping,
|
|
77
|
+
pending when the call expands a mapping whose keys are not established, and
|
|
78
|
+
contradicted by a call writing every keyword name."""
|
|
76
79
|
|
|
77
80
|
if entry is None:
|
|
78
81
|
return ConditionCheck()
|
|
79
82
|
met: list[Condition] = []
|
|
80
83
|
pending: list[Condition] = []
|
|
81
84
|
for condition in entry.conditions:
|
|
85
|
+
if condition.kind == "keyword_name":
|
|
86
|
+
if any(keyword == KEYWORD_NAMES for _, keyword in passed_as):
|
|
87
|
+
met.append(condition)
|
|
88
|
+
elif arguments is None or arguments.keyword_unpacked:
|
|
89
|
+
pending.append(condition)
|
|
90
|
+
else:
|
|
91
|
+
names = ", ".join(sorted({name for name, _ in arguments.keywords}))
|
|
92
|
+
return ConditionCheck(tuple(met), tuple(pending), condition, names or None)
|
|
93
|
+
continue
|
|
82
94
|
if condition.kind == "host":
|
|
83
95
|
origin = url.origin if url is not None and _passed_in(condition, passed_as) else None
|
|
84
96
|
if origin is None:
|
|
@@ -119,7 +131,8 @@ def ruled_out(module: str, site: CallSite, check: ConditionCheck) -> str:
|
|
|
119
131
|
python.yaml.load(Loader=python.yaml.SafeLoader)``."""
|
|
120
132
|
|
|
121
133
|
assert check.contradicted is not None and isinstance(site.target, ExternalSymbol)
|
|
122
|
-
|
|
134
|
+
contradicted = check.contradicted
|
|
135
|
+
argument = contradicted.argument or ("keywords" if contradicted.kind == "keyword_name" else f"#{contradicted.position}")
|
|
123
136
|
passed = f"{argument} absent" if check.passed is None else f"{argument}={check.passed}"
|
|
124
137
|
return f"{module}:{site.location.start_line} {site.target.symbol}({passed})"
|
|
125
138
|
|
|
@@ -154,7 +154,10 @@ class Condition:
|
|
|
154
154
|
``default`` says an absent argument means a vulnerable value. A ``host`` condition
|
|
155
155
|
says the attacker must choose the host of the URL passed as ``argument`` (or at
|
|
156
156
|
``position``); the engine decides it from what the URL's constant text proves. A
|
|
157
|
-
``
|
|
157
|
+
``keyword_name`` condition says the attacker must choose a keyword name of the call,
|
|
158
|
+
as an ORM alias expanded from a mapping; the engine decides it from the keys of what
|
|
159
|
+
the call expands with ``**``. A ``semantic`` condition cannot be checked and is
|
|
160
|
+
reported as pending review."""
|
|
158
161
|
|
|
159
162
|
kind: str
|
|
160
163
|
text: str
|
|
@@ -18,6 +18,7 @@ from typing import ClassVar
|
|
|
18
18
|
from coretrace_python.analysis import Analysis, AnalysisContext, AnyAnalysis
|
|
19
19
|
from coretrace_python.cfg import CFGError
|
|
20
20
|
from coretrace_python.hir import nodes
|
|
21
|
+
from coretrace_python.ir.defuse import DefUse, def_use
|
|
21
22
|
from coretrace_python.ir.lowering import (
|
|
22
23
|
MODULE_BODY,
|
|
23
24
|
LoweringError,
|
|
@@ -26,6 +27,7 @@ from coretrace_python.ir.lowering import (
|
|
|
26
27
|
)
|
|
27
28
|
from coretrace_python.ir.model import (
|
|
28
29
|
Await,
|
|
30
|
+
BuildDict,
|
|
29
31
|
Call,
|
|
30
32
|
Constant,
|
|
31
33
|
FunctionIR,
|
|
@@ -68,11 +70,15 @@ class Arguments:
|
|
|
68
70
|
(``python.yaml.FullLoader``), a constant as Python writes it (``True``, ``'/static'``),
|
|
69
71
|
or None for anything else (a parameter, an expression). ``unpacked`` says the call
|
|
70
72
|
unpacks ``*args`` or ``**kwargs``, so an argument it does not give explicitly may
|
|
71
|
-
still be given; after ``*args`` no position is known, and ``positional`` is empty.
|
|
73
|
+
still be given; after ``*args`` no position is known, and ``positional`` is empty.
|
|
74
|
+
``keyword_unpacked`` says the call expands a ``**mapping`` whose keys it does not
|
|
75
|
+
write, so a keyword name may be anything; a literal with constant keys is written out
|
|
76
|
+
as its keywords, and ``unpacked`` stays true for it only when ``*args`` is also given."""
|
|
72
77
|
|
|
73
78
|
positional: tuple[str | None, ...] = ()
|
|
74
79
|
keywords: tuple[tuple[str, str | None], ...] = ()
|
|
75
80
|
unpacked: bool = False
|
|
81
|
+
keyword_unpacked: bool = False
|
|
76
82
|
|
|
77
83
|
def given(self, keyword: str | None, position: int | None = None) -> tuple[bool, str | None] | None:
|
|
78
84
|
"""Whether the call gives an argument, by ``keyword`` or at ``position``, and what it
|
|
@@ -245,8 +251,45 @@ def derive_symbols(
|
|
|
245
251
|
# passed as an argument is not recorded, so call sites stay small in the cache.
|
|
246
252
|
MAX_CONSTANT_LENGTH = 64
|
|
247
253
|
|
|
254
|
+
_DICT = SymbolId("python.builtins.dict")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _keyword_entries(
|
|
258
|
+
value: Value, symbols: Mapping[Value, SymbolId], defs: Mapping[Value, Instruction], uses: DefUse
|
|
259
|
+
) -> tuple[tuple[str, Value], ...] | None:
|
|
260
|
+
"""The keywords a ``**value`` gives when every name is established: the string keys
|
|
261
|
+
of a dict literal, the keywords of ``dict(...)``, through what they unpack in turn;
|
|
262
|
+
None when a key is computed, the mapping is built elsewhere, or anything but the
|
|
263
|
+
call uses it, since a store or a mutating call may add a key the literal does not
|
|
264
|
+
write."""
|
|
265
|
+
|
|
266
|
+
if len(uses.uses(value)) != 1:
|
|
267
|
+
return None
|
|
268
|
+
made = defs.get(value)
|
|
269
|
+
entries: list[tuple[str, Value]] = []
|
|
270
|
+
if isinstance(made, BuildDict):
|
|
271
|
+
for key, item in made.items:
|
|
272
|
+
constant = defs.get(key)
|
|
273
|
+
if not isinstance(constant, Constant) or not isinstance(constant.value, str):
|
|
274
|
+
return None
|
|
275
|
+
entries.append((constant.value, item))
|
|
276
|
+
nested = made.unpacked
|
|
277
|
+
elif isinstance(made, Call) and symbols.get(made.callee) == _DICT and not made.arguments and not made.starred:
|
|
278
|
+
nested = tuple(v for name, v in made.keywords if name is None)
|
|
279
|
+
entries.extend((name, v) for name, v in made.keywords if name is not None)
|
|
280
|
+
else:
|
|
281
|
+
return None
|
|
282
|
+
for inner in nested:
|
|
283
|
+
found = _keyword_entries(inner, symbols, defs, uses)
|
|
284
|
+
if found is None:
|
|
285
|
+
return None
|
|
286
|
+
entries.extend(found)
|
|
287
|
+
return tuple(entries)
|
|
288
|
+
|
|
248
289
|
|
|
249
|
-
def _arguments(
|
|
290
|
+
def _arguments(
|
|
291
|
+
call: Call, symbols: Mapping[Value, SymbolId], defs: Mapping[Value, Instruction], uses: DefUse
|
|
292
|
+
) -> Arguments:
|
|
250
293
|
def denoted(value: Value) -> str | None:
|
|
251
294
|
symbol = symbols.get(value)
|
|
252
295
|
if symbol is not None:
|
|
@@ -257,10 +300,22 @@ def _arguments(call: Call, symbols: Mapping[Value, SymbolId], defs: Mapping[Valu
|
|
|
257
300
|
written = repr(made.value)
|
|
258
301
|
return written if len(written) <= MAX_CONSTANT_LENGTH else None
|
|
259
302
|
|
|
303
|
+
keywords: list[tuple[str, str | None]] = []
|
|
304
|
+
keyword_unpacked = False
|
|
305
|
+
for name, value in call.keywords:
|
|
306
|
+
if name is not None:
|
|
307
|
+
keywords.append((name, denoted(value)))
|
|
308
|
+
continue
|
|
309
|
+
entries = _keyword_entries(value, symbols, defs, uses)
|
|
310
|
+
if entries is None:
|
|
311
|
+
keyword_unpacked = True
|
|
312
|
+
else:
|
|
313
|
+
keywords.extend((key, denoted(item)) for key, item in entries)
|
|
260
314
|
return Arguments(
|
|
261
315
|
() if call.starred else tuple(denoted(value) for value in call.arguments),
|
|
262
|
-
tuple(
|
|
263
|
-
bool(call.starred) or
|
|
316
|
+
tuple(keywords),
|
|
317
|
+
bool(call.starred) or keyword_unpacked,
|
|
318
|
+
keyword_unpacked,
|
|
264
319
|
)
|
|
265
320
|
|
|
266
321
|
|
|
@@ -480,6 +535,7 @@ class CallGraphAnalysis(Analysis[CallGraph]):
|
|
|
480
535
|
table=table,
|
|
481
536
|
)
|
|
482
537
|
defs = {i.result: i for block in ssa.blocks for i in block.instructions if i.result is not None}
|
|
538
|
+
uses = def_use(ssa)
|
|
483
539
|
found: list[CallSite] = []
|
|
484
540
|
for block in ssa.blocks:
|
|
485
541
|
for instruction in block.instructions:
|
|
@@ -489,7 +545,7 @@ class CallGraphAnalysis(Analysis[CallGraph]):
|
|
|
489
545
|
name,
|
|
490
546
|
instruction.location,
|
|
491
547
|
targets.get(instruction.callee, UnknownTarget()),
|
|
492
|
-
_arguments(instruction, symbols[name], defs),
|
|
548
|
+
_arguments(instruction, symbols[name], defs, uses),
|
|
493
549
|
)
|
|
494
550
|
)
|
|
495
551
|
sites[name] = tuple(found)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from coretrace_python.semantic.symbols import Members
|
|
4
4
|
from coretrace_python.taint.engine import (
|
|
5
|
+
KEYWORD_NAMES,
|
|
5
6
|
EntryPointAnalysis,
|
|
6
7
|
Taint,
|
|
7
8
|
TaintAnalysis,
|
|
@@ -46,6 +47,7 @@ from coretrace_python.taint.templates import (
|
|
|
46
47
|
|
|
47
48
|
__all__ = [
|
|
48
49
|
"FILTER_FUNCTIONS",
|
|
50
|
+
"KEYWORD_NAMES",
|
|
49
51
|
"TEXT_KINDS",
|
|
50
52
|
"AuthorizationGuard",
|
|
51
53
|
"BasePattern",
|
|
@@ -18,6 +18,7 @@ from typing import ClassVar
|
|
|
18
18
|
from coretrace_python.abstract import (
|
|
19
19
|
ATTRIBUTES,
|
|
20
20
|
ELEMENTS,
|
|
21
|
+
KEYS,
|
|
21
22
|
HeapAnalysis,
|
|
22
23
|
HeapFacts,
|
|
23
24
|
HeapLocation,
|
|
@@ -130,12 +131,18 @@ class TaintFlow:
|
|
|
130
131
|
# What the arguments of the sink call denote, to decide the conditions on them.
|
|
131
132
|
sink_arguments: Arguments | None = None
|
|
132
133
|
# How the tainted value is passed to the sink call: at a position, by a keyword, or
|
|
133
|
-
# (None, None) unpacked with ``*`` or ``**``; ``f(x, key=x)`` passes it twice.
|
|
134
|
+
# (None, None) unpacked with ``*`` or ``**``; ``f(x, key=x)`` passes it twice. The
|
|
135
|
+
# keyword ``KEYWORD_NAMES`` says the value's keys are the keyword names of the call.
|
|
134
136
|
passed_as: frozenset[tuple[int | None, str | None]] = frozenset()
|
|
135
137
|
|
|
136
138
|
|
|
137
139
|
Key = Value | HeapLocation
|
|
138
140
|
|
|
141
|
+
# The ``keyword`` of a ``passed_as`` way whose value's keys become the keyword names of
|
|
142
|
+
# the call: ``f(**m)`` passes the keys of ``m`` as ``(None, KEYWORD_NAMES)``.
|
|
143
|
+
KEYWORD_NAMES = "**"
|
|
144
|
+
_DICT = SymbolId("python.builtins.dict")
|
|
145
|
+
|
|
139
146
|
|
|
140
147
|
class TaintFacts:
|
|
141
148
|
def __init__(self, taints: Mapping[Key, Taint], flows: tuple[TaintFlow, ...]) -> None:
|
|
@@ -217,6 +224,14 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
217
224
|
taint = taint.join(state.get(location, Taint.none()))
|
|
218
225
|
return taint
|
|
219
226
|
|
|
227
|
+
def keyed(self, value: Value, state: Mapping[Key, Taint]) -> Taint:
|
|
228
|
+
"""The taint of the keys of the mappings ``value`` points to."""
|
|
229
|
+
|
|
230
|
+
taint = Taint.none()
|
|
231
|
+
for location in self.heap.locations(value, KEYS):
|
|
232
|
+
taint = taint.join(state.get(location, Taint.none()))
|
|
233
|
+
return taint
|
|
234
|
+
|
|
220
235
|
def initial(self) -> State:
|
|
221
236
|
state: dict[Key, Taint] = {
|
|
222
237
|
self.function.parameters[index]: Taint(source.kinds, frozenset({source}))
|
|
@@ -246,6 +261,17 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
246
261
|
self.store(state, instruction.object, ATTRIBUTES, state.get(instruction.value, Taint.none()))
|
|
247
262
|
elif isinstance(instruction, SetItem):
|
|
248
263
|
self.store(state, instruction.object, ELEMENTS, state.get(instruction.value, Taint.none()))
|
|
264
|
+
key = state.get(instruction.key, Taint.none())
|
|
265
|
+
if key:
|
|
266
|
+
self.store(state, instruction.object, KEYS, key)
|
|
267
|
+
elif isinstance(instruction, BuildDict):
|
|
268
|
+
keys = Taint.none()
|
|
269
|
+
for item_key, _ in instruction.items:
|
|
270
|
+
keys = keys.join(state.get(item_key, Taint.none()))
|
|
271
|
+
for unpacked in instruction.unpacked: # ``{**d}`` copies d's keys
|
|
272
|
+
keys = keys.join(self.keyed(unpacked, state))
|
|
273
|
+
if keys:
|
|
274
|
+
self.store(state, instruction.result, KEYS, keys)
|
|
249
275
|
if instruction.result is None:
|
|
250
276
|
continue
|
|
251
277
|
if isinstance(instruction, Phi):
|
|
@@ -352,6 +378,10 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
352
378
|
project, through, arguments, keywords, everything, call, flows, state, (), bound
|
|
353
379
|
)
|
|
354
380
|
return self.declared_clean(target.symbol, result)
|
|
381
|
+
if target.symbol == _DICT:
|
|
382
|
+
keys = self.constructed_keys(call, state)
|
|
383
|
+
if keys:
|
|
384
|
+
self.store(state, call.result, KEYS, keys)
|
|
355
385
|
symbol = target.symbol
|
|
356
386
|
if not self.modelled(symbol):
|
|
357
387
|
# ``get_conn().execute`` derived ``app.database.get_conn.execute``; what
|
|
@@ -376,6 +406,23 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
376
406
|
return self.external(returned, everything, call, state, flows)
|
|
377
407
|
return everything.join(state.get(call.callee, Taint.none()))
|
|
378
408
|
|
|
409
|
+
def constructed_keys(self, call: Call, state: Mapping[Key, Taint]) -> Taint:
|
|
410
|
+
"""What the keys of ``dict(...)`` carry: the keys of the mapping it copies
|
|
411
|
+
(``dict(m)``, ``dict(**m)``) and the first element of each pair of a sequence
|
|
412
|
+
literal (``dict([(k, v)])``); a keyword is a constant key, and an iterable built
|
|
413
|
+
elsewhere (``zip(...)``, a parameter) gives keys the engine cannot establish."""
|
|
414
|
+
|
|
415
|
+
taint = Taint.none()
|
|
416
|
+
for value in (*call.arguments[:1], *(v for name, v in call.keywords if name is None)):
|
|
417
|
+
taint = taint.join(self.keyed(value, state))
|
|
418
|
+
made = self.defs.get(value)
|
|
419
|
+
if isinstance(made, BuildList | BuildTuple | BuildSet) and not made.unpacked:
|
|
420
|
+
for element in made.elements:
|
|
421
|
+
pair = self.defs.get(element)
|
|
422
|
+
if isinstance(pair, BuildTuple | BuildList) and pair.elements:
|
|
423
|
+
taint = taint.join(state.get(pair.elements[0], Taint.none()))
|
|
424
|
+
return taint
|
|
425
|
+
|
|
379
426
|
def nonlocal_result(self, instruction: NonlocalResult, state: dict[Key, Taint]) -> Taint:
|
|
380
427
|
"""The taint a nested callee left in its nonlocal ``name``, in the caller's terms."""
|
|
381
428
|
|
|
@@ -455,6 +502,11 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
455
502
|
self.report(
|
|
456
503
|
flows, sink, self.rendered(argument, state), argument, call, None, None, None, given, keyword=name
|
|
457
504
|
)
|
|
505
|
+
if name is None:
|
|
506
|
+
# The keys of a ``**mapping`` are keyword names of the call: only an
|
|
507
|
+
# advisory asks who chooses a name, no other sink reads one.
|
|
508
|
+
keys = self.keyed(argument, state).without(~TaintKind.ADVISORY)
|
|
509
|
+
self.report(flows, sink, keys, argument, call, None, None, None, given, keyword=KEYWORD_NAMES)
|
|
458
510
|
self.template_filters(symbol, call, given, state, flows)
|
|
459
511
|
cleared = self.clearing.clears(symbol, given)
|
|
460
512
|
if cleared:
|