coretrace-python-analyzer 0.18.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.18.0 → coretrace_python_analyzer-0.20.0}/CHANGELOG.md +31 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/docs/plugins.md +18 -7
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/docs/usage.md +77 -13
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/__init__.py +2 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/heap.py +4 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cache.py +14 -7
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/advisories.py +4 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/correlation.py +16 -3
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/graph.py +4 -1
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/engine.py +95 -28
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/coverage.py +6 -4
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/__init__.py +2 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/callgraph.py +67 -7
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/modulegraph.py +103 -9
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/scopes.py +2 -2
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/__init__.py +2 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/engine.py +106 -9
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/templates.py +279 -29
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/DjangoGoat.json +28 -4
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/httpie.json +16 -4
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/pygoat.json +35 -4
- coretrace_python_analyzer-0.20.0/tests/test_block_super.py +155 -0
- coretrace_python_analyzer-0.20.0/tests/test_entry_point_aliases.py +160 -0
- coretrace_python_analyzer-0.20.0/tests/test_function_aliases_api.py +84 -0
- 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.20.0/tests/test_module_identity.py +384 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.20.0/tests/test_settings_scope.py +109 -0
- coretrace_python_analyzer-0.20.0/tests/test_template_filter_overrides.py +385 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/README.md +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/strings.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/interprocedural/summaries.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/src/coretrace_python/taint/urls.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_advisory_url_host.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_base_patterns.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_code_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_declared_entry_points.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_members.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_open_redirect_destinations.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_ssrf_destinations.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_context_flows.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_filter_calls.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_template_request_processor.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_unreadable_templates.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_url_proof.py +0 -0
- {coretrace_python_analyzer-0.18.0 → coretrace_python_analyzer-0.20.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,36 @@
|
|
|
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
|
+
|
|
17
|
+
## 0.19.0 (2026-09-29)
|
|
18
|
+
|
|
19
|
+
### Analysis
|
|
20
|
+
|
|
21
|
+
- Two files with the same Python module name, `a/app.py` and `z/app.py` outside any package, no longer overwrite each other: both are discovered, analysed and covered whatever the order they are found in. The engine tells them apart by their path from the root as a module name, `a.app` and `z.app`, in the module graph, the summary index, the cache keys and what project plugins see; a unique name stays what it was. An import of such a name resolves to the file in the importer's own root only, and one no root resolves is not followed. Each colliding file carries an `ambiguous-module` note and is covered as `ambiguous`, since a model or a route naming `python.app.main` applies to every definition of that symbol, and so does an importer whose import stayed unresolved; a VEX statement over such a project stays `under_investigation` (#182).
|
|
22
|
+
|
|
23
|
+
### Precision
|
|
24
|
+
|
|
25
|
+
- A `TEMPLATES` assigned inside a function or a class no longer counts as Django settings: only a module-level assignment can activate the `request` context processor, and only a mention of the module's `TEMPLATES` elsewhere leaves that activation uncertain. A function-local `TEMPLATES`, called or not, used to enable the implicit request source, upgrading a template filter finding to exploitable (#184).
|
|
26
|
+
- A template block that renders `{{ block.super }}` keeps the parent block's content, so the filters the parent applies stay fed by the render context, with the parent template as the place of the call; a block without it still replaces the parent's whole block, and a chain of templates keeps exactly the parents each level renders. The parent's filters used to drop out of the exploitable flow as soon as a child defined the block (#185).
|
|
27
|
+
- A project tag library registering a filter of a built-in's name replaces the built-in in every Django template loading it before applying the filter, as Django's parser does: `{% load custom %}{{ bio|striptags }}` calls the project's `striptags` when `app/templatetags/custom.py` registers one, so it is no longer a reachable or exploitable call to `django.template.defaultfilters.striptags`; the project's function is ordinary code, analysed as any other. A load affects only the filters after it, a later load overrides an earlier one, `{% load striptags from custom %}` replaces only what it names, and Django's own libraries change nothing. The analyzer reads the registrations on `register`, a `django.template.Library` however it is imported, in decorator and call form, at module level or under `if`, `try` or a function. Where it cannot tell what a library registers — no such library file, one it cannot read or parse, a name not written as a constant, `register` passed elsewhere or filled through `filters=` or `register.filters`, or two apps giving the name and registering different filters — a filter of that name applied under the load is neither the built-in nor certainly replaced: no call is claimed, and the load counts among the templates the analyzer could not read (`app/templates/app/profile.html:1 loads 'custom', not read`), so an advisory naming the filter stays `under_investigation` in the VEX document. The settings' `OPTIONS["libraries"]` and `OPTIONS["builtins"]` are not read (#183).
|
|
28
|
+
|
|
29
|
+
### Plugins
|
|
30
|
+
|
|
31
|
+
- `ModuleFunction.aliases` gives a project plugin the module-level names bound to a function by assigning one name to another, transitively and in source order, so an integration declaring a handler by the name a module binds (`handlers.main`) can report a name the engine resolves to no function; a method or a nested function has none. The cache format changes.
|
|
32
|
+
- An `EntryPoint` naming a project function or class applies through a module-level alias of it: `main = actual` makes `python.app.handlers.main` name `actual`, so a deployment declaring `handlers.main` reaches the function doing the work. An alias is an assignment of one name to another at module level, followed transitively and in source order, in a conditional or a `try` as well; a name bound inside a function or class, or to a call, an import or anything but a name, is no alias, and the engine still infers nothing from a name. A model naming a method wins over one naming its class, whichever name it uses.
|
|
33
|
+
|
|
3
34
|
## 0.18.0 (2026-09-28)
|
|
4
35
|
|
|
5
36
|
### Precision
|
|
@@ -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
|
|
@@ -53,7 +53,11 @@ resolved through imports and aliases, so `python.os.system` covers `os.system`,
|
|
|
53
53
|
symbol, so a chain such as `sqlite3.connect(p).cursor().execute` is
|
|
54
54
|
`python.sqlite3.connect.cursor.execute`, and `app = Flask(__name__)` gives `app.route` the
|
|
55
55
|
symbol `python.flask.Flask.route`. Builtins are `python.builtins.<name>`. Symbols are
|
|
56
|
-
`SymbolId` values from `coretrace_python.semantic.symbols`.
|
|
56
|
+
`SymbolId` values from `coretrace_python.semantic.symbols`. A function of the project is
|
|
57
|
+
`python.` followed by its module's import name and its qualified name,
|
|
58
|
+
`python.app.handlers.main`; two files with the same import name, `a/app.py` and
|
|
59
|
+
`z/app.py` outside any package, define the same symbols, so a model naming
|
|
60
|
+
`python.app.main` names the function of each.
|
|
57
61
|
|
|
58
62
|
## Kinds of plugin
|
|
59
63
|
|
|
@@ -190,7 +194,9 @@ A model plugin may also carry `advisories`, a tuple of `Advisory` values
|
|
|
190
194
|
(`coretrace_python.dependency`) with the package, the vulnerable range and the affected
|
|
191
195
|
symbols, as the shipped `sample-advisories` plugin does. Requirements matching them are
|
|
192
196
|
reported, calls to the affected symbols become reachable vulnerabilities and tainted
|
|
193
|
-
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.
|
|
194
200
|
|
|
195
201
|
### Reclassifying findings: `ProjectPlugin.refine`
|
|
196
202
|
|
|
@@ -255,14 +261,19 @@ module graph, the dependency graph, every advisory the plugins and advisory file
|
|
|
255
261
|
contributed, the policy, the project root, and each module's imports, call graph and
|
|
256
262
|
functions. `ctx.functions(module)` gives every function as the call graph names it
|
|
257
263
|
(`Admin.get`, `outer.inner`, `<module>`), with its span, so a finding can be placed in
|
|
258
|
-
one,
|
|
259
|
-
when the models make it one
|
|
264
|
+
one, the label of the entry point it is (`http` for a route, `argv` for a command)
|
|
265
|
+
when the models make it one, and its `aliases`, the module-level names bound to it by
|
|
266
|
+
assigning one name to another (`main = actual`), transitively and in source order, so
|
|
267
|
+
an integration declaring a handler by the name a module binds can tell a name the
|
|
268
|
+
engine resolves to no function; a method or a nested function has none. `ctx.call_graph(module)` gives each function's call sites
|
|
260
269
|
(`sites(function)`) and the symbols it reads, called or not (`reads(function)`: reading
|
|
261
270
|
`request.form['name']` reads `python.flask.request.form`), each where it first reads it.
|
|
262
271
|
`ctx.templates` gives the templates found under the project's `templates` directories:
|
|
263
|
-
their `names`, the filters they apply as `calls` (each a
|
|
264
|
-
function behind the filter and where the template
|
|
265
|
-
|
|
272
|
+
their `names`, the filters of Django's own libraries they apply as `calls` (each a
|
|
273
|
+
`FilterCall(symbol, span)`, the function behind the filter and where the template
|
|
274
|
+
applies it; a filter a project tag library the template loads replaces is no call),
|
|
275
|
+
and where they name a template the analyzer cannot read, or load a library it cannot
|
|
276
|
+
read while applying a filter of that library's name (`unread`).
|
|
266
277
|
The shipped `vulnerable-dependency`, `reachable-vulnerability` and `dependency-policy`
|
|
267
278
|
plugins are of this kind.
|
|
268
279
|
|
|
@@ -65,7 +65,13 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
65
65
|
## What is analysed
|
|
66
66
|
|
|
67
67
|
Given a directory, every `.py` file below it is a module of one project, named after
|
|
68
|
-
its package (`app/views.py` is `app.views`).
|
|
68
|
+
its package (`app/views.py` is `app.views`). Two files that would share a name, `a/app.py`
|
|
69
|
+
and `z/app.py` outside any package, are told apart by their path from the root, `a.app`
|
|
70
|
+
and `z.app`, and reported: each carries an `ambiguous-module` note, since a symbol
|
|
71
|
+
`python.app.main` may name a function of either and an import of `app` resolves only
|
|
72
|
+
from the importer's own directory, and each is covered as `ambiguous` rather than
|
|
73
|
+
analysed. An import of such a name that no file of the importer's own directory answers
|
|
74
|
+
is noted where it is written and not followed. Hidden directories, `node_modules`,
|
|
69
75
|
`__pycache__`, `build`, `dist` and any virtual environment, recognised by its
|
|
70
76
|
`pyvenv.cfg` whatever its name, are skipped. Dependency files at the root
|
|
71
77
|
(`requirements*.txt`, `pyproject.toml`, `poetry.lock`, `uv.lock`) are read into a
|
|
@@ -241,7 +247,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
241
247
|
```json
|
|
242
248
|
{
|
|
243
249
|
"schema_version": 1,
|
|
244
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
250
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.20.0"},
|
|
245
251
|
"root": "/home/me/project",
|
|
246
252
|
"findings": [
|
|
247
253
|
{
|
|
@@ -261,7 +267,9 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
261
267
|
}
|
|
262
268
|
```
|
|
263
269
|
|
|
264
|
-
`root` is the directory the paths are relative to.
|
|
270
|
+
`root` is the directory the paths are relative to. A file's `status` is `analysed`,
|
|
271
|
+
`ambiguous` (analysed, but another file has its module name, see above), `syntax-error`
|
|
272
|
+
or `unreadable`; only `analysed` files count in `files_analysed`.
|
|
265
273
|
|
|
266
274
|
A directory check also lists, in `tool.components`, what the result was produced with
|
|
267
275
|
besides the engine and the sources: each plugin it loaded, by manifest name and version,
|
|
@@ -273,7 +281,7 @@ bytes. A single-file check lists no components.
|
|
|
273
281
|
|
|
274
282
|
```json
|
|
275
283
|
"tool": {
|
|
276
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
284
|
+
"name": "coretrace-python-analyzer", "version": "0.20.0",
|
|
277
285
|
"components": [
|
|
278
286
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
279
287
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -372,6 +380,28 @@ Django. Most filters are functions of the same name in `django.template.defaultf
|
|
|
372
380
|
are `<name>_filter` there, and the filters of `i18n`, `l10n`, `tz` and `humanize` are in
|
|
373
381
|
their library's module (`timezone` is `django.templatetags.tz.do_timezone`).
|
|
374
382
|
|
|
383
|
+
A project tag library, a module of a `templatetags` directory, that registers a filter
|
|
384
|
+
of that name replaces the built-in in every template loading it, as Django's parser
|
|
385
|
+
does: `{% load custom %}{{ bio|striptags }}` calls the project's function when
|
|
386
|
+
`custom.py` registers a `striptags` filter, which is no call to the built-in; the
|
|
387
|
+
project's function is ordinary code, whose own calls are analysed as any other. A
|
|
388
|
+
load affects only the filters applied after it in the file, a later load overrides an
|
|
389
|
+
earlier one, `{% load striptags from custom %}` replaces only the filters it names, and
|
|
390
|
+
Django's own libraries (`static`, `i18n`, `l10n`, `tz`, `cache`, `humanize`) change
|
|
391
|
+
nothing. The analyzer reads the registrations on the library's `register`, a
|
|
392
|
+
`django.template.Library` however it is imported: `@register.filter`,
|
|
393
|
+
`@register.filter("name")`, `@register.filter(name="name", is_safe=True)`,
|
|
394
|
+
`register.filter("name", function)` and `register.filter(function)`, at module level or
|
|
395
|
+
under `if`, `try` or a function. Where it cannot tell what a library registers, the
|
|
396
|
+
filter is neither the built-in nor certainly replaced: a library found in no
|
|
397
|
+
`templatetags` directory (one an installed package ships, or that the settings'
|
|
398
|
+
`OPTIONS["libraries"]` or `OPTIONS["builtins"]` name, which are not read), a file it
|
|
399
|
+
cannot read or parse, a name given by a variable or unpacked arguments, `register`
|
|
400
|
+
passed to another function, filled through `filters=` or `register.filters`, or bound
|
|
401
|
+
to anything but a `Library`, or two apps giving the same library name and registering
|
|
402
|
+
different filters. The analyzer then claims no call, and counts the template among those
|
|
403
|
+
it could not read (below) when a filter of the library's name is applied under the load.
|
|
404
|
+
|
|
375
405
|
Attacker input in the render context makes the filter exploitable at the render call:
|
|
376
406
|
`render(request, "app/profile.html", {"bio": request.POST["bio"]})` with
|
|
377
407
|
`{{ bio|striptags }}`. The analyzer links a context value to a filter only where it is
|
|
@@ -386,7 +416,8 @@ certain of it:
|
|
|
386
416
|
arguments of the filters before it, or the output of a `{% filter %}` block. A name a
|
|
387
417
|
tag binds shadows the context, and `{% include ... only %}` passes none of it.
|
|
388
418
|
- The filter renders: a template extending another renders only its blocks, which
|
|
389
|
-
replace the blocks of that name in what it extends
|
|
419
|
+
replace the blocks of that name in what it extends, unless the block renders
|
|
420
|
+
`{{ block.super }}`, which keeps the parent's content and the filters it applies.
|
|
390
421
|
- One file has the template's name; of two files of the same name, which one renders
|
|
391
422
|
depends on the loaders.
|
|
392
423
|
|
|
@@ -399,9 +430,12 @@ With the `django.template.context_processors.request` context processor, a templ
|
|
|
399
430
|
rendered with the request reads it as `request`: `{{ request.GET.q|striptags }}` is
|
|
400
431
|
exploitable at the render call too. The analyzer counts it only where all of these hold:
|
|
401
432
|
|
|
402
|
-
- The project's settings assign `TEMPLATES` a literal list, every
|
|
403
|
-
engine of it lists the processor in a literal `context_processors`,
|
|
404
|
-
of the project names `TEMPLATES
|
|
433
|
+
- The project's settings assign `TEMPLATES` a literal list at module level, every
|
|
434
|
+
`DjangoTemplates` engine of it lists the processor in a literal `context_processors`,
|
|
435
|
+
and no other code of the project names the module's `TEMPLATES`, or a `TEMPLATES` it
|
|
436
|
+
does not bind, as a star import gives. A `TEMPLATES` bound in a function or a class
|
|
437
|
+
is another name: it configures nothing and changes nothing.
|
|
438
|
+
A mere mention of the processor proves nothing.
|
|
405
439
|
- The render call passes the request: `render` and `TemplateResponse` always do,
|
|
406
440
|
`render_to_string` when given `request`, `SimpleTemplateResponse` never.
|
|
407
441
|
- The render's context is certain, absent or a dict literal as above, so it cannot hide
|
|
@@ -454,6 +488,32 @@ attacker's input is passed in that argument. The outcome has three cases:
|
|
|
454
488
|
|
|
455
489
|
The other conditions, the environment included, stay pending as before.
|
|
456
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
|
+
|
|
457
517
|
Every dependency finding records the highest level of evidence established in its
|
|
458
518
|
`level` metadata: `declared` (the requirement allows a vulnerable version), `imported`
|
|
459
519
|
(a module of the package is imported somewhere), `reachable` (an entry point or affected
|
|
@@ -502,10 +562,14 @@ A template the analyzer cannot read may apply any filter. The project names one
|
|
|
502
562
|
`render`, `render_to_string`, `TemplateResponse`, an `{% include %}` or an `{% extends %}`
|
|
503
563
|
names a template by an expression, or by a name found under no `templates` directory,
|
|
504
564
|
including one an installed package ships; a template file that cannot be read counts
|
|
505
|
-
too
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
a
|
|
565
|
+
too, and so does a template loading a tag library the analyzer cannot read, or find,
|
|
566
|
+
or finds in several apps registering different filters, where it then applies a filter
|
|
567
|
+
of a built-in's name, since the library may replace it. An advisory whose entry points
|
|
568
|
+
include a template filter then stays `under_investigation`, and the notes list where
|
|
569
|
+
the project names each such template (`app.views:12 names a template by an
|
|
570
|
+
expression`, `app/templates/app/profile.html:1 loads 'crispy_forms_tags', not found`).
|
|
571
|
+
A template named elsewhere, such as a class-based view's `template_name`, is not
|
|
572
|
+
checked.
|
|
509
573
|
|
|
510
574
|
`not_affected` needs the lock-file check because the analyzer reads the project's code,
|
|
511
575
|
not the code of installed packages. A framework calling the vulnerable function on the
|
|
@@ -513,7 +577,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
513
577
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
514
578
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
515
579
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
516
|
-
(`coretrace-python-analyzer 0.
|
|
580
|
+
(`coretrace-python-analyzer 0.20.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
517
581
|
`@id` changes with the advisory data a statement was decided with.
|
|
518
582
|
|
|
519
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.18.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)
|
|
@@ -134,7 +134,7 @@ class ProjectCache:
|
|
|
134
134
|
def encode(module: CachedModule) -> dict[str, Any]:
|
|
135
135
|
return {
|
|
136
136
|
"format": CACHE_FORMAT,
|
|
137
|
-
"functions": [[f.name, _encode_span(f.span), f.entry_point] for f in module.functions],
|
|
137
|
+
"functions": [[f.name, _encode_span(f.span), f.entry_point, list(f.aliases)] for f in module.functions],
|
|
138
138
|
"summaries": {name: _encode_summary(s) for name, s in module.summaries.items()},
|
|
139
139
|
"sites": [_encode_site(site) for site in module.sites],
|
|
140
140
|
"findings": [_encode_finding(finding) for finding in module.findings],
|
|
@@ -196,8 +196,13 @@ def _decode_span(data: Any) -> SourceSpan:
|
|
|
196
196
|
|
|
197
197
|
|
|
198
198
|
def _decode_function(data: Any) -> ModuleFunction:
|
|
199
|
-
name, span, entry_point = data
|
|
200
|
-
return ModuleFunction(
|
|
199
|
+
name, span, entry_point, aliases = data
|
|
200
|
+
return ModuleFunction(
|
|
201
|
+
_string(name),
|
|
202
|
+
_decode_span(span),
|
|
203
|
+
None if entry_point is None else _string(entry_point),
|
|
204
|
+
tuple(_string(alias) for alias in aliases),
|
|
205
|
+
)
|
|
201
206
|
|
|
202
207
|
|
|
203
208
|
def _decode_read(data: Any) -> SymbolRead:
|
|
@@ -363,6 +368,7 @@ def _encode_arguments(arguments: Arguments) -> dict[str, Any]:
|
|
|
363
368
|
"positional": list(arguments.positional),
|
|
364
369
|
"keywords": [[name, value] for name, value in arguments.keywords],
|
|
365
370
|
"unpacked": arguments.unpacked,
|
|
371
|
+
"keyword_unpacked": arguments.keyword_unpacked,
|
|
366
372
|
}
|
|
367
373
|
|
|
368
374
|
|
|
@@ -370,11 +376,12 @@ def _decode_arguments(data: Mapping[str, Any]) -> Arguments:
|
|
|
370
376
|
def denoted(value: Any) -> str | None:
|
|
371
377
|
return None if value is None else _string(value)
|
|
372
378
|
|
|
373
|
-
unpacked = data["unpacked"]
|
|
374
|
-
if not isinstance(unpacked, bool):
|
|
375
|
-
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}")
|
|
376
382
|
return Arguments(
|
|
377
383
|
tuple(denoted(value) for value in data["positional"]),
|
|
378
384
|
tuple((_string(name), denoted(value)) for name, value in data["keywords"]),
|
|
379
385
|
unpacked,
|
|
386
|
+
keyword_unpacked,
|
|
380
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
|