coretrace-python-analyzer 0.6.0__tar.gz → 0.8.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.8.0}/CHANGELOG.md +21 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/docs/plugins.md +22 -4
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/docs/usage.md +44 -7
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/django/django_models.py +9 -0
- coretrace_python_analyzer-0.8.0/src/coretrace_python/bundled/models/http_clients/http_client_models.py +52 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/cache.py +18 -3
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/cli.py +2 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/engine.py +39 -7
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/findings/refutation.py +111 -20
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/interprocedural/__init__.py +8 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/interprocedural/summaries.py +144 -31
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/plugins/api.py +8 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/taint/__init__.py +5 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/taint/engine.py +33 -14
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/taint/models.py +61 -15
- coretrace_python_analyzer-0.8.0/src/coretrace_python/taint/templates.py +250 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/Chatbot.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/DjangoGoat.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/Flask-Image-Editing-Website.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/InsecureGram.json +12 -6
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/MiniBookApiServer.json +4 -2
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/Vulnerable_fastapi.json +8 -4
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/dvpwa.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/full-stack-fastapi-template.json +2 -1
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/healthchecks.json +8 -88
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/httpie.json +4 -8
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/luigi.json +4 -2
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/pygoat.json +26 -37
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +10 -5
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/vulnerable-flask.json +10 -5
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_cache.py +13 -1
- coretrace_python_analyzer-0.8.0/tests/test_django_safe_values.py +66 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_interprocedural.py +6 -3
- coretrace_python_analyzer-0.8.0/tests/test_project_models.py +117 -0
- coretrace_python_analyzer-0.8.0/tests/test_project_validators.py +93 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_refutation.py +25 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_regression.py +6 -3
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.8.0/tests/test_ssrf_targets.py +124 -0
- coretrace_python_analyzer-0.8.0/tests/test_summary_sanitizers.py +136 -0
- coretrace_python_analyzer-0.8.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.8.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/README.md +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dependency/advisories.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dependency/correlation.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dependency/graph.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/interprocedural/callgraph.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0 (2026-09-25)
|
|
4
|
+
|
|
5
|
+
### Precision
|
|
6
|
+
|
|
7
|
+
- A `Validator` proves its argument safe only for the kinds it declares. A validator declared for `REDIRECT` used to refute a command injection through the same value; the flow is now a hotspot, and it is refuted only when the validator covers every kind reaching the sink (#143).
|
|
8
|
+
- A sanitizer called inside a project function protects the flows through that function, for every caller. That covers a helper that escapes before answering, a view redirecting to `reverse(...)`, and a helper rendering a template the analyzer shows escaping. Function summaries keep, for each parameter, the taint kinds cleared on every path from it to an external call or to the return value. A path that skips the sanitizer, or a kind it does not clear, still reaches the sink. The cache format is bumped (#134).
|
|
9
|
+
|
|
10
|
+
### Plugins
|
|
11
|
+
|
|
12
|
+
- `Plugin.project_models(root)` lets a plugin read models from the project under analysis, such as validators the project declares in a file. It is called once per directory check, in every process, and its models are part of the cache key. A model declared wrongly raises `ModelError`, which the command line now reports as an error (exit status 2) instead of a traceback (#140).
|
|
13
|
+
- A `Validator` model may name a function of the project by its project symbol (`python.hc.accounts.views._allow_redirect`), and the refutation recognises it in its own module too, where a call to it has no imported symbol. A project can thus declare its own validation helpers, and a flow they guard is refuted (#134).
|
|
14
|
+
|
|
15
|
+
## 0.7.0 (2026-09-25)
|
|
16
|
+
|
|
17
|
+
### Precision
|
|
18
|
+
|
|
19
|
+
- 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).
|
|
20
|
+
- 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).
|
|
21
|
+
- `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).
|
|
22
|
+
- 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).
|
|
23
|
+
|
|
3
24
|
## 0.6.0 (2026-09-24)
|
|
4
25
|
|
|
5
26
|
### Reports
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.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,22 +141,40 @@ 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
|
|
145
|
-
| `Sanitizer(symbol, kinds)` | A call whose result is safe 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
|
+
| `Sanitizer(symbol, kinds)` | A call whose result is safe for `kinds`, including when the call is inside a project function the data goes through. |
|
|
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`. |
|
|
148
148
|
| `NamedParameter(pattern, label, kinds)` | A regular expression on parameter names; matching parameters carry `kinds`, as `password` carries `CREDENTIAL`. |
|
|
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
|
-
| `
|
|
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). |
|
|
153
|
+
| `Validator(symbol, kinds=ALL, argument=0)` | A callable whose truth proves its argument safe for `kinds`, such as `re.fullmatch`. A flow guarded by it is refuted when it carries no other kind into the sink, and is a hotspot otherwise: a redirect validator does not make a shell command safe. `symbol` may be a function of the project, by its project symbol (`python.hc.accounts.views._allow_redirect`), which then counts wherever it is called, in its own module too. |
|
|
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
|
|
|
161
|
+
A plugin may also read models from the project it analyses: `project_models(self, root)`
|
|
162
|
+
returns them, from a file at `root` for instance. It is called once per directory check,
|
|
163
|
+
in every process analysing it, before any module. Its models join the plugin's own and
|
|
164
|
+
are part of what cached results depend on. A single-file check reads no project. A
|
|
165
|
+
model declared wrongly raises `ModelError`, which stops the check with its message
|
|
166
|
+
(exit status 2). This is how a project declares validators of its own:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
class DeclaredValidators(ModelPlugin):
|
|
170
|
+
name: ClassVar[str] = "declared-validators"
|
|
171
|
+
|
|
172
|
+
def project_models(self, root: Path) -> Sequence[Model]:
|
|
173
|
+
path = root / "validators.txt"
|
|
174
|
+
lines = path.read_text(encoding="utf-8").splitlines() if path.is_file() else []
|
|
175
|
+
return tuple(Validator(SymbolId(line)) for line in lines if line)
|
|
176
|
+
```
|
|
177
|
+
|
|
160
178
|
A model plugin may also carry `advisories`, a tuple of `Advisory` values
|
|
161
179
|
(`coretrace_python.dependency`) with the package, the vulnerable range and the affected
|
|
162
180
|
symbols, as the shipped `sample-advisories` plugin does. Requirements matching them are
|
|
@@ -101,15 +101,19 @@ confidence (`high`, `medium`, `low`).
|
|
|
101
101
|
### Taint flows
|
|
102
102
|
|
|
103
103
|
Reported when attacker-controlled data reaches a sensitive call without passing through
|
|
104
|
-
a sanitizer for that kind of sink.
|
|
104
|
+
a sanitizer for that kind of sink. The sanitizer may also sit inside a project function
|
|
105
|
+
the data goes through, like a helper that escapes before it answers, or a view that
|
|
106
|
+
redirects to `reverse(...)`. It then protects every caller, on every path through the
|
|
107
|
+
function where it is called. What a function stores into an object or a `nonlocal`
|
|
108
|
+
variable is not treated that way yet.
|
|
105
109
|
|
|
106
110
|
| Rule | Reached sink |
|
|
107
111
|
|---|---|
|
|
108
112
|
| `command-injection` | Shell or process execution (`os.system`, `subprocess` with a string, …). |
|
|
109
113
|
| `sql-injection` | A database statement (`cursor.execute`, SQLAlchemy `text`, Django `raw`, …). Parameters of a parameterised query are not statements. |
|
|
110
114
|
| `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. |
|
|
115
|
+
| `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. |
|
|
116
|
+
| `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
117
|
| `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
118
|
| `open-redirect` | An HTTP redirect target. |
|
|
115
119
|
| `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 +122,42 @@ a sanitizer for that kind of sink.
|
|
|
118
122
|
Each flow is judged before it is reported. A dominating guard that proves the value
|
|
119
123
|
safe (`isdigit()`, membership in a constant collection, equality with a constant, a
|
|
120
124
|
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
|
-
|
|
125
|
+
`re.fullmatch`) refutes it and nothing is reported. A hotspot is reported at medium
|
|
126
|
+
confidence. Two things make a flow a hotspot:
|
|
127
|
+
|
|
128
|
+
- A guard that examines the data without proving it safe. That covers a test of the value
|
|
129
|
+
itself (`if cmd:`, `len(cmd) < 10`), of the same attribute, or of a method called on
|
|
130
|
+
it (`form.is_valid()`).
|
|
131
|
+
- An authorization decorator such as `login_required`.
|
|
132
|
+
|
|
133
|
+
A guard that reads another attribute of the object the data comes from is no guard of
|
|
134
|
+
that data. `if request.method == 'POST':` before `os.system(request.POST['cmd'])` leaves
|
|
135
|
+
a vulnerability. When the flow passes the object whole, to the sink or to a function,
|
|
136
|
+
a guard on any of its attributes still counts. The verdict and its evidence are in the
|
|
137
|
+
finding's metadata.
|
|
138
|
+
|
|
139
|
+
Django's `render_to_string(name, context)` returns HTML in which autoescaping escaped
|
|
140
|
+
every variable, so data reaching a response through it is no `xss`, if the template really
|
|
141
|
+
escapes. On a directory check, the analyzer reads the project's templates to decide it.
|
|
142
|
+
|
|
143
|
+
It establishes escaping only when all of the following hold:
|
|
144
|
+
|
|
145
|
+
- It finds every template of that name under a `templates` directory, by the path Django
|
|
146
|
+
uses (`app/page.html`), and everything those templates extend or include.
|
|
147
|
+
- None of them marks output safe: `|safe`, `|safeseq` or `{% autoescape off %}`.
|
|
148
|
+
- None of them uses a tag or filter beyond Django's own, or loads a library beyond its
|
|
149
|
+
built-ins (`static`, `i18n`, `l10n`, `tz`, `cache`, `humanize`).
|
|
150
|
+
- None of them places a variable where HTML escaping does not protect it: a `<script>`
|
|
151
|
+
or `<style>` block, an event handler (`onclick="…"`), a `style` attribute, a URL
|
|
152
|
+
attribute not fixed by a relative or `http(s)://host/` prefix, an unquoted attribute,
|
|
153
|
+
or an attribute name.
|
|
154
|
+
- No Python file of the project sets `autoescape` to `False`.
|
|
155
|
+
|
|
156
|
+
The analyzer never assumes escaping where it cannot read the template. That covers a
|
|
157
|
+
template named by an expression, a template found through a `DIRS` entry not named
|
|
158
|
+
`templates`, one shipped by an installed package, one rendered through `get_template`,
|
|
159
|
+
and Flask's Jinja2 templates. The flow is then reported as before. A single-file check
|
|
160
|
+
reads no template.
|
|
124
161
|
|
|
125
162
|
### Dangerous API usage
|
|
126
163
|
|
|
@@ -202,7 +239,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
202
239
|
```json
|
|
203
240
|
{
|
|
204
241
|
"schema_version": 1,
|
|
205
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
242
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.8.0"},
|
|
206
243
|
"root": "/home/me/project",
|
|
207
244
|
"findings": [
|
|
208
245
|
{
|
|
@@ -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.8.0}/src/coretrace_python/cache.py
RENAMED
|
@@ -25,6 +25,7 @@ from coretrace_python.findings import Confidence, Finding, Severity
|
|
|
25
25
|
from coretrace_python.interprocedural import (
|
|
26
26
|
Arguments,
|
|
27
27
|
CallSite,
|
|
28
|
+
Cleared,
|
|
28
29
|
ExternalCall,
|
|
29
30
|
ExternalSymbol,
|
|
30
31
|
FunctionSummary,
|
|
@@ -41,7 +42,7 @@ from coretrace_python.interprocedural import (
|
|
|
41
42
|
from coretrace_python.semantic.symbols import SymbolId
|
|
42
43
|
from coretrace_python.source import SourceId, SourceSpan
|
|
43
44
|
|
|
44
|
-
CACHE_FORMAT =
|
|
45
|
+
CACHE_FORMAT = 10
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
@dataclass(frozen=True)
|
|
@@ -233,10 +234,12 @@ def _encode_call(call: ExternalCall) -> dict[str, Any]:
|
|
|
233
234
|
return {
|
|
234
235
|
"symbol": str(call.symbol),
|
|
235
236
|
"arguments": [sorted(deps) for deps in call.argument_dependencies],
|
|
236
|
-
"keywords": sorted(call.keyword_dependencies
|
|
237
|
+
"keywords": [[name, sorted(deps)] for name, deps in call.keyword_dependencies],
|
|
237
238
|
"location": _encode_span(call.location),
|
|
238
239
|
"call_site": None if call.call_site is None else _encode_span(call.call_site),
|
|
239
240
|
"given": _encode_arguments(call.arguments),
|
|
241
|
+
"argument_cleared": [_encode_cleared(c) for c in call.argument_cleared],
|
|
242
|
+
"keyword_cleared": [_encode_cleared(c) for c in call.keyword_cleared],
|
|
240
243
|
}
|
|
241
244
|
|
|
242
245
|
|
|
@@ -245,13 +248,23 @@ def _decode_call(data: Mapping[str, Any]) -> ExternalCall:
|
|
|
245
248
|
return ExternalCall(
|
|
246
249
|
SymbolId(_string(data["symbol"])),
|
|
247
250
|
tuple(_indices(deps) for deps in data["arguments"]),
|
|
248
|
-
_indices(data["keywords"]),
|
|
251
|
+
tuple((None if name is None else _string(name), _indices(deps)) for name, deps in data["keywords"]),
|
|
249
252
|
_decode_span(data["location"]),
|
|
250
253
|
None if site is None else _decode_span(site),
|
|
251
254
|
_decode_arguments(data["given"]),
|
|
255
|
+
tuple(_decode_cleared(c) for c in data["argument_cleared"]),
|
|
256
|
+
tuple(_decode_cleared(c) for c in data["keyword_cleared"]),
|
|
252
257
|
)
|
|
253
258
|
|
|
254
259
|
|
|
260
|
+
def _encode_cleared(cleared: Cleared) -> list[list[int]]:
|
|
261
|
+
return [[parameter, bits] for parameter, bits in cleared]
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _decode_cleared(data: Any) -> Cleared:
|
|
265
|
+
return tuple((_integer(parameter), _integer(bits)) for parameter, bits in data)
|
|
266
|
+
|
|
267
|
+
|
|
255
268
|
def _encode_summary(summary: FunctionSummary) -> dict[str, Any]:
|
|
256
269
|
return {
|
|
257
270
|
"name": summary.name,
|
|
@@ -275,6 +288,7 @@ def _encode_summary(summary: FunctionSummary) -> dict[str, Any]:
|
|
|
275
288
|
for w in summary.nonlocal_writes
|
|
276
289
|
],
|
|
277
290
|
"static": summary.static,
|
|
291
|
+
"return_cleared": _encode_cleared(summary.return_cleared),
|
|
278
292
|
}
|
|
279
293
|
|
|
280
294
|
|
|
@@ -303,6 +317,7 @@ def _decode_summary(data: Mapping[str, Any]) -> FunctionSummary:
|
|
|
303
317
|
for w in data["nonlocal_writes"]
|
|
304
318
|
),
|
|
305
319
|
bool(data["static"]),
|
|
320
|
+
_decode_cleared(data["return_cleared"]),
|
|
306
321
|
)
|
|
307
322
|
|
|
308
323
|
|
{coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/cli.py
RENAMED
|
@@ -27,6 +27,7 @@ from coretrace_python.reporters import FORMATS, render
|
|
|
27
27
|
from coretrace_python.semantic.imports import ImportResolutionError
|
|
28
28
|
from coretrace_python.semantic.scopes import ScopeError
|
|
29
29
|
from coretrace_python.source import SourceManager
|
|
30
|
+
from coretrace_python.taint import ModelError
|
|
30
31
|
|
|
31
32
|
EXIT_CLEAN = 0
|
|
32
33
|
EXIT_FINDINGS = 1
|
|
@@ -45,6 +46,7 @@ _ANALYSIS_ERRORS = (
|
|
|
45
46
|
AnalysisError,
|
|
46
47
|
ManifestError,
|
|
47
48
|
IncompatiblePluginError,
|
|
49
|
+
ModelError,
|
|
48
50
|
)
|
|
49
51
|
|
|
50
52
|
|
{coretrace_python_analyzer-0.6.0 → coretrace_python_analyzer-0.8.0}/src/coretrace_python/engine.py
RENAMED
|
@@ -70,6 +70,7 @@ from coretrace_python.interprocedural import (
|
|
|
70
70
|
CallGraph,
|
|
71
71
|
CallGraphAnalysis,
|
|
72
72
|
CallSite,
|
|
73
|
+
ClearingAnalysis,
|
|
73
74
|
FunctionSummary,
|
|
74
75
|
ModuleFunction,
|
|
75
76
|
ModuleGraph,
|
|
@@ -109,6 +110,7 @@ from coretrace_python.source import SourceFile, SourceId, SourceManager, SourceS
|
|
|
109
110
|
from coretrace_python.taint import (
|
|
110
111
|
EntryPoint,
|
|
111
112
|
EntryPointAnalysis,
|
|
113
|
+
EscapedTemplates,
|
|
112
114
|
ModelTable,
|
|
113
115
|
RegisteredRoutes,
|
|
114
116
|
Routes,
|
|
@@ -116,6 +118,7 @@ from coretrace_python.taint import (
|
|
|
116
118
|
SecurityModelRegistry,
|
|
117
119
|
TaintAnalysis,
|
|
118
120
|
TaintKind,
|
|
121
|
+
escaped_templates,
|
|
119
122
|
registered_routes,
|
|
120
123
|
)
|
|
121
124
|
|
|
@@ -146,6 +149,8 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
|
|
|
146
149
|
RefutationAnalysis,
|
|
147
150
|
DependencyAnalysis,
|
|
148
151
|
RegisteredRoutes,
|
|
152
|
+
EscapedTemplates,
|
|
153
|
+
ClearingAnalysis,
|
|
149
154
|
)
|
|
150
155
|
|
|
151
156
|
|
|
@@ -178,7 +183,15 @@ class ResultsEvicted(TransformationPass):
|
|
|
178
183
|
|
|
179
184
|
name: ClassVar[str] = "project.results-evicted"
|
|
180
185
|
preserves: ClassVar[frozenset[AnyAnalysis]] = frozenset(
|
|
181
|
-
{
|
|
186
|
+
{
|
|
187
|
+
*SEMANTIC_ANALYSES,
|
|
188
|
+
SecurityModelAnalysis,
|
|
189
|
+
ProjectSummaries,
|
|
190
|
+
DependencyAnalysis,
|
|
191
|
+
RegisteredRoutes,
|
|
192
|
+
EscapedTemplates,
|
|
193
|
+
ClearingAnalysis,
|
|
194
|
+
}
|
|
182
195
|
)
|
|
183
196
|
|
|
184
197
|
@classmethod
|
|
@@ -236,7 +249,9 @@ def build_manager(
|
|
|
236
249
|
"""A manager with every engine analysis registered and the engine inputs provided."""
|
|
237
250
|
|
|
238
251
|
manager = _register_all(module)
|
|
239
|
-
|
|
252
|
+
table = (models or SecurityModelRegistry()).freeze()
|
|
253
|
+
manager.provide(SecurityModelAnalysis, table)
|
|
254
|
+
manager.provide(ClearingAnalysis, table.clearing())
|
|
240
255
|
manager.provide(ProjectSummaries, SummaryIndex())
|
|
241
256
|
manager.provide(RegisteredRoutes, _routes_of(manager))
|
|
242
257
|
return manager
|
|
@@ -266,10 +281,14 @@ def load_plugins(plugin_roots: Sequence[Path], manager: AnalysisManager) -> Plug
|
|
|
266
281
|
return registry
|
|
267
282
|
|
|
268
283
|
|
|
269
|
-
def plugin_models(plugins: Iterable[Plugin]) -> ModelTable:
|
|
284
|
+
def plugin_models(plugins: Iterable[Plugin], root: Path | None = None) -> ModelTable:
|
|
285
|
+
"""The models of ``plugins``, with those they read from the project at ``root``."""
|
|
286
|
+
|
|
270
287
|
models = SecurityModelRegistry()
|
|
271
288
|
for plugin in plugins:
|
|
272
289
|
models.register(*plugin.models, origin=plugin.name)
|
|
290
|
+
if root is not None:
|
|
291
|
+
models.register(*plugin.project_models(root), origin=plugin.name)
|
|
273
292
|
return models.freeze()
|
|
274
293
|
|
|
275
294
|
|
|
@@ -284,7 +303,9 @@ def analyze_file(source: SourceFile, plugin_roots: Sequence[Path]) -> FileAnalys
|
|
|
284
303
|
|
|
285
304
|
manager = _register_all(build_hir(source))
|
|
286
305
|
registry = load_plugins(plugin_roots, manager)
|
|
287
|
-
|
|
306
|
+
table = plugin_models(loaded.plugin for loaded in registry)
|
|
307
|
+
manager.provide(SecurityModelAnalysis, table)
|
|
308
|
+
manager.provide(ClearingAnalysis, table.clearing())
|
|
288
309
|
manager.provide(ProjectSummaries, SummaryIndex())
|
|
289
310
|
manager.provide(RegisteredRoutes, _routes_of(manager))
|
|
290
311
|
findings, supported = _check_module(manager, tuple(loaded.plugin for loaded in registry))
|
|
@@ -375,7 +396,7 @@ def analyze_project(
|
|
|
375
396
|
(a for plugin in all_plugins for a in plugin.advisories), file_advisories
|
|
376
397
|
)
|
|
377
398
|
affected = affected_symbols(dependencies, advisories)
|
|
378
|
-
models = plugin_models(all_plugins).extended(*advisory_sinks(affected))
|
|
399
|
+
models = plugin_models(all_plugins, root).extended(*advisory_sinks(affected))
|
|
379
400
|
for manager in managers.values():
|
|
380
401
|
manager.provide(SecurityModelAnalysis, models)
|
|
381
402
|
manager.provide(DependencyAnalysis, dependencies)
|
|
@@ -397,10 +418,14 @@ def analyze_project(
|
|
|
397
418
|
for name in sorted(analysable):
|
|
398
419
|
for symbol, registered in _routes_of(analysable[name]).items():
|
|
399
420
|
routes.setdefault(symbol, registered)
|
|
421
|
+
escaped = escaped_templates(root)
|
|
422
|
+
clearing = models.clearing(escaped)
|
|
400
423
|
for manager in analysable.values():
|
|
401
424
|
manager.provide(RegisteredRoutes, routes)
|
|
425
|
+
manager.provide(EscapedTemplates, escaped)
|
|
426
|
+
manager.provide(ClearingAnalysis, clearing)
|
|
402
427
|
|
|
403
|
-
configuration = _configuration_key(registry, plugins, models, advisories, dependencies, routes)
|
|
428
|
+
configuration = _configuration_key(registry, plugins, models, advisories, dependencies, routes, escaped)
|
|
404
429
|
keys = module_keys(
|
|
405
430
|
graph,
|
|
406
431
|
{name: fingerprint(configuration, str(files[name].source_id), name, files[name].text) for name in analysable},
|
|
@@ -441,6 +466,7 @@ def analyze_project(
|
|
|
441
466
|
encode_index(_seed(results, graph, component)),
|
|
442
467
|
advisory_paths,
|
|
443
468
|
_encode_routes(routes),
|
|
469
|
+
tuple(sorted(escaped)),
|
|
444
470
|
),
|
|
445
471
|
)
|
|
446
472
|
for component in pending
|
|
@@ -588,6 +614,7 @@ class _Batch:
|
|
|
588
614
|
seed: Mapping[str, Any]
|
|
589
615
|
advisory_paths: tuple[Path, ...] = ()
|
|
590
616
|
routes: tuple[tuple[str, str, str, int], ...] = ()
|
|
617
|
+
escaped: tuple[str, ...] = ()
|
|
591
618
|
|
|
592
619
|
|
|
593
620
|
def _analyse_batch(batch: _Batch) -> dict[str, dict[str, Any]]:
|
|
@@ -606,12 +633,15 @@ def _analyse_batch(batch: _Batch) -> dict[str, dict[str, Any]]:
|
|
|
606
633
|
(a for plugin in all_plugins for a in plugin.advisories), file_advisories
|
|
607
634
|
)
|
|
608
635
|
affected = affected_symbols(dependencies, advisories)
|
|
609
|
-
models = plugin_models(all_plugins).extended(*advisory_sinks(affected))
|
|
636
|
+
models = plugin_models(all_plugins, batch.root).extended(*advisory_sinks(affected))
|
|
610
637
|
routes = _decode_routes(batch.routes)
|
|
638
|
+
escaped = frozenset(batch.escaped)
|
|
611
639
|
for manager in managers.values():
|
|
612
640
|
manager.provide(SecurityModelAnalysis, models)
|
|
613
641
|
manager.provide(DependencyAnalysis, dependencies)
|
|
614
642
|
manager.provide(RegisteredRoutes, routes)
|
|
643
|
+
manager.provide(EscapedTemplates, escaped)
|
|
644
|
+
manager.provide(ClearingAnalysis, models.clearing(escaped))
|
|
615
645
|
module_plugins = tuple(p for p in all_plugins if not isinstance(p, ProjectPlugin))
|
|
616
646
|
results = _analyse_managers(managers, decode_index(batch.seed), module_plugins, affected)
|
|
617
647
|
return {name: encode(entry) for name, entry in results.items()}
|
|
@@ -639,6 +669,7 @@ def _configuration_key(
|
|
|
639
669
|
advisories: tuple[Advisory, ...],
|
|
640
670
|
dependencies: DependencyGraph,
|
|
641
671
|
routes: Routes | None = None,
|
|
672
|
+
escaped: frozenset[str] = frozenset(),
|
|
642
673
|
) -> str:
|
|
643
674
|
"""Everything a module's results depend on besides the project sources (§11)."""
|
|
644
675
|
|
|
@@ -658,6 +689,7 @@ def _configuration_key(
|
|
|
658
689
|
repr(dependencies.requirements),
|
|
659
690
|
repr(dependencies.errors),
|
|
660
691
|
repr(_encode_routes(routes or {})),
|
|
692
|
+
repr(sorted(escaped)),
|
|
661
693
|
)
|
|
662
694
|
|
|
663
695
|
|