coretrace-python-analyzer 0.6.0__tar.gz → 0.7.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.6.0 → coretrace_python_analyzer-0.7.0}/CHANGELOG.md +9 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/docs/plugins.md +3 -2
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/docs/usage.md +39 -6
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/django/django_models.py +9 -0
- coretrace_python_analyzer-0.7.0/src/coretrace_python/bundled/models/http_clients/http_client_models.py +52 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cache.py +3 -3
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/engine.py +19 -2
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/refutation.py +70 -5
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/summaries.py +18 -8
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/__init__.py +5 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/engine.py +34 -7
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/models.py +49 -15
- coretrace_python_analyzer-0.7.0/src/coretrace_python/taint/templates.py +250 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Chatbot.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/DjangoGoat.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Flask-Image-Editing-Website.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/InsecureGram.json +12 -6
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/MiniBookApiServer.json +4 -2
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Vulnerable_fastapi.json +8 -4
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/dvpwa.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/full-stack-fastapi-template.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/healthchecks.json +24 -48
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/httpie.json +4 -8
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/luigi.json +4 -2
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/pygoat.json +26 -37
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +10 -5
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/vulnerable-flask.json +10 -5
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_cache.py +10 -1
- coretrace_python_analyzer-0.7.0/tests/test_django_safe_values.py +66 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_interprocedural.py +6 -3
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_refutation.py +25 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_regression.py +6 -3
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.7.0/tests/test_ssrf_targets.py +124 -0
- coretrace_python_analyzer-0.7.0/tests/test_template_escaping.py +178 -0
- coretrace_python_analyzer-0.6.0/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -43
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/README.md +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/advisories.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/correlation.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/graph.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/callgraph.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0 (2026-09-25)
|
|
4
|
+
|
|
5
|
+
### Precision
|
|
6
|
+
|
|
7
|
+
- A dominating guard makes a flow a hotspot only when it examines what the sink receives. `if request.method == 'POST':` before `request.POST['cmd']` no longer lowers a vulnerability to a hotspot. A test of the value itself, of the same attribute, of a method called on it (`form.is_valid()`), or of any part of an object the flow passes whole still does. The regression snapshots record the verdict of each taint finding (#135).
|
|
8
|
+
- The SSRF sinks of Requests and httpx read the destination only: the URL (the first argument, the second after the method for `request` and `stream`, or `url=`) or the prepared request `send` takes. Attacker data in the body, the JSON, the headers or the query parameters is no longer a server-side request forgery. `Sink` gains `keywords` next to `positions`, and function summaries keep the name of each keyword argument, so a project function forwarding `url=` still reaches the sink; the cache format is bumped (#128).
|
|
9
|
+
- `render_to_string` escapes what it renders where the analyzer can establish it: on a directory check, it reads the project's Django templates, and a template escapes when neither it nor what it extends or includes marks output safe, uses a tag, filter or library beyond Django's own, or prints a variable where HTML escaping does not protect it, and no settings turn autoescaping off. Data reaching a response through such a template is no longer `xss`; any other template keeps the flow reported. `TemplateRender` declares such rendering calls, and the escaped templates are part of the cache key (#130).
|
|
10
|
+
- Django's `csrf.get_token` returns a token of letters and digits whatever the request holds, and `reverse`/`reverse_lazy` build a local path that attacker data in their arguments or query cannot turn into another host: the first is no longer an injection, the second no longer an open redirect. `reverse` still carries other kinds, since it leaves `'` unquoted (#132).
|
|
11
|
+
|
|
3
12
|
## 0.6.0 (2026-09-24)
|
|
4
13
|
|
|
5
14
|
### Reports
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.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
|
|
@@ -141,7 +141,7 @@ class BottleModels(ModelPlugin):
|
|
|
141
141
|
| Model | Declares |
|
|
142
142
|
|---|---|
|
|
143
143
|
| `Source(symbol, label, kinds=ALL)` | A call or attribute whose value is attacker-controlled; `label` names it in messages (`http`, `stdin`, `argv`, `http-response`). `kinds` restricts what it can inject. |
|
|
144
|
-
| `Sink(symbol, kinds, positions=())` | A call dangerous for `kinds
|
|
144
|
+
| `Sink(symbol, kinds, positions=(), keywords=())` | A call dangerous for `kinds`. `positions` limits a kind to the given argument indexes, and `keywords` to the given keyword names; a restricted kind reaches only the arguments listed there. SQL sinks read the statement only, and the SSRF sinks of HTTP clients read the URL, by position or as `url=`. A starred argument or `**kwargs` reaches no restricted kind. |
|
|
145
145
|
| `Sanitizer(symbol, kinds)` | A call whose result is safe for `kinds`. |
|
|
146
146
|
| `EntryPoint(symbol, label, kinds=ALL)` | A decorator or a class base whose functions receive their parameters as `label` input, such as `flask.Flask.route`. |
|
|
147
147
|
| `TypedParameter(symbol, label, kinds=ALL)` | A class whose annotated parameters carry `label` input, such as Django's `HttpRequest`. |
|
|
@@ -149,11 +149,12 @@ class BottleModels(ModelPlugin):
|
|
|
149
149
|
| `RouteRegistrar(symbol, argument, label, kinds=ALL, keyword=None)` | A call registering a view at argument `argument` (or `keyword`), such as Django's `path`; the view receives `label` input. |
|
|
150
150
|
| `SuffixSink(suffix, kinds, positions=())` | A sink matched by the end of the symbol, for methods of any class such as `objects.raw`. |
|
|
151
151
|
| `SafeArgument(symbol, argument, values, position=None, kinds=ALL)` | A call to the sink `symbol` whose `argument` (by keyword, or at `position`) denotes one of `values` is not a sink for `kinds`, such as `yaml.load` with `Loader=yaml.SafeLoader`. Values are written as the analyzer records arguments: a symbol by its canonical name, a constant as Python writes it (`True`). Only a value given explicitly makes the call safe. |
|
|
152
|
+
| `TemplateRender(symbol, position=0, keyword="template_name")` | A call rendering, with autoescaping, the template named by its argument at `position` or `keyword`, such as `render_to_string`. What it returns carries no `HTML` when the name is written in the call and the project's template escapes everything it prints (see the usage guide). |
|
|
152
153
|
| `Validator(symbol, kinds=ALL, argument=0)` | A callable whose truth proves its argument safe, such as `re.fullmatch`; a flow guarded by it is refuted. |
|
|
153
154
|
| `AuthorizationGuard(symbol, label)` | A decorator or a condition restricting who reaches the code, such as `login_required`; a flow behind it is a hotspot. |
|
|
154
155
|
|
|
155
156
|
Two plugins may describe the same symbol. An identical model is registered once; two
|
|
156
|
-
`Sink` models merge their kinds and
|
|
157
|
+
`Sink` models merge their kinds, positions and keywords, so a plugin can add `SQL` to a sink
|
|
157
158
|
another plugin declared for `COMMAND`; any other difference is a conflict that stops
|
|
158
159
|
the analysis with a `ModelError` naming both plugins.
|
|
159
160
|
|
|
@@ -108,8 +108,8 @@ a sanitizer for that kind of sink.
|
|
|
108
108
|
| `command-injection` | Shell or process execution (`os.system`, `subprocess` with a string, …). |
|
|
109
109
|
| `sql-injection` | A database statement (`cursor.execute`, SQLAlchemy `text`, Django `raw`, …). Parameters of a parameterised query are not statements. |
|
|
110
110
|
| `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
|
|
111
|
-
| `ssrf` | The URL of an HTTP client request (Requests, httpx, `urllib`). |
|
|
112
|
-
| `xss` | An HTTP response body built without escaping. |
|
|
111
|
+
| `ssrf` | The URL of an HTTP client request (Requests, httpx, `urllib`), by position or as `url=`; the body, headers and query parameters are not the destination. |
|
|
112
|
+
| `xss` | An HTTP response body built without escaping. A Django template rendered by `render_to_string` escapes, where the analyzer can establish it (below). |
|
|
113
113
|
| `insecure-deserialization` | `pickle`, `marshal`, `dill`, `jsonpickle`, `shelve`, and the YAML loaders (`yaml.load`, `load_all`, `full_load`, `unsafe_load` and their several-document versions); `yaml.load` and `load_all` given a safe loader (`SafeLoader`, `BaseLoader`) are not. |
|
|
114
114
|
| `open-redirect` | An HTTP redirect target. |
|
|
115
115
|
| `plaintext-credential-storage` | A parameter named like a password stored in a database without hashing. Medium confidence, since a name is a hint. |
|
|
@@ -118,9 +118,42 @@ a sanitizer for that kind of sink.
|
|
|
118
118
|
Each flow is judged before it is reported. A dominating guard that proves the value
|
|
119
119
|
safe (`isdigit()`, membership in a constant collection, equality with a constant, a
|
|
120
120
|
numeric value proven by `int()`, `len()` or bounded arithmetic, a validator such as
|
|
121
|
-
`re.fullmatch`) refutes it and nothing is reported. A
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
`re.fullmatch`) refutes it and nothing is reported. A hotspot is reported at medium
|
|
122
|
+
confidence. Two things make a flow a hotspot:
|
|
123
|
+
|
|
124
|
+
- A guard that examines the data without proving it safe. That covers a test of the value
|
|
125
|
+
itself (`if cmd:`, `len(cmd) < 10`), of the same attribute, or of a method called on
|
|
126
|
+
it (`form.is_valid()`).
|
|
127
|
+
- An authorization decorator such as `login_required`.
|
|
128
|
+
|
|
129
|
+
A guard that reads another attribute of the object the data comes from is no guard of
|
|
130
|
+
that data. `if request.method == 'POST':` before `os.system(request.POST['cmd'])` leaves
|
|
131
|
+
a vulnerability. When the flow passes the object whole, to the sink or to a function,
|
|
132
|
+
a guard on any of its attributes still counts. The verdict and its evidence are in the
|
|
133
|
+
finding's metadata.
|
|
134
|
+
|
|
135
|
+
Django's `render_to_string(name, context)` returns HTML in which autoescaping escaped
|
|
136
|
+
every variable, so data reaching a response through it is no `xss`, if the template really
|
|
137
|
+
escapes. On a directory check, the analyzer reads the project's templates to decide it.
|
|
138
|
+
|
|
139
|
+
It establishes escaping only when all of the following hold:
|
|
140
|
+
|
|
141
|
+
- It finds every template of that name under a `templates` directory, by the path Django
|
|
142
|
+
uses (`app/page.html`), and everything those templates extend or include.
|
|
143
|
+
- None of them marks output safe: `|safe`, `|safeseq` or `{% autoescape off %}`.
|
|
144
|
+
- None of them uses a tag or filter beyond Django's own, or loads a library beyond its
|
|
145
|
+
built-ins (`static`, `i18n`, `l10n`, `tz`, `cache`, `humanize`).
|
|
146
|
+
- None of them places a variable where HTML escaping does not protect it: a `<script>`
|
|
147
|
+
or `<style>` block, an event handler (`onclick="…"`), a `style` attribute, a URL
|
|
148
|
+
attribute not fixed by a relative or `http(s)://host/` prefix, an unquoted attribute,
|
|
149
|
+
or an attribute name.
|
|
150
|
+
- No Python file of the project sets `autoescape` to `False`.
|
|
151
|
+
|
|
152
|
+
The analyzer never assumes escaping where it cannot read the template. That covers a
|
|
153
|
+
template named by an expression, a template found through a `DIRS` entry not named
|
|
154
|
+
`templates`, one shipped by an installed package, one rendered through `get_template`,
|
|
155
|
+
and Flask's Jinja2 templates. The flow is then reported as before. A single-file check
|
|
156
|
+
reads no template.
|
|
124
157
|
|
|
125
158
|
### Dangerous API usage
|
|
126
159
|
|
|
@@ -202,7 +235,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
202
235
|
```json
|
|
203
236
|
{
|
|
204
237
|
"schema_version": 1,
|
|
205
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
238
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.7.0"},
|
|
206
239
|
"root": "/home/me/project",
|
|
207
240
|
"findings": [
|
|
208
241
|
{
|
|
@@ -23,6 +23,7 @@ from coretrace_python.taint import (
|
|
|
23
23
|
Source,
|
|
24
24
|
SuffixSink,
|
|
25
25
|
TaintKind,
|
|
26
|
+
TemplateRender,
|
|
26
27
|
TypedParameter,
|
|
27
28
|
)
|
|
28
29
|
|
|
@@ -119,7 +120,15 @@ class DjangoModels(ModelPlugin):
|
|
|
119
120
|
Sink(_sym("django.http.HttpResponseRedirect"), TaintKind.REDIRECT, _TARGET_ONLY),
|
|
120
121
|
Sink(_sym("django.http.HttpResponsePermanentRedirect"), TaintKind.REDIRECT, _TARGET_ONLY),
|
|
121
122
|
Sanitizer(_sym("django.utils.html.escape"), TaintKind.HTML),
|
|
123
|
+
TemplateRender(_sym("django.template.loader.render_to_string")),
|
|
122
124
|
Sanitizer(_sym("django.utils.html.conditional_escape"), TaintKind.HTML),
|
|
125
|
+
# The masked CSRF secret: ASCII letters and digits only, a malformed cookie is
|
|
126
|
+
# replaced before it is used.
|
|
127
|
+
Sanitizer(_sym("django.middleware.csrf.get_token"), TaintKind.ALL),
|
|
128
|
+
# A path of the project's URL configuration, never scheme-relative: attacker data
|
|
129
|
+
# in its arguments or query reaches no other host through a redirect.
|
|
130
|
+
*(Sanitizer(_sym(f"django.urls.{name}"), TaintKind.REDIRECT) for name in ("reverse", "reverse_lazy")),
|
|
131
|
+
*(Sanitizer(_sym(f"django.urls.base.{name}"), TaintKind.REDIRECT) for name in ("reverse", "reverse_lazy")),
|
|
123
132
|
*(AuthorizationGuard(_sym(decorator), label) for decorator, label in _AUTHORIZATION_DECORATORS),
|
|
124
133
|
# ``urlpatterns = [path('login/', views.log_in)]``: the referenced view is an
|
|
125
134
|
# entry point wherever it is defined; routers register viewsets.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Requests and httpx models: every request function is a SSRF sink for the argument that
|
|
2
|
+
chooses the destination, and what it returns is an ``http-response`` source, so data
|
|
3
|
+
fetched from a remote server is untrusted. The destination is the URL — first, or second
|
|
4
|
+
after the method, or ``url=`` — or the prepared request a ``send`` takes; the body, the
|
|
5
|
+
headers and the query parameters do not choose the host."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import ClassVar
|
|
10
|
+
|
|
11
|
+
from coretrace_python.plugins import ModelPlugin
|
|
12
|
+
from coretrace_python.semantic.symbols import SymbolId
|
|
13
|
+
from coretrace_python.taint import Model, Sink, Source, TaintKind
|
|
14
|
+
|
|
15
|
+
_METHODS = ("get", "post", "put", "patch", "delete", "head", "options", "request")
|
|
16
|
+
|
|
17
|
+
_CALLERS = (
|
|
18
|
+
"requests",
|
|
19
|
+
"requests.Session",
|
|
20
|
+
"requests.api",
|
|
21
|
+
"httpx",
|
|
22
|
+
"httpx.Client",
|
|
23
|
+
"httpx.AsyncClient",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Where each function takes its destination, by position (the receiver of a method
|
|
27
|
+
# excluded) and by keyword, as its signature declares it.
|
|
28
|
+
_DESTINATIONS = {
|
|
29
|
+
**{f"{caller}.{method}": (0, "url") for caller in _CALLERS for method in _METHODS if method != "request"},
|
|
30
|
+
**{f"{caller}.request": (1, "url") for caller in _CALLERS},
|
|
31
|
+
"httpx.stream": (1, "url"),
|
|
32
|
+
"httpx.Client.stream": (1, "url"),
|
|
33
|
+
"httpx.AsyncClient.stream": (1, "url"),
|
|
34
|
+
"requests.Session.send": (0, "request"),
|
|
35
|
+
"httpx.Client.send": (0, "request"),
|
|
36
|
+
"httpx.AsyncClient.send": (0, "request"),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _sym(path: str) -> SymbolId:
|
|
41
|
+
return SymbolId(f"python.{path}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class HttpClientModels(ModelPlugin):
|
|
45
|
+
name: ClassVar[str] = "http-client-models"
|
|
46
|
+
models: ClassVar[tuple[Model, ...]] = (
|
|
47
|
+
*(
|
|
48
|
+
Sink(_sym(function), TaintKind.SSRF, ((TaintKind.SSRF, (position,)),), ((TaintKind.SSRF, (keyword,)),))
|
|
49
|
+
for function, (position, keyword) in _DESTINATIONS.items()
|
|
50
|
+
),
|
|
51
|
+
*(Source(_sym(function), "http-response") for function in _DESTINATIONS),
|
|
52
|
+
)
|
{coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cache.py
RENAMED
|
@@ -41,7 +41,7 @@ from coretrace_python.interprocedural import (
|
|
|
41
41
|
from coretrace_python.semantic.symbols import SymbolId
|
|
42
42
|
from coretrace_python.source import SourceId, SourceSpan
|
|
43
43
|
|
|
44
|
-
CACHE_FORMAT =
|
|
44
|
+
CACHE_FORMAT = 9
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
@dataclass(frozen=True)
|
|
@@ -233,7 +233,7 @@ def _encode_call(call: ExternalCall) -> dict[str, Any]:
|
|
|
233
233
|
return {
|
|
234
234
|
"symbol": str(call.symbol),
|
|
235
235
|
"arguments": [sorted(deps) for deps in call.argument_dependencies],
|
|
236
|
-
"keywords": sorted(call.keyword_dependencies
|
|
236
|
+
"keywords": [[name, sorted(deps)] for name, deps in call.keyword_dependencies],
|
|
237
237
|
"location": _encode_span(call.location),
|
|
238
238
|
"call_site": None if call.call_site is None else _encode_span(call.call_site),
|
|
239
239
|
"given": _encode_arguments(call.arguments),
|
|
@@ -245,7 +245,7 @@ def _decode_call(data: Mapping[str, Any]) -> ExternalCall:
|
|
|
245
245
|
return ExternalCall(
|
|
246
246
|
SymbolId(_string(data["symbol"])),
|
|
247
247
|
tuple(_indices(deps) for deps in data["arguments"]),
|
|
248
|
-
_indices(data["keywords"]),
|
|
248
|
+
tuple((None if name is None else _string(name), _indices(deps)) for name, deps in data["keywords"]),
|
|
249
249
|
_decode_span(data["location"]),
|
|
250
250
|
None if site is None else _decode_span(site),
|
|
251
251
|
_decode_arguments(data["given"]),
|
{coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/engine.py
RENAMED
|
@@ -109,6 +109,7 @@ from coretrace_python.source import SourceFile, SourceId, SourceManager, SourceS
|
|
|
109
109
|
from coretrace_python.taint import (
|
|
110
110
|
EntryPoint,
|
|
111
111
|
EntryPointAnalysis,
|
|
112
|
+
EscapedTemplates,
|
|
112
113
|
ModelTable,
|
|
113
114
|
RegisteredRoutes,
|
|
114
115
|
Routes,
|
|
@@ -116,6 +117,7 @@ from coretrace_python.taint import (
|
|
|
116
117
|
SecurityModelRegistry,
|
|
117
118
|
TaintAnalysis,
|
|
118
119
|
TaintKind,
|
|
120
|
+
escaped_templates,
|
|
119
121
|
registered_routes,
|
|
120
122
|
)
|
|
121
123
|
|
|
@@ -146,6 +148,7 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
|
|
|
146
148
|
RefutationAnalysis,
|
|
147
149
|
DependencyAnalysis,
|
|
148
150
|
RegisteredRoutes,
|
|
151
|
+
EscapedTemplates,
|
|
149
152
|
)
|
|
150
153
|
|
|
151
154
|
|
|
@@ -178,7 +181,14 @@ class ResultsEvicted(TransformationPass):
|
|
|
178
181
|
|
|
179
182
|
name: ClassVar[str] = "project.results-evicted"
|
|
180
183
|
preserves: ClassVar[frozenset[AnyAnalysis]] = frozenset(
|
|
181
|
-
{
|
|
184
|
+
{
|
|
185
|
+
*SEMANTIC_ANALYSES,
|
|
186
|
+
SecurityModelAnalysis,
|
|
187
|
+
ProjectSummaries,
|
|
188
|
+
DependencyAnalysis,
|
|
189
|
+
RegisteredRoutes,
|
|
190
|
+
EscapedTemplates,
|
|
191
|
+
}
|
|
182
192
|
)
|
|
183
193
|
|
|
184
194
|
@classmethod
|
|
@@ -397,10 +407,12 @@ def analyze_project(
|
|
|
397
407
|
for name in sorted(analysable):
|
|
398
408
|
for symbol, registered in _routes_of(analysable[name]).items():
|
|
399
409
|
routes.setdefault(symbol, registered)
|
|
410
|
+
escaped = escaped_templates(root)
|
|
400
411
|
for manager in analysable.values():
|
|
401
412
|
manager.provide(RegisteredRoutes, routes)
|
|
413
|
+
manager.provide(EscapedTemplates, escaped)
|
|
402
414
|
|
|
403
|
-
configuration = _configuration_key(registry, plugins, models, advisories, dependencies, routes)
|
|
415
|
+
configuration = _configuration_key(registry, plugins, models, advisories, dependencies, routes, escaped)
|
|
404
416
|
keys = module_keys(
|
|
405
417
|
graph,
|
|
406
418
|
{name: fingerprint(configuration, str(files[name].source_id), name, files[name].text) for name in analysable},
|
|
@@ -441,6 +453,7 @@ def analyze_project(
|
|
|
441
453
|
encode_index(_seed(results, graph, component)),
|
|
442
454
|
advisory_paths,
|
|
443
455
|
_encode_routes(routes),
|
|
456
|
+
tuple(sorted(escaped)),
|
|
444
457
|
),
|
|
445
458
|
)
|
|
446
459
|
for component in pending
|
|
@@ -588,6 +601,7 @@ class _Batch:
|
|
|
588
601
|
seed: Mapping[str, Any]
|
|
589
602
|
advisory_paths: tuple[Path, ...] = ()
|
|
590
603
|
routes: tuple[tuple[str, str, str, int], ...] = ()
|
|
604
|
+
escaped: tuple[str, ...] = ()
|
|
591
605
|
|
|
592
606
|
|
|
593
607
|
def _analyse_batch(batch: _Batch) -> dict[str, dict[str, Any]]:
|
|
@@ -612,6 +626,7 @@ def _analyse_batch(batch: _Batch) -> dict[str, dict[str, Any]]:
|
|
|
612
626
|
manager.provide(SecurityModelAnalysis, models)
|
|
613
627
|
manager.provide(DependencyAnalysis, dependencies)
|
|
614
628
|
manager.provide(RegisteredRoutes, routes)
|
|
629
|
+
manager.provide(EscapedTemplates, frozenset(batch.escaped))
|
|
615
630
|
module_plugins = tuple(p for p in all_plugins if not isinstance(p, ProjectPlugin))
|
|
616
631
|
results = _analyse_managers(managers, decode_index(batch.seed), module_plugins, affected)
|
|
617
632
|
return {name: encode(entry) for name, entry in results.items()}
|
|
@@ -639,6 +654,7 @@ def _configuration_key(
|
|
|
639
654
|
advisories: tuple[Advisory, ...],
|
|
640
655
|
dependencies: DependencyGraph,
|
|
641
656
|
routes: Routes | None = None,
|
|
657
|
+
escaped: frozenset[str] = frozenset(),
|
|
642
658
|
) -> str:
|
|
643
659
|
"""Everything a module's results depend on besides the project sources (§11)."""
|
|
644
660
|
|
|
@@ -658,6 +674,7 @@ def _configuration_key(
|
|
|
658
674
|
repr(dependencies.requirements),
|
|
659
675
|
repr(dependencies.errors),
|
|
660
676
|
repr(_encode_routes(routes or {})),
|
|
677
|
+
repr(sorted(escaped)),
|
|
661
678
|
)
|
|
662
679
|
|
|
663
680
|
|
|
@@ -8,10 +8,13 @@ and ``not`` combine as expected, a ``Validator`` model names a callable whose tr
|
|
|
8
8
|
one of its arguments, a numeric value (``abstract.ranges``) cannot inject, and anything
|
|
9
9
|
else that mentions the value is a guard that does not prove it. A proof counts for an
|
|
10
10
|
origin when every dependence path from that origin to the sink argument goes through a
|
|
11
|
-
proven value. A
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
proven value. A guard mentions an origin only when it examines what the sink receives:
|
|
12
|
+
reading another attribute or item of an object the flow reads (``request.method`` next
|
|
13
|
+
to ``request.POST``) examines nothing of it, while the same attribute, or a method called
|
|
14
|
+
on the object (``form.is_valid()``), does. A flow is refuted when every tainted origin is
|
|
15
|
+
proven safe or the sink is unreachable, a hotspot when it sits behind an
|
|
16
|
+
``AuthorizationGuard`` (a decorator or a dominating condition) or when an unproven guard
|
|
17
|
+
mentions it, and a vulnerability otherwise.
|
|
15
18
|
"""
|
|
16
19
|
|
|
17
20
|
from __future__ import annotations
|
|
@@ -37,6 +40,7 @@ from coretrace_python.ir.model import (
|
|
|
37
40
|
Constant,
|
|
38
41
|
FunctionIR,
|
|
39
42
|
GetAttr,
|
|
43
|
+
GetItem,
|
|
40
44
|
Instruction,
|
|
41
45
|
UnaryOp,
|
|
42
46
|
Value,
|
|
@@ -267,6 +271,66 @@ class _Judge:
|
|
|
267
271
|
return tested, "equals a constant", definition.operator == "eq"
|
|
268
272
|
return None
|
|
269
273
|
|
|
274
|
+
def examined(
|
|
275
|
+
self, condition: Value, mentioned: set[Value], argument: Value, chain: frozenset[Value]
|
|
276
|
+
) -> set[Value]:
|
|
277
|
+
"""What the guard examines of the values it mentions: everything its condition is
|
|
278
|
+
computed from, except an object it only reads another attribute or item of than
|
|
279
|
+
the flow reads (``request`` in ``request.method`` when the sink receives
|
|
280
|
+
``request.POST['cmd']``). A method called on the object (``form.is_valid()``), the
|
|
281
|
+
same attribute or item, or any part of an object the flow takes whole (passes to
|
|
282
|
+
the sink or to a call), examines it."""
|
|
283
|
+
|
|
284
|
+
reads = {self.access(value) for value in chain} - {None}
|
|
285
|
+
whole = {argument} | {
|
|
286
|
+
operand
|
|
287
|
+
for value in chain
|
|
288
|
+
if (definition := self.defs.get(value)) is not None
|
|
289
|
+
for operand in definition.operands()
|
|
290
|
+
if not (isinstance(definition, GetAttr | GetItem) and definition.object == operand)
|
|
291
|
+
}
|
|
292
|
+
found: set[Value] = set()
|
|
293
|
+
called: set[Value] = set()
|
|
294
|
+
pending = [condition]
|
|
295
|
+
while pending:
|
|
296
|
+
value = pending.pop()
|
|
297
|
+
if value in found:
|
|
298
|
+
continue
|
|
299
|
+
found.add(value)
|
|
300
|
+
definition = self.defs.get(value)
|
|
301
|
+
if definition is None:
|
|
302
|
+
continue
|
|
303
|
+
if isinstance(definition, Call):
|
|
304
|
+
called.add(definition.callee)
|
|
305
|
+
access = None if value in called else self.access(value)
|
|
306
|
+
for operand in definition.operands():
|
|
307
|
+
if access is not None and access[0] == operand and operand not in whole and self.aside(access, reads):
|
|
308
|
+
continue
|
|
309
|
+
pending.append(operand)
|
|
310
|
+
return found & mentioned
|
|
311
|
+
|
|
312
|
+
@staticmethod
|
|
313
|
+
def aside(access: tuple[Value, str, object], reads: set[tuple[Value, str, object] | None]) -> bool:
|
|
314
|
+
"""Whether reading ``access`` reads beside what the flow reads of the same object;
|
|
315
|
+
an item under a key unknown on either side may be the same one."""
|
|
316
|
+
|
|
317
|
+
owner, kind, name = access
|
|
318
|
+
if kind == "item" and (name is None or (owner, "item", None) in reads):
|
|
319
|
+
return False
|
|
320
|
+
return access not in reads
|
|
321
|
+
|
|
322
|
+
def access(self, value: Value) -> tuple[Value, str, object] | None:
|
|
323
|
+
"""``(object, "attribute" or "item", name or constant key)`` for a read of an
|
|
324
|
+
attribute or an item; ``None`` for any other value, and as the key when unknown."""
|
|
325
|
+
|
|
326
|
+
definition = self.defs.get(value)
|
|
327
|
+
if isinstance(definition, GetAttr):
|
|
328
|
+
return definition.object, "attribute", definition.attribute
|
|
329
|
+
if isinstance(definition, GetItem):
|
|
330
|
+
key = self.defs.get(definition.key)
|
|
331
|
+
return definition.object, "item", key.value if isinstance(key, Constant) else None
|
|
332
|
+
return None
|
|
333
|
+
|
|
270
334
|
def is_constant_collection(self, value: Value) -> bool:
|
|
271
335
|
definition = self.defs.get(value)
|
|
272
336
|
if isinstance(definition, Constant):
|
|
@@ -326,8 +390,9 @@ class _Judge:
|
|
|
326
390
|
for value, reason in found.items():
|
|
327
391
|
if value in chain:
|
|
328
392
|
proven.setdefault(value, reason)
|
|
393
|
+
examined = self.examined(guard.condition, mentioned, flow.argument, chain)
|
|
329
394
|
for origin in origins:
|
|
330
|
-
if origin not in mentions and
|
|
395
|
+
if origin not in mentions and examined & (
|
|
331
396
|
{origin} | {w for w in chain if origin in self.closure(w)}
|
|
332
397
|
):
|
|
333
398
|
mentions[origin] = guard.line
|
|
@@ -87,11 +87,13 @@ EMPTY = Dep()
|
|
|
87
87
|
|
|
88
88
|
@dataclass(frozen=True)
|
|
89
89
|
class ExternalCall:
|
|
90
|
-
"""An external symbol reached from this function; dependencies are parameter indices.
|
|
90
|
+
"""An external symbol reached from this function; dependencies are parameter indices.
|
|
91
|
+
Keyword arguments keep their name, so a sink reading ``url=`` only can be decided;
|
|
92
|
+
``*args`` and ``**kwargs`` have none."""
|
|
91
93
|
|
|
92
94
|
symbol: SymbolId
|
|
93
95
|
argument_dependencies: tuple[Dependencies, ...]
|
|
94
|
-
keyword_dependencies: Dependencies
|
|
96
|
+
keyword_dependencies: tuple[tuple[str | None, Dependencies], ...]
|
|
95
97
|
location: SourceSpan
|
|
96
98
|
call_site: SourceSpan | None
|
|
97
99
|
# What the arguments of the call at ``location`` denote, where it is made.
|
|
@@ -313,9 +315,13 @@ class _DependenceProblem(DataflowProblem[State]):
|
|
|
313
315
|
|
|
314
316
|
def call(self, call: Call, state: dict[Key, Dep]) -> Dep:
|
|
315
317
|
arguments = tuple(self.deep(a, state) for a in call.arguments)
|
|
318
|
+
named = (
|
|
319
|
+
*((name, self.deep(value, state)) for name, value in call.keywords),
|
|
320
|
+
*((None, self.deep(value, state)) for value in call.starred),
|
|
321
|
+
)
|
|
316
322
|
keywords = EMPTY
|
|
317
|
-
for
|
|
318
|
-
keywords |=
|
|
323
|
+
for _, deps in named:
|
|
324
|
+
keywords |= deps
|
|
319
325
|
everything = keywords
|
|
320
326
|
for deps in arguments:
|
|
321
327
|
everything |= deps
|
|
@@ -335,7 +341,7 @@ class _DependenceProblem(DataflowProblem[State]):
|
|
|
335
341
|
self.record(
|
|
336
342
|
target.symbol,
|
|
337
343
|
tuple(a.parameters for a in arguments),
|
|
338
|
-
|
|
344
|
+
tuple((name, deps.parameters) for name, deps in named),
|
|
339
345
|
call.location,
|
|
340
346
|
None,
|
|
341
347
|
self.graph.arguments_at(self.name, call.location),
|
|
@@ -418,7 +424,7 @@ class _DependenceProblem(DataflowProblem[State]):
|
|
|
418
424
|
self.record(
|
|
419
425
|
reached.symbol,
|
|
420
426
|
tuple(mapped(d).parameters for d in reached.argument_dependencies),
|
|
421
|
-
mapped(reached.keyword_dependencies)
|
|
427
|
+
tuple((name, mapped(d).parameters) for name, d in reached.keyword_dependencies),
|
|
422
428
|
reached.location,
|
|
423
429
|
call.location,
|
|
424
430
|
reached.arguments,
|
|
@@ -435,7 +441,7 @@ class _DependenceProblem(DataflowProblem[State]):
|
|
|
435
441
|
self,
|
|
436
442
|
symbol: SymbolId,
|
|
437
443
|
arguments: tuple[Dependencies, ...],
|
|
438
|
-
keywords: Dependencies,
|
|
444
|
+
keywords: tuple[tuple[str | None, Dependencies], ...],
|
|
439
445
|
location: SourceSpan,
|
|
440
446
|
call_site: SourceSpan | None,
|
|
441
447
|
given: Arguments,
|
|
@@ -446,7 +452,11 @@ class _DependenceProblem(DataflowProblem[State]):
|
|
|
446
452
|
arguments = tuple(
|
|
447
453
|
a | b for a, b in zip(previous.argument_dependencies, arguments, strict=False)
|
|
448
454
|
)
|
|
449
|
-
keywords
|
|
455
|
+
# The same call records the same keywords, in the same order.
|
|
456
|
+
keywords = tuple(
|
|
457
|
+
(name, deps | old)
|
|
458
|
+
for (name, deps), (_, old) in zip(keywords, previous.keyword_dependencies, strict=False)
|
|
459
|
+
)
|
|
450
460
|
self.external[key] = ExternalCall(symbol, arguments, keywords, location, call_site, given)
|
|
451
461
|
|
|
452
462
|
|
|
@@ -24,15 +24,18 @@ from coretrace_python.taint.models import (
|
|
|
24
24
|
Source,
|
|
25
25
|
SuffixSink,
|
|
26
26
|
TaintKind,
|
|
27
|
+
TemplateRender,
|
|
27
28
|
TypedParameter,
|
|
28
29
|
Validator,
|
|
29
30
|
)
|
|
30
31
|
from coretrace_python.taint.routes import RegisteredRoutes, Routes, registered_routes
|
|
32
|
+
from coretrace_python.taint.templates import EscapedTemplates, escaped_templates
|
|
31
33
|
|
|
32
34
|
__all__ = [
|
|
33
35
|
"AuthorizationGuard",
|
|
34
36
|
"EntryPoint",
|
|
35
37
|
"EntryPointAnalysis",
|
|
38
|
+
"EscapedTemplates",
|
|
36
39
|
"Model",
|
|
37
40
|
"ModelError",
|
|
38
41
|
"ModelTable",
|
|
@@ -52,8 +55,10 @@ __all__ = [
|
|
|
52
55
|
"TaintFacts",
|
|
53
56
|
"TaintFlow",
|
|
54
57
|
"TaintKind",
|
|
58
|
+
"TemplateRender",
|
|
55
59
|
"TypedParameter",
|
|
56
60
|
"Validator",
|
|
61
|
+
"escaped_templates",
|
|
57
62
|
"propagate_taint",
|
|
58
63
|
"registered_routes",
|
|
59
64
|
]
|