coretrace-python-analyzer 0.5.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.5.0 → coretrace_python_analyzer-0.7.0}/CHANGELOG.md +32 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/docs/plugins.md +9 -4
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/docs/usage.md +70 -10
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/__init__.py +1 -1
- coretrace_python_analyzer-0.7.0/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +82 -0
- {coretrace_python_analyzer-0.5.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.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +25 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cache.py +16 -6
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cli.py +33 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/__init__.py +2 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/advisories.py +16 -8
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/correlation.py +1 -13
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/graph.py +76 -9
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/policy.py +6 -2
- coretrace_python_analyzer-0.7.0/src/coretrace_python/dependency/vex.py +182 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/engine.py +36 -4
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/refutation.py +70 -5
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/__init__.py +2 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/callgraph.py +50 -2
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/summaries.py +18 -8
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/__init__.py +7 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/engine.py +49 -9
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/models.py +73 -15
- coretrace_python_analyzer-0.7.0/src/coretrace_python/taint/templates.py +250 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Chatbot.json +2 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/DjangoGoat.json +2 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Flask-Image-Editing-Website.json +2 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/InsecureGram.json +12 -6
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/MiniBookApiServer.json +4 -2
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Vulnerable_fastapi.json +8 -4
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/dvpwa.json +2 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/full-stack-fastapi-template.json +2 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/healthchecks.json +24 -48
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/httpie.json +4 -8
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/luigi.json +4 -2
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/pygoat.json +26 -37
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +10 -5
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/vulnerable-flask.json +10 -5
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_cache.py +14 -2
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_dependencies.py +80 -0
- coretrace_python_analyzer-0.7.0/tests/test_django_safe_values.py +66 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_docs.py +1 -1
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_interprocedural.py +6 -3
- coretrace_python_analyzer-0.7.0/tests/test_read_entry_points.py +123 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_refutation.py +25 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_regression.py +6 -3
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.7.0/tests/test_safe_arguments.py +130 -0
- 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.7.0/tests/test_vex.py +284 -0
- coretrace_python_analyzer-0.5.0/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -49
- coretrace_python_analyzer-0.5.0/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -43
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/README.md +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/tests/test_tornado_bottle_models.py +0 -0
|
@@ -1,5 +1,37 @@
|
|
|
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
|
+
|
|
12
|
+
## 0.6.0 (2026-09-24)
|
|
13
|
+
|
|
14
|
+
### Reports
|
|
15
|
+
|
|
16
|
+
- `--vex PATH` writes an OpenVEX document with one statement per advisory affecting a requirement. A statement is `affected` when the project's code reaches the vulnerability, including findings the policy accepts or a comment suppresses. It is `not_affected` (`vulnerable_code_not_in_execute_path`) only for an advisory with entry points that nothing reaches, in a complete analysis, and when the lock file shows no other package requiring the vulnerable one. Anything else is `under_investigation`, with the reason (#97).
|
|
17
|
+
|
|
18
|
+
### Dependencies
|
|
19
|
+
|
|
20
|
+
- An advisory entry point marked `read` is an attribute whose getter runs the affected code, such as `request.form`: reading it is reachable, called or not — a subscript, an iteration or a method called on it reads it — with one finding per function, where it is first read (#111).
|
|
21
|
+
- A lock file records which packages require which. `DependencyGraph.required_by(name)` gives the other packages requiring a package; the project's own packages are not counted. `ProjectAnalysis.accepted` keeps the findings of advisories the policy accepts (#97).
|
|
22
|
+
|
|
23
|
+
### Detection
|
|
24
|
+
|
|
25
|
+
- `yaml.load_all`, `yaml.full_load_all` and `yaml.unsafe_load_all` are deserialization sinks like their single-document versions; `yaml.load_all` with a safe loader is not, and `yaml.safe_load_all` stays safe (#123).
|
|
26
|
+
|
|
27
|
+
### Precision
|
|
28
|
+
|
|
29
|
+
- A sink call can be made safe by one of its arguments: a `SafeArgument` model names the argument and the values that take kinds off the sink, only when the call gives one explicitly. `yaml.load` with `SafeLoader`, `BaseLoader` or their C versions, under every spelling, is no longer an insecure deserialization; an absent loader, another loader, a variable or unpacked arguments still are (#121).
|
|
30
|
+
|
|
31
|
+
### Plugins
|
|
32
|
+
|
|
33
|
+
- The call graph records the symbols each function reads, called or not, where it first reads each: `CallGraph.reads(function)` gives them as `SymbolRead` records, modules served from the cache keep them, and the cache format is bumped (#111).
|
|
34
|
+
|
|
3
35
|
## 0.5.0 (2026-09-24)
|
|
4
36
|
|
|
5
37
|
### Dependencies
|
|
@@ -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,18 +141,20 @@ 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`. |
|
|
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
|
+
| `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). |
|
|
151
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. |
|
|
152
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. |
|
|
153
155
|
|
|
154
156
|
Two plugins may describe the same symbol. An identical model is registered once; two
|
|
155
|
-
`Sink` models merge their kinds and
|
|
157
|
+
`Sink` models merge their kinds, positions and keywords, so a plugin can add `SQL` to a sink
|
|
156
158
|
another plugin declared for `COMMAND`; any other difference is a conflict that stops
|
|
157
159
|
the analysis with a `ModelError` naming both plugins.
|
|
158
160
|
|
|
@@ -226,8 +228,11 @@ contributed, the policy, the project root, and each module's imports, call graph
|
|
|
226
228
|
functions. `ctx.functions(module)` gives every function as the call graph names it
|
|
227
229
|
(`Admin.get`, `outer.inner`, `<module>`), with its span, so a finding can be placed in
|
|
228
230
|
one, and the label of the entry point it is (`http` for a route, `argv` for a command)
|
|
229
|
-
when the models make it one.
|
|
230
|
-
`
|
|
231
|
+
when the models make it one. `ctx.call_graph(module)` gives each function's call sites
|
|
232
|
+
(`sites(function)`) and the symbols it reads, called or not (`reads(function)`: reading
|
|
233
|
+
`request.form['name']` reads `python.flask.request.form`), each where it first reads it.
|
|
234
|
+
The shipped `vulnerable-dependency`, `reachable-vulnerability` and `dependency-policy`
|
|
235
|
+
plugins are of this kind.
|
|
231
236
|
|
|
232
237
|
```python
|
|
233
238
|
from collections.abc import Sequence
|
|
@@ -50,6 +50,7 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
50
50
|
| `--cache DIR` | Keep per-module results under `DIR` and reuse them for unchanged modules. |
|
|
51
51
|
| `--jobs N` | Analyse independent modules in `N` processes. |
|
|
52
52
|
| `--sbom PATH` | Write a CycloneDX bill of materials of the dependencies to `PATH`. |
|
|
53
|
+
| `--vex PATH` | Write an OpenVEX document to `PATH`: whether each advisory affecting a requirement affects the project. |
|
|
53
54
|
| `--advisories FILE` | Read a local advisory file in addition to `advisories.json` at the root. Repeatable. |
|
|
54
55
|
| `--policy FILE` | Apply this dependency policy instead of `coretrace-policy.toml` at the root. |
|
|
55
56
|
| `--import-advisories SRC OUT` | Convert an OSV dump into the local advisory file `OUT`, then exit. |
|
|
@@ -59,7 +60,7 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
59
60
|
| `--ssa` | With `--emit-ir`, print the static single assignment form. |
|
|
60
61
|
| `--help` | Show the options and exit. |
|
|
61
62
|
|
|
62
|
-
`--cache`, `--jobs`, `--sbom`, `--advisories` and `--policy` apply to a directory check.
|
|
63
|
+
`--cache`, `--jobs`, `--sbom`, `--vex`, `--advisories` and `--policy` apply to a directory check.
|
|
63
64
|
|
|
64
65
|
## What is analysed
|
|
65
66
|
|
|
@@ -107,9 +108,9 @@ a sanitizer for that kind of sink.
|
|
|
107
108
|
| `command-injection` | Shell or process execution (`os.system`, `subprocess` with a string, …). |
|
|
108
109
|
| `sql-injection` | A database statement (`cursor.execute`, SQLAlchemy `text`, Django `raw`, …). Parameters of a parameterised query are not statements. |
|
|
109
110
|
| `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
|
|
110
|
-
| `ssrf` | The URL of an HTTP client request (Requests, httpx, `urllib`). |
|
|
111
|
-
| `xss` | An HTTP response body built without escaping. |
|
|
112
|
-
| `insecure-deserialization` | `pickle`, `yaml.load` and
|
|
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
|
+
| `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. |
|
|
113
114
|
| `open-redirect` | An HTTP redirect target. |
|
|
114
115
|
| `plaintext-credential-storage` | A parameter named like a password stored in a database without hashing. Medium confidence, since a name is a hint. |
|
|
115
116
|
| `exploitable-vulnerability` | An API affected by an advisory of a vulnerable requirement, see Dependencies. |
|
|
@@ -117,9 +118,42 @@ a sanitizer for that kind of sink.
|
|
|
117
118
|
Each flow is judged before it is reported. A dominating guard that proves the value
|
|
118
119
|
safe (`isdigit()`, membership in a constant collection, equality with a constant, a
|
|
119
120
|
numeric value proven by `int()`, `len()` or bounded arithmetic, a validator such as
|
|
120
|
-
`re.fullmatch`) refutes it and nothing is reported. A
|
|
121
|
-
|
|
122
|
-
|
|
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.
|
|
123
157
|
|
|
124
158
|
### Dangerous API usage
|
|
125
159
|
|
|
@@ -151,7 +185,7 @@ sources and in configuration files.
|
|
|
151
185
|
| Rule | Trigger |
|
|
152
186
|
|---|---|
|
|
153
187
|
| `vulnerable-dependency` | A requirement allows a version affected by an advisory, reported at its line. |
|
|
154
|
-
| `reachable-vulnerability` | A call in the project to an API the advisory affects
|
|
188
|
+
| `reachable-vulnerability` | A call in the project to an API the advisory affects, or a read of an entry point marked `read`. |
|
|
155
189
|
| `exploitable-vulnerability` | Attacker-controlled data reaches such a call. Critical. |
|
|
156
190
|
| `denied-dependency` | A package the policy denies. |
|
|
157
191
|
| `unpinned-dependency` | A requirement without an exact pin when the policy requires pins. |
|
|
@@ -201,7 +235,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
201
235
|
```json
|
|
202
236
|
{
|
|
203
237
|
"schema_version": 1,
|
|
204
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
238
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.7.0"},
|
|
205
239
|
"root": "/home/me/project",
|
|
206
240
|
"findings": [
|
|
207
241
|
{
|
|
@@ -291,6 +325,13 @@ call to either kind of symbol is reachable. `modules` names the top-level module
|
|
|
291
325
|
package installs, so the analyzer can tell an imported package from a merely required
|
|
292
326
|
one; it defaults to the package name.
|
|
293
327
|
|
|
328
|
+
An entry point marked `"read": true` is an attribute whose getter runs the affected code,
|
|
329
|
+
such as a request body parsed on first access
|
|
330
|
+
(`{"symbol": "python.flask.request.form", "justification": "…", "read": true}`): reading
|
|
331
|
+
it is reachable, called or not — `request.form['name']`, `for key in request.form`,
|
|
332
|
+
`request.form.get('name')` all read it. A function reading it several times is reported
|
|
333
|
+
once, where it first reads it.
|
|
334
|
+
|
|
294
335
|
An `argument` condition is decided at each call. It names the argument by keyword and,
|
|
295
336
|
when it may be passed positionally, by `position` (counted from 0, the receiver of a
|
|
296
337
|
method excluded); `values` are what makes the call affected, written as the analyzer
|
|
@@ -305,7 +346,8 @@ template's origin — cannot be decided and is always pending review, never drop
|
|
|
305
346
|
Every dependency finding records the highest level of evidence established in its
|
|
306
347
|
`level` metadata: `declared` (the requirement allows a vulnerable version), `imported`
|
|
307
348
|
(a module of the package is imported somewhere), `reachable` (an entry point or affected
|
|
308
|
-
symbol is called
|
|
349
|
+
symbol is called, or an entry point marked `read` is read) or `exploitable` (attacker
|
|
350
|
+
input reaches it). The last two carry
|
|
309
351
|
`entry_point`, `justification`, `conditions`, and which of them the call meets
|
|
310
352
|
(`conditions_met`) or leaves to review (`conditions_pending_review`). A requirement whose
|
|
311
353
|
entry points are only called with arguments that contradict a condition stays
|
|
@@ -332,6 +374,24 @@ with its package URL, and the advisories affecting them as vulnerabilities.
|
|
|
332
374
|
coretrace-python-analyzer --check src/ --sbom sbom.json --policy security/policy.toml
|
|
333
375
|
```
|
|
334
376
|
|
|
377
|
+
`--vex PATH` writes an OpenVEX 0.2.0 document with one statement per advisory affecting
|
|
378
|
+
a requirement. In each statement, the required package is a subcomponent of the project,
|
|
379
|
+
identified as `pkg:generic/<directory>`. The status follows from the evidence of the
|
|
380
|
+
check, including findings the policy accepts or a comment suppresses:
|
|
381
|
+
|
|
382
|
+
| Status | When |
|
|
383
|
+
|---|---|
|
|
384
|
+
| `affected` | The project's code reaches the vulnerable code. The notes give each place and its level, `reachable` or `exploitable`. |
|
|
385
|
+
| `not_affected` | Justified as `vulnerable_code_not_in_execute_path`, only when all of these hold: the advisory names entry points, no code of the project reaches them, every file and function was analysed, and a lock file (`uv.lock` or `poetry.lock`) shows that no other package requires the vulnerable one. The impact statement names the entry points and any calls ruled out by their arguments. |
|
|
386
|
+
| `under_investigation` | Anything else. The notes say which condition failed. |
|
|
387
|
+
|
|
388
|
+
`not_affected` needs the lock-file check because the analyzer reads the project's code,
|
|
389
|
+
not the code of installed packages. A framework calling the vulnerable function on the
|
|
390
|
+
project's behalf would go unseen. Without a lock file, a package is never
|
|
391
|
+
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
392
|
+
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
393
|
+
cannot know it.
|
|
394
|
+
|
|
335
395
|
## Large projects
|
|
336
396
|
|
|
337
397
|
`--cache DIR` keeps the results of a directory check on disk, one entry per module,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Calls, anywhere in the project, to an API affected by a vulnerable requirement, and
|
|
2
|
+
reads of an affected attribute whose getter runs the vulnerable code."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Iterator, Sequence
|
|
7
|
+
from typing import ClassVar
|
|
8
|
+
|
|
9
|
+
from coretrace_python.analysis import AnyAnalysis
|
|
10
|
+
from coretrace_python.dependency import Advisory, DependencyAnalysis
|
|
11
|
+
from coretrace_python.dependency.correlation import (
|
|
12
|
+
ConditionCheck,
|
|
13
|
+
affected_symbols,
|
|
14
|
+
check_conditions,
|
|
15
|
+
evidence,
|
|
16
|
+
)
|
|
17
|
+
from coretrace_python.findings import Confidence, Finding
|
|
18
|
+
from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
|
|
19
|
+
from coretrace_python.plugins import ProjectContext, ProjectPlugin
|
|
20
|
+
from coretrace_python.semantic.symbols import SymbolId
|
|
21
|
+
from coretrace_python.source import SourceSpan
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ReachableVulnerabilityPlugin(ProjectPlugin):
|
|
25
|
+
name: ClassVar[str] = "reachable-vulnerability"
|
|
26
|
+
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis, CallGraphAnalysis})
|
|
27
|
+
|
|
28
|
+
def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
|
|
29
|
+
affected = affected_symbols(ctx.dependencies, ctx.advisories)
|
|
30
|
+
if not affected:
|
|
31
|
+
return ()
|
|
32
|
+
findings: list[Finding] = []
|
|
33
|
+
for module in ctx.modules:
|
|
34
|
+
graph = ctx.call_graph(module)
|
|
35
|
+
for function in graph.functions:
|
|
36
|
+
for site in graph.sites(function):
|
|
37
|
+
if not isinstance(site.target, ExternalSymbol):
|
|
38
|
+
continue
|
|
39
|
+
for advisory in affected.get(site.target.symbol, ()):
|
|
40
|
+
entry = advisory.entry_point(site.target.symbol)
|
|
41
|
+
if entry is not None and entry.read:
|
|
42
|
+
continue # a call reads its callee first: reported with the reads
|
|
43
|
+
check = check_conditions(entry, site.arguments)
|
|
44
|
+
if check.contradicted is not None:
|
|
45
|
+
continue
|
|
46
|
+
findings.append(_reached(advisory, site.target.symbol, site.location, function, check))
|
|
47
|
+
# Reading ``request.form.get`` reads ``request.form``: one finding per entry
|
|
48
|
+
# point and function, where the function first reads it.
|
|
49
|
+
reported: set[tuple[Advisory, SymbolId]] = set()
|
|
50
|
+
for read in graph.reads(function):
|
|
51
|
+
for symbol in _read_through(read.symbol):
|
|
52
|
+
for advisory in affected.get(symbol, ()):
|
|
53
|
+
entry = advisory.entry_point(symbol)
|
|
54
|
+
if entry is None or not entry.read or (advisory, symbol) in reported:
|
|
55
|
+
continue
|
|
56
|
+
reported.add((advisory, symbol))
|
|
57
|
+
check = check_conditions(entry, None)
|
|
58
|
+
findings.append(_reached(advisory, symbol, read.location, function, check))
|
|
59
|
+
return findings
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _read_through(symbol: SymbolId) -> Iterator[SymbolId]:
|
|
63
|
+
"""``symbol`` and every symbol it is an attribute of, as reading it reads them all."""
|
|
64
|
+
|
|
65
|
+
components = symbol.canonical_name.split(".")
|
|
66
|
+
for end in range(len(components), 1, -1):
|
|
67
|
+
yield SymbolId(".".join(components[:end]))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _reached(advisory: Advisory, symbol: SymbolId, span: SourceSpan, function: str, check: ConditionCheck) -> Finding:
|
|
71
|
+
return Finding(
|
|
72
|
+
rule_id="reachable-vulnerability",
|
|
73
|
+
message=(
|
|
74
|
+
f"{advisory.id}: {symbol} is affected in the required "
|
|
75
|
+
f"{advisory.package} {advisory.vulnerable}: {advisory.summary}"
|
|
76
|
+
),
|
|
77
|
+
severity=advisory.severity,
|
|
78
|
+
confidence=Confidence.HIGH,
|
|
79
|
+
span=span,
|
|
80
|
+
function=function,
|
|
81
|
+
metadata=evidence(advisory, symbol, "reachable", check),
|
|
82
|
+
)
|
|
@@ -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
|
+
)
|
|
@@ -6,9 +6,28 @@ from typing import ClassVar
|
|
|
6
6
|
|
|
7
7
|
from coretrace_python.plugins import ModelPlugin
|
|
8
8
|
from coretrace_python.semantic.symbols import SymbolId
|
|
9
|
-
from coretrace_python.taint import
|
|
9
|
+
from coretrace_python.taint import (
|
|
10
|
+
Model,
|
|
11
|
+
SafeArgument,
|
|
12
|
+
Sanitizer,
|
|
13
|
+
Sink,
|
|
14
|
+
Source,
|
|
15
|
+
TaintKind,
|
|
16
|
+
Validator,
|
|
17
|
+
)
|
|
10
18
|
|
|
11
19
|
_ENVIRONMENT_KINDS = TaintKind.ALL & ~(TaintKind.COMMAND | TaintKind.PATH)
|
|
20
|
+
# SafeLoader and BaseLoader build plain data only, in Python and in C, under every spelling.
|
|
21
|
+
_SAFE_YAML_LOADERS = (
|
|
22
|
+
"python.yaml.SafeLoader",
|
|
23
|
+
"python.yaml.loader.SafeLoader",
|
|
24
|
+
"python.yaml.BaseLoader",
|
|
25
|
+
"python.yaml.loader.BaseLoader",
|
|
26
|
+
"python.yaml.CSafeLoader",
|
|
27
|
+
"python.yaml.cyaml.CSafeLoader",
|
|
28
|
+
"python.yaml.CBaseLoader",
|
|
29
|
+
"python.yaml.cyaml.CBaseLoader",
|
|
30
|
+
)
|
|
12
31
|
_PROCESS_OUTPUT_KINDS = TaintKind.ALL & ~TaintKind.PATH
|
|
13
32
|
|
|
14
33
|
|
|
@@ -66,6 +85,11 @@ class PythonStdlibModels(ModelPlugin):
|
|
|
66
85
|
Sink(_sym("dill.loads"), TaintKind.DESERIALIZATION),
|
|
67
86
|
Sink(_sym("jsonpickle.decode"), TaintKind.DESERIALIZATION),
|
|
68
87
|
Sink(_sym("yaml.load"), TaintKind.DESERIALIZATION),
|
|
88
|
+
Sink(_sym("yaml.load_all"), TaintKind.DESERIALIZATION),
|
|
89
|
+
Sink(_sym("yaml.full_load_all"), TaintKind.DESERIALIZATION),
|
|
90
|
+
Sink(_sym("yaml.unsafe_load_all"), TaintKind.DESERIALIZATION),
|
|
91
|
+
SafeArgument(_sym("yaml.load"), "Loader", _SAFE_YAML_LOADERS, position=1, kinds=TaintKind.DESERIALIZATION),
|
|
92
|
+
SafeArgument(_sym("yaml.load_all"), "Loader", _SAFE_YAML_LOADERS, position=1, kinds=TaintKind.DESERIALIZATION),
|
|
69
93
|
Sink(_sym("yaml.unsafe_load"), TaintKind.DESERIALIZATION),
|
|
70
94
|
Sink(_sym("yaml.full_load"), TaintKind.DESERIALIZATION),
|
|
71
95
|
Sanitizer(_sym("os.path.basename"), TaintKind.PATH),
|
{coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cache.py
RENAMED
|
@@ -4,9 +4,10 @@ A module's results are stored under a key derived from everything they depend on
|
|
|
4
4
|
source text and identity, the engine, schema and plugin API versions, the plugins and
|
|
5
5
|
their code, the security models, the advisories, the dependency graph, and the keys of
|
|
6
6
|
the project modules it imports transitively. A module whose key is unchanged on a later
|
|
7
|
-
run is served from the cache: its summaries seed the project index, its call sites
|
|
8
|
-
the project plugins and its findings are reported as they were.
|
|
9
|
-
tampered or foreign file can never execute anything; an
|
|
7
|
+
run is served from the cache: its summaries seed the project index, its call sites and
|
|
8
|
+
symbol reads serve the project plugins and its findings are reported as they were.
|
|
9
|
+
Entries are JSON, so a tampered or foreign file can never execute anything; an
|
|
10
|
+
unreadable entry is a miss.
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from __future__ import annotations
|
|
@@ -33,13 +34,14 @@ from coretrace_python.interprocedural import (
|
|
|
33
34
|
Mutation,
|
|
34
35
|
NonlocalWrite,
|
|
35
36
|
SummaryIndex,
|
|
37
|
+
SymbolRead,
|
|
36
38
|
Target,
|
|
37
39
|
UnknownTarget,
|
|
38
40
|
)
|
|
39
41
|
from coretrace_python.semantic.symbols import SymbolId
|
|
40
42
|
from coretrace_python.source import SourceId, SourceSpan
|
|
41
43
|
|
|
42
|
-
CACHE_FORMAT =
|
|
44
|
+
CACHE_FORMAT = 9
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
@dataclass(frozen=True)
|
|
@@ -50,6 +52,7 @@ class CachedModule:
|
|
|
50
52
|
summaries: Mapping[str, FunctionSummary]
|
|
51
53
|
sites: tuple[CallSite, ...]
|
|
52
54
|
findings: tuple[Finding, ...]
|
|
55
|
+
reads: tuple[SymbolRead, ...] = ()
|
|
53
56
|
|
|
54
57
|
def __post_init__(self) -> None:
|
|
55
58
|
object.__setattr__(self, "summaries", MappingProxyType(dict(self.summaries)))
|
|
@@ -134,6 +137,7 @@ def encode(module: CachedModule) -> dict[str, Any]:
|
|
|
134
137
|
"summaries": {name: _encode_summary(s) for name, s in module.summaries.items()},
|
|
135
138
|
"sites": [_encode_site(site) for site in module.sites],
|
|
136
139
|
"findings": [_encode_finding(finding) for finding in module.findings],
|
|
140
|
+
"reads": [[r.function, _encode_span(r.location), str(r.symbol)] for r in module.reads],
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
|
|
@@ -145,6 +149,7 @@ def decode(data: Mapping[str, Any]) -> CachedModule:
|
|
|
145
149
|
{_string(name): _decode_summary(s) for name, s in data["summaries"].items()},
|
|
146
150
|
tuple(_decode_site(site) for site in data["sites"]),
|
|
147
151
|
tuple(_decode_finding(finding) for finding in data["findings"]),
|
|
152
|
+
tuple(_decode_read(read) for read in data["reads"]),
|
|
148
153
|
)
|
|
149
154
|
|
|
150
155
|
|
|
@@ -194,6 +199,11 @@ def _decode_function(data: Any) -> ModuleFunction:
|
|
|
194
199
|
return ModuleFunction(_string(name), _decode_span(span), None if entry_point is None else _string(entry_point))
|
|
195
200
|
|
|
196
201
|
|
|
202
|
+
def _decode_read(data: Any) -> SymbolRead:
|
|
203
|
+
function, span, symbol = data
|
|
204
|
+
return SymbolRead(_string(function), _decode_span(span), SymbolId(_string(symbol)))
|
|
205
|
+
|
|
206
|
+
|
|
197
207
|
def _encode_finding(finding: Finding) -> dict[str, Any]:
|
|
198
208
|
return {
|
|
199
209
|
"rule": finding.rule_id,
|
|
@@ -223,7 +233,7 @@ def _encode_call(call: ExternalCall) -> dict[str, Any]:
|
|
|
223
233
|
return {
|
|
224
234
|
"symbol": str(call.symbol),
|
|
225
235
|
"arguments": [sorted(deps) for deps in call.argument_dependencies],
|
|
226
|
-
"keywords": sorted(call.keyword_dependencies
|
|
236
|
+
"keywords": [[name, sorted(deps)] for name, deps in call.keyword_dependencies],
|
|
227
237
|
"location": _encode_span(call.location),
|
|
228
238
|
"call_site": None if call.call_site is None else _encode_span(call.call_site),
|
|
229
239
|
"given": _encode_arguments(call.arguments),
|
|
@@ -235,7 +245,7 @@ def _decode_call(data: Mapping[str, Any]) -> ExternalCall:
|
|
|
235
245
|
return ExternalCall(
|
|
236
246
|
SymbolId(_string(data["symbol"])),
|
|
237
247
|
tuple(_indices(deps) for deps in data["arguments"]),
|
|
238
|
-
_indices(data["keywords"]),
|
|
248
|
+
tuple((None if name is None else _string(name), _indices(deps)) for name, deps in data["keywords"]),
|
|
239
249
|
_decode_span(data["location"]),
|
|
240
250
|
None if site is None else _decode_span(site),
|
|
241
251
|
_decode_arguments(data["given"]),
|
{coretrace_python_analyzer-0.5.0 → coretrace_python_analyzer-0.7.0}/src/coretrace_python/cli.py
RENAMED
|
@@ -3,13 +3,20 @@ from __future__ import annotations
|
|
|
3
3
|
import argparse
|
|
4
4
|
import sys
|
|
5
5
|
import zipfile
|
|
6
|
+
from datetime import UTC, datetime
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
from coretrace_python import __version__, engine
|
|
9
10
|
from coretrace_python.analysis import AnalysisError
|
|
10
11
|
from coretrace_python.cache import ProjectCache
|
|
11
12
|
from coretrace_python.cfg import CFGError
|
|
12
|
-
from coretrace_python.dependency import
|
|
13
|
+
from coretrace_python.dependency import (
|
|
14
|
+
dump_advisories,
|
|
15
|
+
import_osv,
|
|
16
|
+
read_osv,
|
|
17
|
+
render_sbom,
|
|
18
|
+
render_vex,
|
|
19
|
+
)
|
|
13
20
|
from coretrace_python.findings import Severity
|
|
14
21
|
from coretrace_python.findings.baseline import Baseline, BaselineError
|
|
15
22
|
from coretrace_python.frontend import HIRBuildError, ParseError, build_hir
|
|
@@ -106,6 +113,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
106
113
|
metavar="PATH",
|
|
107
114
|
help="with --check on a directory, write a CycloneDX bill of materials to PATH",
|
|
108
115
|
)
|
|
116
|
+
parser.add_argument(
|
|
117
|
+
"--vex",
|
|
118
|
+
type=Path,
|
|
119
|
+
default=None,
|
|
120
|
+
metavar="PATH",
|
|
121
|
+
help="with --check on a directory, write to PATH an OpenVEX document saying whether "
|
|
122
|
+
"each advisory affecting a requirement affects the project",
|
|
123
|
+
)
|
|
109
124
|
parser.add_argument(
|
|
110
125
|
"--advisories",
|
|
111
126
|
action="append",
|
|
@@ -182,6 +197,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
182
197
|
if args.sbom is not None and not (args.check and args.path.is_dir()):
|
|
183
198
|
print("error: --sbom only applies to --check on a directory", file=sys.stderr)
|
|
184
199
|
return EXIT_ERROR
|
|
200
|
+
if args.vex is not None and not (args.check and args.path.is_dir()):
|
|
201
|
+
print("error: --vex only applies to --check on a directory", file=sys.stderr)
|
|
202
|
+
return EXIT_ERROR
|
|
185
203
|
if args.advisories and not (args.check and args.path.is_dir()):
|
|
186
204
|
print("error: --advisories only applies to --check on a directory", file=sys.stderr)
|
|
187
205
|
return EXIT_ERROR
|
|
@@ -227,6 +245,20 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
227
245
|
render_sbom(analysis.dependencies, analysis.advisories, engine.TOOL_NAME, __version__),
|
|
228
246
|
encoding="utf-8",
|
|
229
247
|
)
|
|
248
|
+
if args.vex is not None:
|
|
249
|
+
args.vex.write_text(
|
|
250
|
+
render_vex(
|
|
251
|
+
analysis.dependencies,
|
|
252
|
+
analysis.advisories,
|
|
253
|
+
(*analysis.findings, *analysis.suppressed, *analysis.accepted),
|
|
254
|
+
analysis.coverage,
|
|
255
|
+
args.path,
|
|
256
|
+
engine.TOOL_NAME,
|
|
257
|
+
__version__,
|
|
258
|
+
datetime.now(UTC),
|
|
259
|
+
),
|
|
260
|
+
encoding="utf-8",
|
|
261
|
+
)
|
|
230
262
|
else:
|
|
231
263
|
file_analysis = engine.analyze_file(SourceManager().load_file(args.path), plugin_roots)
|
|
232
264
|
findings, coverage = file_analysis.findings, file_analysis.coverage
|
|
@@ -22,6 +22,7 @@ from coretrace_python.dependency.graph import (
|
|
|
22
22
|
)
|
|
23
23
|
from coretrace_python.dependency.policy import POLICY_FILE, Policy, apply_policy, load_policy
|
|
24
24
|
from coretrace_python.dependency.sbom import render_sbom
|
|
25
|
+
from coretrace_python.dependency.vex import render_vex
|
|
25
26
|
|
|
26
27
|
__all__ = [
|
|
27
28
|
"ADVISORY_FILE",
|
|
@@ -45,4 +46,5 @@ __all__ = [
|
|
|
45
46
|
"parse_dependencies",
|
|
46
47
|
"read_osv",
|
|
47
48
|
"render_sbom",
|
|
49
|
+
"render_vex",
|
|
48
50
|
]
|