coretrace-python-analyzer 0.12.0__tar.gz → 0.13.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.12.0 → coretrace_python_analyzer-0.13.0}/CHANGELOG.md +10 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/docs/plugins.md +1 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/docs/usage.md +4 -4
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +22 -10
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/django/django_models.py +19 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +16 -1
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/__init__.py +2 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/engine.py +39 -12
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/models.py +25 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_nosql_injection.py +136 -10
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_release.py +1 -1
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/README.md +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cache.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/advisories.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/correlation.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/graph.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/engine.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/callgraph.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/summaries.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/templates.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/pygoat.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_code_injection.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_members.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.0 (2026-09-26)
|
|
4
|
+
|
|
5
|
+
### Precision
|
|
6
|
+
|
|
7
|
+
- The text attributes and URL parameters of a request object carry no `NOSQL`. Django, REST framework, aiohttp and tornado views read a request tainted as a whole, so `request.GET["name"]` and a URL parameter were `nosql-injection` like the body. Now `GET`, `POST`, `COOKIES`, `META`, `headers`, `query_params`, aiohttp's `query` and `match_info`, tornado's `arguments` and the other text attributes are text, and so are the URL parameters a view receives after the request. The body, the files, REST framework's `data` and aiohttp's `json()` keep the kinds of the request. The other kinds are unchanged, and so is the regression corpus. A helper function the request is passed to still reads it as a whole (#162).
|
|
8
|
+
|
|
9
|
+
### Plugins
|
|
10
|
+
|
|
11
|
+
- A `RequestObject(symbol, text=())` model says that the inputs from an entry point, a route registrar, a typed parameter or a source are request objects, and which of their attributes are text (#162).
|
|
12
|
+
|
|
3
13
|
## 0.12.0 (2026-09-26)
|
|
4
14
|
|
|
5
15
|
### Detection
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.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
|
|
@@ -151,6 +151,7 @@ class BottleModels(ModelPlugin):
|
|
|
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
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. |
|
|
154
|
+
| `RequestObject(symbol, text=())` | The inputs from `symbol`, an entry point, a route registrar, a typed parameter or a source, are request objects. A view receives the request first (after `self`), then URL parameters, which are text. Reading one of the request's `text` attributes gives text, as Django's `GET` or aiohttp's `query`; its body and files keep the request's kinds. An attribute read counts only when every source of the value is such a request. |
|
|
154
155
|
| `AuthorizationGuard(symbol, label)` | A decorator or a condition restricting who reaches the code, such as `login_required`; a flow behind it is a hotspot. |
|
|
155
156
|
| `Members(symbol, dynamic=None, defined=(), typed=())` | What the attributes and items of an instance of the class `symbol` give, for objects that give others by name. `typed` maps an attribute to the class of what it gives, called or not, since a call denotes its callee: pymongo's `get_database` gives a `Database`, as Flask-PyMongo's `db` property does. `dynamic`, when set, is the class of every item and of every attribute outside `defined` and `typed`: a pymongo client gives a `Database` by name (`client.shop`, `client["shop"]`), a database a `Collection`. Every path to a collection then denotes the collection class, so a sink on `Collection.find` covers them all. Without `dynamic`, an attribute keeps its path and an item its container's symbol, as for any class. |
|
|
156
157
|
|
|
@@ -112,7 +112,7 @@ variable is not treated that way yet.
|
|
|
112
112
|
| `command-injection` | Shell or process execution (`os.system`, `subprocess` with a string, …). |
|
|
113
113
|
| `code-injection` | The code `eval` or `exec` runs; the globals and locals passed with it are data. `dangerous-eval` reports every such call, this rule the ones attacker input reaches. |
|
|
114
114
|
| `sql-injection` | A database statement (`cursor.execute`, SQLAlchemy `text`, Django `raw`, …). Parameters of a parameterised query are not statements. |
|
|
115
|
-
| `nosql-injection` | A NoSQL query, such as a MongoDB filter, given attacker input that may hold a structure: a JSON body giving `{"$ne": null}` matches any value. The input that may hold a structure is JSON, the raw bodies a `json.loads` decodes, files, WebSocket messages and standard input. Text cannot hold an operator: form fields, query parameters, headers, cookies, URL parameters, the command line, the environment, and an entry-point parameter annotated with scalars (`str`, `int`, `Optional[str]`, `Annotated[str, Query()]`, `list[str]`, …), which FastAPI validates.
|
|
115
|
+
| `nosql-injection` | A NoSQL query, such as a MongoDB filter, given attacker input that may hold a structure: a JSON body giving `{"$ne": null}` matches any value. The input that may hold a structure is JSON, the raw bodies a `json.loads` decodes, files, WebSocket messages and standard input. Text cannot hold an operator: form fields, query parameters, headers, cookies, URL parameters, the command line, the environment, and an entry-point parameter annotated with scalars (`str`, `int`, `Optional[str]`, `Annotated[str, Query()]`, `list[str]`, …), which FastAPI validates. A Django, REST framework, aiohttp or tornado view reads a request object: its text attributes (`GET`, `POST`, `headers`, `query_params`, `query`, `match_info`, `arguments`, …) and the URL parameters after it are text, its body, files and `data` are not. Limits: a helper function the request is passed to reads it as a whole; the keys of a dict are not followed, so a string concatenated into `$where` is not reported. The analyzer ships no NoSQL sink: model plugins declare them. |
|
|
116
116
|
| `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
|
|
117
117
|
| `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. |
|
|
118
118
|
| `xss` | An HTTP response body built without escaping. A Django template rendered by `render_to_string` escapes, where the analyzer can establish it (below). |
|
|
@@ -241,7 +241,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
241
241
|
```json
|
|
242
242
|
{
|
|
243
243
|
"schema_version": 1,
|
|
244
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
244
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.13.0"},
|
|
245
245
|
"root": "/home/me/project",
|
|
246
246
|
"findings": [
|
|
247
247
|
{
|
|
@@ -273,7 +273,7 @@ bytes. A single-file check lists no components.
|
|
|
273
273
|
|
|
274
274
|
```json
|
|
275
275
|
"tool": {
|
|
276
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
276
|
+
"name": "coretrace-python-analyzer", "version": "0.13.0",
|
|
277
277
|
"components": [
|
|
278
278
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
279
279
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -436,7 +436,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
436
436
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
437
437
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
438
438
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
439
|
-
(`coretrace-python-analyzer 0.
|
|
439
|
+
(`coretrace-python-analyzer 0.13.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
440
440
|
`@id` changes with the advisory data a statement was decided with.
|
|
441
441
|
|
|
442
442
|
## Large projects
|
|
@@ -12,6 +12,7 @@ from coretrace_python.semantic.symbols import SymbolId
|
|
|
12
12
|
from coretrace_python.taint import (
|
|
13
13
|
EntryPoint,
|
|
14
14
|
Model,
|
|
15
|
+
RequestObject,
|
|
15
16
|
RouteRegistrar,
|
|
16
17
|
Sink,
|
|
17
18
|
Source,
|
|
@@ -22,6 +23,23 @@ from coretrace_python.taint import (
|
|
|
22
23
|
_METHODS = ("get", "post", "put", "patch", "delete", "head", "options")
|
|
23
24
|
_REQUEST_CLASSES = ("aiohttp.web.Request", "aiohttp.web_request.Request")
|
|
24
25
|
_ROUTERS = ("aiohttp.web.Application.router", "aiohttp.web.UrlDispatcher")
|
|
26
|
+
# ``@routes.get('/')`` on a ``RouteTableDef``.
|
|
27
|
+
_ROUTE_TABLES = tuple(f"aiohttp.web.RouteTableDef.{method}" for method in (*_METHODS, "route", "view"))
|
|
28
|
+
# ``app.router.add_route('GET', '/', handler)``, ``app.router.add_get('/', handler)`` and
|
|
29
|
+
# ``app.add_routes([web.get('/', handler)])``, each with the argument naming the handler.
|
|
30
|
+
_ROUTE_CALLS = (
|
|
31
|
+
*((f"{router}.add_route", 2) for router in _ROUTERS),
|
|
32
|
+
*((f"{router}.add_{method}", 1) for router in _ROUTERS for method in (*_METHODS, "view")),
|
|
33
|
+
("aiohttp.web.route", 2),
|
|
34
|
+
*((f"aiohttp.web.{method}", 1) for method in (*_METHODS, "view")),
|
|
35
|
+
)
|
|
36
|
+
# What a request gives as text; its body (``json()``, ``text()``, ``read()``) may hold a
|
|
37
|
+
# structure.
|
|
38
|
+
_REQUEST_TEXT = (
|
|
39
|
+
"query", "rel_url", "url", "path", "raw_path", "path_qs", "query_string", "match_info", "headers",
|
|
40
|
+
"raw_headers", "cookies", "method", "host", "scheme", "remote", "forwarded", "content_type", "charset",
|
|
41
|
+
"if_modified_since", "post",
|
|
42
|
+
)
|
|
25
43
|
_REDIRECTS = ("HTTPFound", "HTTPMovedPermanently", "HTTPSeeOther", "HTTPTemporaryRedirect", "HTTPPermanentRedirect")
|
|
26
44
|
_CLIENT_FUNCTIONS = (
|
|
27
45
|
*(f"aiohttp.ClientSession.{method}" for method in (*_METHODS, "request")),
|
|
@@ -40,18 +58,12 @@ class AiohttpModels(ModelPlugin):
|
|
|
40
58
|
# The whole request is attacker-controlled: its query, match info, headers,
|
|
41
59
|
# cookies and body all come from the client.
|
|
42
60
|
*(TypedParameter(_sym(cls), "http") for cls in _REQUEST_CLASSES),
|
|
43
|
-
|
|
44
|
-
*(
|
|
45
|
-
# ``app.router.add_route('GET', '/', handler)`` and ``app.router.add_get('/', handler)``.
|
|
46
|
-
*(RouteRegistrar(_sym(f"{router}.add_route"), 2, "http", keyword="handler") for router in _ROUTERS),
|
|
61
|
+
*(EntryPoint(_sym(table), "http") for table in _ROUTE_TABLES),
|
|
62
|
+
*(RouteRegistrar(_sym(call), argument, "http", keyword="handler") for call, argument in _ROUTE_CALLS),
|
|
47
63
|
*(
|
|
48
|
-
|
|
49
|
-
for
|
|
50
|
-
for method in (*_METHODS, "view")
|
|
64
|
+
RequestObject(_sym(symbol), _REQUEST_TEXT)
|
|
65
|
+
for symbol in (*_REQUEST_CLASSES, *_ROUTE_TABLES, *(call for call, _ in _ROUTE_CALLS))
|
|
51
66
|
),
|
|
52
|
-
# ``app.add_routes([web.get('/', handler)])``: the route definitions name the handler.
|
|
53
|
-
RouteRegistrar(_sym("aiohttp.web.route"), 2, "http", keyword="handler"),
|
|
54
|
-
*(RouteRegistrar(_sym(f"aiohttp.web.{method}"), 1, "http", keyword="handler") for method in (*_METHODS, "view")),
|
|
55
67
|
Sink(_sym("aiohttp.web.Response"), TaintKind.HTML),
|
|
56
68
|
Sink(_sym("aiohttp.web.FileResponse"), TaintKind.PATH),
|
|
57
69
|
*(Sink(_sym(f"aiohttp.web.{exception}"), TaintKind.REDIRECT) for exception in _REDIRECTS),
|
|
@@ -17,6 +17,7 @@ from coretrace_python.taint import (
|
|
|
17
17
|
AuthorizationGuard,
|
|
18
18
|
EntryPoint,
|
|
19
19
|
Model,
|
|
20
|
+
RequestObject,
|
|
20
21
|
RouteRegistrar,
|
|
21
22
|
Sanitizer,
|
|
22
23
|
Sink,
|
|
@@ -88,6 +89,23 @@ _AUTHORIZATION_DECORATORS = (
|
|
|
88
89
|
)
|
|
89
90
|
|
|
90
91
|
|
|
92
|
+
# What a request object gives as text: the query string, form fields, headers, cookies
|
|
93
|
+
# and path. Its body, its files and REST framework's parsed ``data`` may hold a structure.
|
|
94
|
+
_REQUEST_TEXT = (
|
|
95
|
+
"GET", "POST", "COOKIES", "META", "headers", "path", "path_info", "method", "scheme", "encoding",
|
|
96
|
+
"content_type", "content_params", "resolver_match", "get_full_path", "get_full_path_info",
|
|
97
|
+
"build_absolute_uri", "get_host", "get_port", "get_signed_cookie", "query_params",
|
|
98
|
+
)
|
|
99
|
+
# Every input a request object comes from: an annotated parameter, a class-based view's
|
|
100
|
+
# method or ``self.request``, a decorated view, a view registered in a URL configuration.
|
|
101
|
+
_REQUEST_INPUTS = (
|
|
102
|
+
*_REQUEST_CLASSES,
|
|
103
|
+
*_VIEW_BASES,
|
|
104
|
+
*(f"{base}.request" for base in _VIEW_BASES),
|
|
105
|
+
*_VIEW_DECORATORS,
|
|
106
|
+
*_ROUTE_REGISTRARS,
|
|
107
|
+
)
|
|
108
|
+
|
|
91
109
|
_TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
|
|
92
110
|
|
|
93
111
|
|
|
@@ -103,6 +121,7 @@ class DjangoModels(ModelPlugin):
|
|
|
103
121
|
# ``self.request`` in a class-based view: the attribute is inherited from the base.
|
|
104
122
|
*(Source(_sym(f"{base}.request"), "http") for base in _VIEW_BASES),
|
|
105
123
|
*(EntryPoint(_sym(decorator), "http") for decorator in _VIEW_DECORATORS),
|
|
124
|
+
*(RequestObject(_sym(symbol), _REQUEST_TEXT) for symbol in _REQUEST_INPUTS),
|
|
106
125
|
Sink(_sym("django.db.connection.cursor.execute"), TaintKind.SQL | TaintKind.CREDENTIAL, ((TaintKind.SQL, (0,)),)),
|
|
107
126
|
Sink(_sym("django.db.connection.cursor.executemany"), TaintKind.SQL | TaintKind.CREDENTIAL, ((TaintKind.SQL, (0,)),)),
|
|
108
127
|
Sink(_sym("django.db.models.expressions.RawSQL"), TaintKind.SQL | TaintKind.CREDENTIAL),
|
|
@@ -8,7 +8,16 @@ from typing import ClassVar
|
|
|
8
8
|
|
|
9
9
|
from coretrace_python.plugins import ModelPlugin
|
|
10
10
|
from coretrace_python.semantic.symbols import SymbolId
|
|
11
|
-
from coretrace_python.taint import
|
|
11
|
+
from coretrace_python.taint import (
|
|
12
|
+
TEXT_KINDS,
|
|
13
|
+
EntryPoint,
|
|
14
|
+
Model,
|
|
15
|
+
RequestObject,
|
|
16
|
+
Sanitizer,
|
|
17
|
+
Sink,
|
|
18
|
+
Source,
|
|
19
|
+
TaintKind,
|
|
20
|
+
)
|
|
12
21
|
|
|
13
22
|
_HANDLERS = ("tornado.web.RequestHandler", "tornado.websocket.WebSocketHandler")
|
|
14
23
|
# Text, which cannot hold a query operator; the request object holds the raw body too.
|
|
@@ -16,6 +25,11 @@ _TEXT_METHODS = (
|
|
|
16
25
|
"get_argument", "get_arguments", "get_query_argument", "get_query_arguments",
|
|
17
26
|
"get_body_argument", "get_body_arguments", "get_cookie", "path_args", "path_kwargs",
|
|
18
27
|
)
|
|
28
|
+
# What ``self.request`` gives as text; its body and files may hold a structure.
|
|
29
|
+
_REQUEST_TEXT = (
|
|
30
|
+
"arguments", "query_arguments", "body_arguments", "query", "uri", "path", "headers", "cookies",
|
|
31
|
+
"method", "host", "host_name", "remote_ip", "protocol", "version", "full_url",
|
|
32
|
+
)
|
|
19
33
|
_CLIENTS = ("tornado.httpclient.AsyncHTTPClient.fetch", "tornado.httpclient.HTTPClient.fetch")
|
|
20
34
|
_TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
|
|
21
35
|
|
|
@@ -33,6 +47,7 @@ class TornadoModels(ModelPlugin):
|
|
|
33
47
|
EntryPoint(_sym("tornado.websocket.WebSocketHandler"), "http"),
|
|
34
48
|
*(Source(_sym(f"{handler}.{method}"), "http", TEXT_KINDS) for handler in _HANDLERS for method in _TEXT_METHODS),
|
|
35
49
|
*(Source(_sym(f"{handler}.request"), "http") for handler in _HANDLERS),
|
|
50
|
+
*(RequestObject(_sym(f"{handler}.request"), _REQUEST_TEXT) for handler in _HANDLERS),
|
|
36
51
|
*(Sink(_sym(f"{handler}.write"), TaintKind.HTML) for handler in _HANDLERS),
|
|
37
52
|
*(Sink(_sym(f"{handler}.finish"), TaintKind.HTML) for handler in _HANDLERS),
|
|
38
53
|
*(Sink(_sym(f"{handler}.redirect"), TaintKind.REDIRECT, _TARGET_ONLY) for handler in _HANDLERS),
|
|
@@ -17,6 +17,7 @@ from coretrace_python.taint.models import (
|
|
|
17
17
|
ModelError,
|
|
18
18
|
ModelTable,
|
|
19
19
|
NamedParameter,
|
|
20
|
+
RequestObject,
|
|
20
21
|
RouteRegistrar,
|
|
21
22
|
SafeArgument,
|
|
22
23
|
Sanitizer,
|
|
@@ -45,6 +46,7 @@ __all__ = [
|
|
|
45
46
|
"ModelTable",
|
|
46
47
|
"NamedParameter",
|
|
47
48
|
"RegisteredRoutes",
|
|
49
|
+
"RequestObject",
|
|
48
50
|
"RouteRegistrar",
|
|
49
51
|
"Routes",
|
|
50
52
|
"SafeArgument",
|
|
@@ -74,6 +74,7 @@ from coretrace_python.semantic.scopes import ScopeAnalysis, ScopeId, ScopeTable
|
|
|
74
74
|
from coretrace_python.semantic.symbols import SymbolAnalysis, SymbolId, SymbolTable
|
|
75
75
|
from coretrace_python.source import SourceSpan
|
|
76
76
|
from coretrace_python.taint.models import (
|
|
77
|
+
TEXT_KINDS,
|
|
77
78
|
EntryPoint,
|
|
78
79
|
ModelTable,
|
|
79
80
|
SecurityModelAnalysis,
|
|
@@ -84,6 +85,9 @@ from coretrace_python.taint.models import (
|
|
|
84
85
|
from coretrace_python.taint.routes import RegisteredRoutes, Routes
|
|
85
86
|
from coretrace_python.taint.templates import EscapedTemplates
|
|
86
87
|
|
|
88
|
+
# What only a structure carries, and text does not.
|
|
89
|
+
_STRUCTURED = TaintKind.ALL & ~TEXT_KINDS
|
|
90
|
+
|
|
87
91
|
|
|
88
92
|
@dataclass(frozen=True)
|
|
89
93
|
class Taint:
|
|
@@ -274,19 +278,42 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
274
278
|
taint = Taint.none()
|
|
275
279
|
symbol = self.symbols.get(instruction.result) if instruction.result else None
|
|
276
280
|
if symbol is not None:
|
|
277
|
-
|
|
278
|
-
if source is not None:
|
|
279
|
-
taint = Taint(source.kinds, frozenset({source}))
|
|
281
|
+
taint = self.covered(symbol)
|
|
280
282
|
if isinstance(instruction, Symbol | Compare):
|
|
281
283
|
return taint
|
|
282
284
|
for operand in instruction.operands():
|
|
283
|
-
|
|
285
|
+
operand_taint = state.get(operand, Taint.none())
|
|
286
|
+
if isinstance(instruction, GetAttr):
|
|
287
|
+
operand_taint = self.attribute(operand_taint, instruction.attribute)
|
|
288
|
+
taint = taint.join(operand_taint)
|
|
284
289
|
if isinstance(instruction, BuildList | BuildTuple | BuildDict | BuildSet):
|
|
285
290
|
# ``[*xs]`` and ``{**d}`` copy the contents of what they unpack.
|
|
286
291
|
for unpacked in instruction.unpacked:
|
|
287
292
|
taint = taint.join(self.deep(unpacked, state))
|
|
288
293
|
return taint
|
|
289
294
|
|
|
295
|
+
def covered(self, symbol: SymbolId) -> Taint:
|
|
296
|
+
"""The taint of the source covering ``symbol``, if any. Below a request source, as
|
|
297
|
+
``self.request.GET`` is, it is the taint of the request attribute read."""
|
|
298
|
+
|
|
299
|
+
source = self.models.source_covering(symbol)
|
|
300
|
+
if source is None:
|
|
301
|
+
return Taint.none()
|
|
302
|
+
taint = Taint(source.kinds, frozenset({source}))
|
|
303
|
+
if symbol == source.symbol:
|
|
304
|
+
return taint
|
|
305
|
+
read = symbol.canonical_name.removeprefix(f"{source.symbol.canonical_name}.").split(".")[0]
|
|
306
|
+
return self.attribute(taint, read)
|
|
307
|
+
|
|
308
|
+
def attribute(self, taint: Taint, name: str) -> Taint:
|
|
309
|
+
"""What attribute ``name`` of a value carrying ``taint`` carries: text when every
|
|
310
|
+
source of the value is a request object listing ``name`` among its text attributes."""
|
|
311
|
+
|
|
312
|
+
requests = [self.models.request_object(source.symbol) for source in taint.sources]
|
|
313
|
+
if taint and all(request is not None and name in request.text for request in requests):
|
|
314
|
+
return taint.without(_STRUCTURED)
|
|
315
|
+
return taint
|
|
316
|
+
|
|
290
317
|
def call(self, call: Call, state: dict[Key, Taint], flows: list[TaintFlow]) -> Taint:
|
|
291
318
|
arguments = tuple(self.deep(a, state) for a in call.arguments)
|
|
292
319
|
keywords = Taint.none()
|
|
@@ -420,11 +447,7 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
420
447
|
if cleared:
|
|
421
448
|
return everything.without(TaintKind(cleared))
|
|
422
449
|
# A method on a tainted object returns tainted data (``request.args.get``).
|
|
423
|
-
|
|
424
|
-
source = self.models.source_covering(symbol)
|
|
425
|
-
if source is not None:
|
|
426
|
-
return everything.join(Taint(source.kinds, frozenset({source})))
|
|
427
|
-
return everything
|
|
450
|
+
return everything.join(state.get(call.callee, Taint.none())).join(self.covered(symbol))
|
|
428
451
|
|
|
429
452
|
def guarded(self, sink: Sink | None, arguments: Arguments) -> Sink | None:
|
|
430
453
|
"""The sink as this call makes it: an argument declared safe (``yaml.load`` with
|
|
@@ -777,7 +800,8 @@ def parameter_sources(
|
|
|
777
800
|
entry point (``self`` excepted for a method), including one registered elsewhere
|
|
778
801
|
(``routes``), and every parameter annotated with a typed-parameter symbol. An entry
|
|
779
802
|
point's parameter annotated with scalars is text: FastAPI validates it before the
|
|
780
|
-
handler runs, and a scalar cannot hold a query operator.
|
|
803
|
+
handler runs, and a scalar cannot hold a query operator. So are the URL parameters
|
|
804
|
+
a view receives after a request object."""
|
|
781
805
|
|
|
782
806
|
owner = _owner(module, function)
|
|
783
807
|
sources: dict[int, Source] = {}
|
|
@@ -786,10 +810,13 @@ def parameter_sources(
|
|
|
786
810
|
entry = function_entry_point(function, module, models, scopes, symbols, instances, routes)
|
|
787
811
|
if entry is not None:
|
|
788
812
|
first = 1 if owner is not None else 0
|
|
813
|
+
request = models.request_object(entry.symbol)
|
|
789
814
|
for index in range(first, len(function.parameters)):
|
|
790
815
|
annotation = function.parameters[index].annotation
|
|
791
|
-
|
|
792
|
-
|
|
816
|
+
text = (request is not None and index > first) or (
|
|
817
|
+
annotation is not None and _scalar(annotation, symbols, enclosing)
|
|
818
|
+
)
|
|
819
|
+
kinds = entry.kinds & ~_STRUCTURED if text else entry.kinds
|
|
793
820
|
sources[index] = Source(entry.symbol, entry.label, kinds)
|
|
794
821
|
for index, parameter in enumerate(function.parameters):
|
|
795
822
|
if parameter.annotation is None:
|
|
@@ -205,6 +205,22 @@ class TemplateRender:
|
|
|
205
205
|
keyword: str = "template_name"
|
|
206
206
|
|
|
207
207
|
|
|
208
|
+
@dataclass(frozen=True)
|
|
209
|
+
class RequestObject:
|
|
210
|
+
"""The inputs from ``symbol`` are request objects; ``symbol`` names an entry point, a
|
|
211
|
+
route registrar, a typed parameter or a source. A view receives the request first
|
|
212
|
+
(after ``self``), then URL parameters, which are text. Reading one of the request's
|
|
213
|
+
``text`` attributes (``GET``, ``headers``) gives text, while its body and files keep the
|
|
214
|
+
kinds of the request: text carries ``TEXT_KINDS``."""
|
|
215
|
+
|
|
216
|
+
symbol: SymbolId
|
|
217
|
+
text: tuple[str, ...] = ()
|
|
218
|
+
|
|
219
|
+
def __post_init__(self) -> None:
|
|
220
|
+
# Sorted: the models' repr is part of the cache key, the same in every process.
|
|
221
|
+
object.__setattr__(self, "text", tuple(sorted(set(self.text))))
|
|
222
|
+
|
|
223
|
+
|
|
208
224
|
@dataclass(frozen=True)
|
|
209
225
|
class AuthorizationGuard:
|
|
210
226
|
"""A decorator, or a condition, that restricts who reaches the code behind it; a
|
|
@@ -228,6 +244,7 @@ Model = (
|
|
|
228
244
|
| SafeArgument
|
|
229
245
|
| TemplateRender
|
|
230
246
|
| Members
|
|
247
|
+
| RequestObject
|
|
231
248
|
)
|
|
232
249
|
|
|
233
250
|
|
|
@@ -246,6 +263,7 @@ class ModelTable:
|
|
|
246
263
|
safe_arguments: tuple[SafeArgument, ...] = ()
|
|
247
264
|
template_renders: tuple[TemplateRender, ...] = ()
|
|
248
265
|
members: tuple[Members, ...] = ()
|
|
266
|
+
request_objects: tuple[RequestObject, ...] = ()
|
|
249
267
|
_by_symbol: dict[type[Model], dict[SymbolId, Model]] = field(
|
|
250
268
|
init=False, repr=False, compare=False
|
|
251
269
|
)
|
|
@@ -262,6 +280,7 @@ class ModelTable:
|
|
|
262
280
|
RouteRegistrar: {m.symbol: m for m in self.route_registrars},
|
|
263
281
|
SafeArgument: {m.symbol: m for m in self.safe_arguments},
|
|
264
282
|
TemplateRender: {m.symbol: m for m in self.template_renders},
|
|
283
|
+
RequestObject: {m.symbol: m for m in self.request_objects},
|
|
265
284
|
}
|
|
266
285
|
object.__setattr__(self, "_by_symbol", MappingProxyType(index))
|
|
267
286
|
|
|
@@ -330,6 +349,7 @@ class ModelTable:
|
|
|
330
349
|
self.safe_arguments,
|
|
331
350
|
self.template_renders,
|
|
332
351
|
self.members,
|
|
352
|
+
self.request_objects,
|
|
333
353
|
)
|
|
334
354
|
|
|
335
355
|
def sanitizer(self, symbol: SymbolId) -> Sanitizer | None:
|
|
@@ -344,6 +364,10 @@ class ModelTable:
|
|
|
344
364
|
found = self._by_symbol[TemplateRender].get(symbol)
|
|
345
365
|
return found if isinstance(found, TemplateRender) else None
|
|
346
366
|
|
|
367
|
+
def request_object(self, symbol: SymbolId) -> RequestObject | None:
|
|
368
|
+
found = self._by_symbol[RequestObject].get(symbol)
|
|
369
|
+
return found if isinstance(found, RequestObject) else None
|
|
370
|
+
|
|
347
371
|
def members_by_class(self) -> Mapping[SymbolId, Members]:
|
|
348
372
|
"""The ``Members`` models by class, as symbol resolution reads them."""
|
|
349
373
|
|
|
@@ -408,6 +432,7 @@ class SecurityModelRegistry:
|
|
|
408
432
|
safe_arguments=tuple(m for m in models if isinstance(m, SafeArgument)),
|
|
409
433
|
template_renders=tuple(m for m in models if isinstance(m, TemplateRender)),
|
|
410
434
|
members=tuple(m for m in models if isinstance(m, Members)),
|
|
435
|
+
request_objects=tuple(m for m in models if isinstance(m, RequestObject)),
|
|
411
436
|
)
|
|
412
437
|
|
|
413
438
|
|
{coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/tests/test_nosql_injection.py
RENAMED
|
@@ -11,9 +11,13 @@ text too, since FastAPI validates it before the handler runs.
|
|
|
11
11
|
|
|
12
12
|
The engine ships no NOSQL sink; these tests declare one, ``docstore.find(filter)``.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
a
|
|
14
|
+
Django, Django REST framework, aiohttp and tornado hand views a request object, tainted as
|
|
15
|
+
a whole: its text attributes (``GET``, ``headers``, ``query``, ``arguments``) are text, its
|
|
16
|
+
body and files are not, and the URL parameters a view receives after it are text.
|
|
17
|
+
|
|
18
|
+
Limits: a helper function the request is passed to reads it as a whole, since function
|
|
19
|
+
summaries do not know where their parameters come from; the keys of a dict literal are
|
|
20
|
+
not followed, so a string concatenated into ``$where`` is not reported.
|
|
17
21
|
"""
|
|
18
22
|
|
|
19
23
|
from __future__ import annotations
|
|
@@ -176,9 +180,12 @@ def test_standard_input_is_a_payload_and_the_command_line_text(plugins: list[Pat
|
|
|
176
180
|
|
|
177
181
|
|
|
178
182
|
DJANGO = (
|
|
179
|
-
"import json\n\nimport docstore\nfrom django.http import HttpRequest, JsonResponse\n\n"
|
|
180
|
-
"def
|
|
181
|
-
" return JsonResponse(docstore.find({query}))\n"
|
|
183
|
+
"import json\n\nimport docstore\nfrom django.http import HttpRequest, JsonResponse\nfrom django.urls import path\n\n"
|
|
184
|
+
"def search(request, slug):\n"
|
|
185
|
+
" return JsonResponse(docstore.find({query}))\n\n"
|
|
186
|
+
"def typed(request: HttpRequest):\n"
|
|
187
|
+
" return JsonResponse(docstore.find({query}))\n\n"
|
|
188
|
+
"urlpatterns = [path('search/<slug>/', search)]\n"
|
|
182
189
|
)
|
|
183
190
|
|
|
184
191
|
|
|
@@ -186,12 +193,131 @@ DJANGO = (
|
|
|
186
193
|
"query",
|
|
187
194
|
[
|
|
188
195
|
"json.loads(request.body)",
|
|
189
|
-
|
|
190
|
-
|
|
196
|
+
"json.loads(request.read())",
|
|
197
|
+
"json.load(request.FILES['filter'])",
|
|
198
|
+
],
|
|
199
|
+
)
|
|
200
|
+
def test_the_body_and_files_of_a_django_request_are_payloads(plugins: list[Path], query: str) -> None:
|
|
201
|
+
assert injected(plugins, DJANGO.format(query=query)) == [8, 11]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@pytest.mark.parametrize(
|
|
205
|
+
"query",
|
|
206
|
+
[
|
|
207
|
+
"{'name': request.GET['name']}",
|
|
208
|
+
"{'name': request.POST.get('name')}",
|
|
209
|
+
"{'agent': request.headers['User-Agent'], 'meta': request.META['REMOTE_ADDR']}",
|
|
210
|
+
"{'token': request.COOKIES['token'], 'path': request.get_full_path()}",
|
|
211
|
+
],
|
|
212
|
+
)
|
|
213
|
+
def test_the_text_attributes_of_a_django_request_are_text(plugins: list[Path], query: str) -> None:
|
|
214
|
+
assert injected(plugins, DJANGO.format(query=query)) == []
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def test_the_url_parameters_after_the_request_are_text(plugins: list[Path]) -> None:
|
|
218
|
+
text = DJANGO.format(query="{'tag': slug}").replace("def typed(request: HttpRequest)", "def typed(request, slug)")
|
|
219
|
+
|
|
220
|
+
assert injected(plugins, text) == []
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@pytest.mark.parametrize(
|
|
224
|
+
"query, reported",
|
|
225
|
+
[
|
|
226
|
+
("json.loads(request.body)", True),
|
|
227
|
+
("json.loads(self.request.body)", True),
|
|
228
|
+
("{'name': request.POST['name']}", False),
|
|
229
|
+
("{'name': self.request.GET['name']}", False),
|
|
230
|
+
("{'tag': slug}", False),
|
|
191
231
|
],
|
|
192
232
|
)
|
|
193
|
-
def
|
|
194
|
-
|
|
233
|
+
def test_a_class_based_view_receives_the_same_request(plugins: list[Path], query: str, reported: bool) -> None:
|
|
234
|
+
text = (
|
|
235
|
+
"import json\n\nimport docstore\nfrom django.views import View\n\n"
|
|
236
|
+
"class Search(View):\n"
|
|
237
|
+
" def post(self, request, slug):\n"
|
|
238
|
+
f" return docstore.find({query})\n"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
assert injected(plugins, text) == ([8] if reported else [])
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@pytest.mark.parametrize("query, reported", [("request.data", True), ("{'q': request.query_params['q']}", False)])
|
|
245
|
+
def test_a_rest_framework_request_parses_the_body_into_data(plugins: list[Path], query: str, reported: bool) -> None:
|
|
246
|
+
text = (
|
|
247
|
+
"import docstore\nfrom rest_framework.decorators import api_view\n\n"
|
|
248
|
+
"@api_view(['POST'])\n"
|
|
249
|
+
"def create(request):\n"
|
|
250
|
+
f" return docstore.find({query})\n"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
assert injected(plugins, text) == ([6] if reported else [])
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@pytest.mark.parametrize(
|
|
257
|
+
"query, reported",
|
|
258
|
+
[
|
|
259
|
+
("await request.json()", True),
|
|
260
|
+
("{'name': request.match_info['name']}", False),
|
|
261
|
+
("{'q': request.query['q'], 'agent': request.headers['User-Agent']}", False),
|
|
262
|
+
("{'name': (await request.post())['name']}", False),
|
|
263
|
+
],
|
|
264
|
+
)
|
|
265
|
+
def test_an_aiohttp_request_is_text_but_for_its_body(plugins: list[Path], query: str, reported: bool) -> None:
|
|
266
|
+
text = (
|
|
267
|
+
"import docstore\nfrom aiohttp import web\n\n"
|
|
268
|
+
"routes = web.RouteTableDef()\n\n"
|
|
269
|
+
"@routes.post('/users/{{name}}')\n"
|
|
270
|
+
"async def users(request):\n"
|
|
271
|
+
f" return docstore.find({query})\n\n"
|
|
272
|
+
"async def typed(request: web.Request):\n"
|
|
273
|
+
f" return docstore.find({query})\n"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
assert injected(plugins, text) == ([8, 11] if reported else [])
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@pytest.mark.parametrize(
|
|
280
|
+
"query, reported",
|
|
281
|
+
[
|
|
282
|
+
("json.loads(self.request.body)", True),
|
|
283
|
+
("{'args': self.request.arguments, 'uri': self.request.uri}", False),
|
|
284
|
+
("{'name': name}", False),
|
|
285
|
+
],
|
|
286
|
+
)
|
|
287
|
+
def test_a_tornado_request_is_text_but_for_its_body(plugins: list[Path], query: str, reported: bool) -> None:
|
|
288
|
+
text = (
|
|
289
|
+
"import json\n\nimport docstore\nimport tornado.web\n\n"
|
|
290
|
+
"class Users(tornado.web.RequestHandler):\n"
|
|
291
|
+
" def post(self, name):\n"
|
|
292
|
+
f" docstore.find({query})\n"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
assert injected(plugins, text) == ([8] if reported else [])
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_text_attributes_still_carry_the_other_kinds(plugins: list[Path]) -> None:
|
|
299
|
+
text = (
|
|
300
|
+
"from django.http import HttpResponse\nfrom django.urls import path\n\n"
|
|
301
|
+
"def hello(request, slug):\n"
|
|
302
|
+
" return HttpResponse(request.GET['name'] + slug)\n\n"
|
|
303
|
+
"urlpatterns = [path('hello/<slug>/', hello)]\n"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
assert [f.span.start_line for f in check(plugins, text) if f.rule_id == "xss"] == [5]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def test_a_helper_the_request_is_passed_to_reads_it_as_a_whole(plugins: list[Path]) -> None:
|
|
310
|
+
text = (
|
|
311
|
+
"import docstore\nfrom rest_framework.decorators import api_view\n\n"
|
|
312
|
+
"def lookup(request):\n"
|
|
313
|
+
" return docstore.find({'name': request.GET['name']})\n\n"
|
|
314
|
+
"@api_view(['GET'])\n"
|
|
315
|
+
"def view(request):\n"
|
|
316
|
+
" return lookup(request)\n"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Limit: the summary of ``lookup`` does not know its parameter is a request.
|
|
320
|
+
assert injected(plugins, text) == [9]
|
|
195
321
|
|
|
196
322
|
|
|
197
323
|
def test_parameters_of_one_entry_point_remain_one_origin(plugins: list[Path]) -> None:
|
|
@@ -30,7 +30,7 @@ def require_changelog() -> None:
|
|
|
30
30
|
def test_the_version_is_declared_once_and_mirrored() -> None:
|
|
31
31
|
project = tomllib.loads((REPO / "pyproject.toml").read_text(encoding="utf-8"))["project"]
|
|
32
32
|
|
|
33
|
-
assert project["version"] == coretrace_python.__version__ == "0.
|
|
33
|
+
assert project["version"] == coretrace_python.__version__ == "0.13.0"
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def test_the_changelog_starts_with_the_current_version() -> None:
|
{coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/ci.yml
RENAMED
|
File without changes
|
{coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/release.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coretrace_python_analyzer-0.12.0 → coretrace_python_analyzer-0.13.0}/examples/canonical_symbols.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|