coretrace-python-analyzer 0.11.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.11.0 → coretrace_python_analyzer-0.13.0}/CHANGELOG.md +23 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/docs/plugins.md +3 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/docs/usage.md +4 -3
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +22 -10
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +10 -6
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/cli/cli_models.py +4 -4
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/django/django_models.py +19 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/flask/flask_models.py +12 -9
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +5 -3
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +25 -5
- coretrace_python_analyzer-0.13.0/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +20 -0
- coretrace_python_analyzer-0.13.0/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +9 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/engine.py +7 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/callgraph.py +14 -12
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/lowering.py +1 -1
- coretrace_python_analyzer-0.13.0/src/coretrace_python/semantic/__init__.py +9 -0
- coretrace_python_analyzer-0.13.0/src/coretrace_python/semantic/symbols.py +177 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/__init__.py +6 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/engine.py +105 -15
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/models.py +41 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_analysis_manager.py +3 -2
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_detectors.py +2 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_http_clients.py +1 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_docs.py +1 -1
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_frameworks.py +47 -2
- coretrace_python_analyzer-0.13.0/tests/test_members.py +206 -0
- coretrace_python_analyzer-0.13.0/tests/test_nosql_injection.py +332 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_operator_sources.py +6 -4
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_packaging.py +2 -2
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.11.0/src/coretrace_python/semantic/__init__.py +0 -9
- coretrace_python_analyzer-0.11.0/src/coretrace_python/semantic/symbols.py +0 -87
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/README.md +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cache.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/advisories.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/correlation.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/graph.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/summaries.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/templates.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/pygoat.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_code_injection.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,28 @@
|
|
|
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
|
+
|
|
13
|
+
## 0.12.0 (2026-09-26)
|
|
14
|
+
|
|
15
|
+
### Detection
|
|
16
|
+
|
|
17
|
+
- A module-level name bound to an attribute, an item or a call of another module-level name resolves in functions, as the same value does in a function body. `cursor = conn.cursor()` after `conn = sqlite3.connect(...)`, `db = client.shop` and `run = os.system` used to leave the calls made through them unknown, so a query built from request data and executed through a module-level cursor was not reported. Names resolve in statement order. The regression corpus is unchanged (#155).
|
|
18
|
+
- `nosql-injection` reports attacker input that may hold a structure reaching a NoSQL sink, such as a MongoDB filter, where a JSON body giving `{"$ne": null}` matches any value. Only input that may hold a structure carries `NOSQL`: JSON, the raw bodies a `json.loads` decodes, files, WebSocket messages and standard input. Text cannot hold an operator. The text sources of Flask, bottle and tornado, the URL parameters of their routes, the command line, the environment and `input()` no longer carry `NOSQL`. Neither does an entry-point parameter annotated with scalars (`str`, `Optional[int]`, `Annotated[str, Query()]`, `list[str]`, …), which FastAPI validates. Two limits remain. Django and aiohttp request objects stay tainted as a whole. The keys of a dict are not followed, so a string concatenated into `$where` is not reported. The engine ships no NoSQL sink: model plugins declare them (#158).
|
|
19
|
+
|
|
20
|
+
### Plugins
|
|
21
|
+
|
|
22
|
+
- `TEXT_KINDS` holds every kind but `NOSQL`: what text input carries. A model plugin declares its text sources with it (#158).
|
|
23
|
+
|
|
24
|
+
- A `Members(symbol, dynamic=None, defined=(), typed=())` model says what the attributes and items of a class's instances give, for libraries whose objects give others by names the project chooses. A pymongo client gives the database it is dotted or indexed with, a database gives a collection. The names used to end up in the symbol (`MongoClient.shop.users.find`), or an item vanished into its container's symbol (`MongoClient.find`), so no model could list them. With the model, every path to a collection denotes the collection class, the one a parameter annotated `Collection` denotes: `client.shop.users`, `client["shop"]["users"]`, `client.get_database("shop").get_collection("users")`, a module-level `db = client.shop`, an attribute inherited from the client class. A sink on `Collection.find` covers them all. Symbol resolution now depends on these models, which the engine provides to every module and worker (#157).
|
|
25
|
+
|
|
3
26
|
## 0.11.0 (2026-09-25)
|
|
4
27
|
|
|
5
28
|
### 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
|
|
@@ -140,7 +140,7 @@ class BottleModels(ModelPlugin):
|
|
|
140
140
|
|
|
141
141
|
| Model | Declares |
|
|
142
142
|
|---|---|
|
|
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. |
|
|
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. Text input, such as a form field, a header or a URL parameter, carries `TEXT_KINDS`, every kind but `NOSQL`: a string cannot hold a query operator. |
|
|
144
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`, including when the call is inside a project function the data goes through. |
|
|
146
146
|
| `EntryPoint(symbol, label, kinds=ALL)` | A decorator or a class base whose functions receive their parameters as `label` input, such as `flask.Flask.route`. |
|
|
@@ -151,7 +151,9 @@ 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. |
|
|
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. |
|
|
155
157
|
|
|
156
158
|
Two plugins may describe the same symbol. An identical model is registered once; two
|
|
157
159
|
`Sink` models merge their kinds, positions and keywords, so a plugin can add `SQL` to a sink
|
|
@@ -112,6 +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. 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. |
|
|
115
116
|
| `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
|
|
116
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. |
|
|
117
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). |
|
|
@@ -240,7 +241,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
240
241
|
```json
|
|
241
242
|
{
|
|
242
243
|
"schema_version": 1,
|
|
243
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
244
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.13.0"},
|
|
244
245
|
"root": "/home/me/project",
|
|
245
246
|
"findings": [
|
|
246
247
|
{
|
|
@@ -272,7 +273,7 @@ bytes. A single-file check lists no components.
|
|
|
272
273
|
|
|
273
274
|
```json
|
|
274
275
|
"tool": {
|
|
275
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
276
|
+
"name": "coretrace-python-analyzer", "version": "0.13.0",
|
|
276
277
|
"components": [
|
|
277
278
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
278
279
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -435,7 +436,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
435
436
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
436
437
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
437
438
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
438
|
-
(`coretrace-python-analyzer 0.
|
|
439
|
+
(`coretrace-python-analyzer 0.13.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
439
440
|
`@id` changes with the advisory data a statement was decided with.
|
|
440
441
|
|
|
441
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),
|
|
@@ -8,13 +8,15 @@ 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 EntryPoint, Model, Sanitizer, Sink, Source, TaintKind
|
|
11
|
+
from coretrace_python.taint import TEXT_KINDS, EntryPoint, Model, Sanitizer, Sink, Source, TaintKind
|
|
12
12
|
|
|
13
13
|
_METHODS = ("route", "get", "post", "put", "delete", "patch")
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
# Text, which cannot hold a query operator; JSON, raw bodies and files may hold a structure.
|
|
15
|
+
_TEXT_ATTRIBUTES = (
|
|
16
|
+
"query", "forms", "params", "headers", "cookies", "GET", "POST",
|
|
16
17
|
"url", "path", "fullpath", "query_string", "get_cookie", "get_header",
|
|
17
18
|
)
|
|
19
|
+
_PAYLOAD_ATTRIBUTES = ("json", "body", "files")
|
|
18
20
|
_FIRST_ONLY = lambda kind: ((kind, (0,)),)
|
|
19
21
|
|
|
20
22
|
|
|
@@ -25,9 +27,11 @@ def _sym(path: str) -> SymbolId:
|
|
|
25
27
|
class BottleModels(ModelPlugin):
|
|
26
28
|
name: ClassVar[str] = "bottle-models"
|
|
27
29
|
models: ClassVar[tuple[Model, ...]] = (
|
|
28
|
-
|
|
29
|
-
*(EntryPoint(_sym(f"bottle.
|
|
30
|
-
*(
|
|
30
|
+
# A route's parameters are segments of its URL.
|
|
31
|
+
*(EntryPoint(_sym(f"bottle.{method}"), "http", TEXT_KINDS) for method in _METHODS),
|
|
32
|
+
*(EntryPoint(_sym(f"bottle.Bottle.{method}"), "http", TEXT_KINDS) for method in _METHODS),
|
|
33
|
+
*(Source(_sym(f"bottle.request.{attribute}"), "http", TEXT_KINDS) for attribute in _TEXT_ATTRIBUTES),
|
|
34
|
+
*(Source(_sym(f"bottle.request.{attribute}"), "http") for attribute in _PAYLOAD_ATTRIBUTES),
|
|
31
35
|
Sink(_sym("bottle.redirect"), TaintKind.REDIRECT, _FIRST_ONLY(TaintKind.REDIRECT)),
|
|
32
36
|
# ``static_file(filename, root)``: the root is the application's own.
|
|
33
37
|
Sink(_sym("bottle.static_file"), TaintKind.PATH, _FIRST_ONLY(TaintKind.PATH)),
|
|
@@ -6,11 +6,11 @@ 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 EntryPoint, Model, TaintKind
|
|
9
|
+
from coretrace_python.taint import TEXT_KINDS, EntryPoint, Model, TaintKind
|
|
10
10
|
|
|
11
|
-
# A command-line tool is expected to open the paths it is given: argv input
|
|
12
|
-
#
|
|
13
|
-
ARGV_KINDS =
|
|
11
|
+
# A command-line tool is expected to open the paths it is given: argv input is text,
|
|
12
|
+
# without PATH.
|
|
13
|
+
ARGV_KINDS = TEXT_KINDS & ~TaintKind.PATH
|
|
14
14
|
|
|
15
15
|
_COMMANDS = (
|
|
16
16
|
"click.command",
|
|
@@ -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),
|
|
@@ -7,6 +7,7 @@ from typing import ClassVar
|
|
|
7
7
|
from coretrace_python.plugins import ModelPlugin
|
|
8
8
|
from coretrace_python.semantic.symbols import SymbolId
|
|
9
9
|
from coretrace_python.taint import (
|
|
10
|
+
TEXT_KINDS,
|
|
10
11
|
AuthorizationGuard,
|
|
11
12
|
EntryPoint,
|
|
12
13
|
Model,
|
|
@@ -17,10 +18,10 @@ from coretrace_python.taint import (
|
|
|
17
18
|
TaintKind,
|
|
18
19
|
)
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
21
|
+
# Strings, or mappings of strings: text, which cannot hold a query operator.
|
|
22
|
+
_TEXT_ATTRIBUTES = ("args", "form", "values", "cookies", "headers", "url", "full_path", "path", "query_string")
|
|
23
|
+
# JSON, and the raw bodies and files a ``json.loads`` may decode into a structure.
|
|
24
|
+
_PAYLOAD_ATTRIBUTES = ("json", "get_json", "data", "get_data", "stream", "files")
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
_TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
|
|
@@ -33,11 +34,13 @@ def _sym(path: str) -> SymbolId:
|
|
|
33
34
|
class FlaskModels(ModelPlugin):
|
|
34
35
|
name: ClassVar[str] = "flask-models"
|
|
35
36
|
models: ClassVar[tuple[Model, ...]] = (
|
|
36
|
-
*(Source(_sym(f"flask.request.{attribute}"), "http") for attribute in
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
*(Source(_sym(f"flask.request.{attribute}"), "http", TEXT_KINDS) for attribute in _TEXT_ATTRIBUTES),
|
|
38
|
+
*(Source(_sym(f"flask.request.{attribute}"), "http") for attribute in _PAYLOAD_ATTRIBUTES),
|
|
39
|
+
# A route's parameters are segments of its URL.
|
|
40
|
+
EntryPoint(_sym("flask.Flask.route"), "http", TEXT_KINDS),
|
|
41
|
+
EntryPoint(_sym("flask.Blueprint.route"), "http", TEXT_KINDS),
|
|
42
|
+
RouteRegistrar(_sym("flask.Flask.add_url_rule"), 2, "http", TEXT_KINDS, keyword="view_func"),
|
|
43
|
+
RouteRegistrar(_sym("flask.Blueprint.add_url_rule"), 2, "http", TEXT_KINDS, keyword="view_func"),
|
|
41
44
|
Sink(_sym("flask.render_template_string"), TaintKind.HTML),
|
|
42
45
|
Sink(_sym("flask.make_response"), TaintKind.HTML),
|
|
43
46
|
Sink(_sym("flask.Response"), TaintKind.HTML),
|
|
@@ -7,6 +7,7 @@ from typing import ClassVar
|
|
|
7
7
|
from coretrace_python.plugins import ModelPlugin
|
|
8
8
|
from coretrace_python.semantic.symbols import SymbolId
|
|
9
9
|
from coretrace_python.taint import (
|
|
10
|
+
TEXT_KINDS,
|
|
10
11
|
Model,
|
|
11
12
|
SafeArgument,
|
|
12
13
|
Sanitizer,
|
|
@@ -16,7 +17,7 @@ from coretrace_python.taint import (
|
|
|
16
17
|
Validator,
|
|
17
18
|
)
|
|
18
19
|
|
|
19
|
-
_ENVIRONMENT_KINDS =
|
|
20
|
+
_ENVIRONMENT_KINDS = TEXT_KINDS & ~(TaintKind.COMMAND | TaintKind.PATH)
|
|
20
21
|
# SafeLoader and BaseLoader build plain data only, in Python and in C, under every spelling.
|
|
21
22
|
_SAFE_YAML_LOADERS = (
|
|
22
23
|
"python.yaml.SafeLoader",
|
|
@@ -38,13 +39,14 @@ def _sym(path: str) -> SymbolId:
|
|
|
38
39
|
class PythonStdlibModels(ModelPlugin):
|
|
39
40
|
name: ClassVar[str] = "python-stdlib-models"
|
|
40
41
|
models: ClassVar[tuple[Model, ...]] = (
|
|
41
|
-
|
|
42
|
+
# A line of text; the stream may be a payload ``json.load`` decodes.
|
|
43
|
+
Source(_sym("builtins.input"), "stdin", TEXT_KINDS),
|
|
42
44
|
Source(_sym("sys.stdin"), "stdin"),
|
|
43
45
|
# Operator-controlled inputs. A command-line tool is expected to open the paths
|
|
44
46
|
# it is given; the environment is set by whoever runs the program, so a command
|
|
45
47
|
# or a path built from it is not an injection; the output of a local process is
|
|
46
48
|
# not a path either, but a downloaded script piped into a shell is a real flaw.
|
|
47
|
-
Source(_sym("sys.argv"), "argv",
|
|
49
|
+
Source(_sym("sys.argv"), "argv", TEXT_KINDS & ~TaintKind.PATH),
|
|
48
50
|
Source(_sym("os.environ"), "environment", _ENVIRONMENT_KINDS),
|
|
49
51
|
Source(_sym("subprocess.run.stdout"), "process-output", _PROCESS_OUTPUT_KINDS),
|
|
50
52
|
Source(_sym("subprocess.check_output"), "process-output", _PROCESS_OUTPUT_KINDS),
|
|
@@ -8,12 +8,27 @@ 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.
|
|
24
|
+
_TEXT_METHODS = (
|
|
15
25
|
"get_argument", "get_arguments", "get_query_argument", "get_query_arguments",
|
|
16
|
-
"get_body_argument", "get_body_arguments", "get_cookie", "
|
|
26
|
+
"get_body_argument", "get_body_arguments", "get_cookie", "path_args", "path_kwargs",
|
|
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",
|
|
17
32
|
)
|
|
18
33
|
_CLIENTS = ("tornado.httpclient.AsyncHTTPClient.fetch", "tornado.httpclient.HTTPClient.fetch")
|
|
19
34
|
_TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
|
|
@@ -26,8 +41,13 @@ def _sym(path: str) -> SymbolId:
|
|
|
26
41
|
class TornadoModels(ModelPlugin):
|
|
27
42
|
name: ClassVar[str] = "tornado-models"
|
|
28
43
|
models: ClassVar[tuple[Model, ...]] = (
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
# A request handler's parameters are segments of its URL; a WebSocket handler also
|
|
45
|
+
# receives the messages, raw payloads a ``json.loads`` may decode.
|
|
46
|
+
EntryPoint(_sym("tornado.web.RequestHandler"), "http", TEXT_KINDS),
|
|
47
|
+
EntryPoint(_sym("tornado.websocket.WebSocketHandler"), "http"),
|
|
48
|
+
*(Source(_sym(f"{handler}.{method}"), "http", TEXT_KINDS) for handler in _HANDLERS for method in _TEXT_METHODS),
|
|
49
|
+
*(Source(_sym(f"{handler}.request"), "http") for handler in _HANDLERS),
|
|
50
|
+
*(RequestObject(_sym(f"{handler}.request"), _REQUEST_TEXT) for handler in _HANDLERS),
|
|
31
51
|
*(Sink(_sym(f"{handler}.write"), TaintKind.HTML) for handler in _HANDLERS),
|
|
32
52
|
*(Sink(_sym(f"{handler}.finish"), TaintKind.HTML) for handler in _HANDLERS),
|
|
33
53
|
*(Sink(_sym(f"{handler}.redirect"), TaintKind.REDIRECT, _TARGET_ONLY) for handler in _HANDLERS),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""NoSQL injection: attacker input that may hold a structure reaching a NOSQL sink, such
|
|
2
|
+
as a MongoDB filter, where a JSON body giving ``{"$ne": null}`` matches any value. Text
|
|
3
|
+
input carries no ``NOSQL``: a string cannot hold an operator. The engine ships no NOSQL
|
|
4
|
+
sink; model plugins declare them."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import ClassVar
|
|
9
|
+
|
|
10
|
+
from coretrace_python.findings import Severity
|
|
11
|
+
from coretrace_python.plugins import TaintDetector
|
|
12
|
+
from coretrace_python.taint import TaintKind
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NoSqlInjectionPlugin(TaintDetector):
|
|
16
|
+
name: ClassVar[str] = "nosql-injection"
|
|
17
|
+
rule_id: ClassVar[str] = "nosql-injection"
|
|
18
|
+
kind: ClassVar[TaintKind] = TaintKind.NOSQL
|
|
19
|
+
severity: ClassVar[Severity] = Severity.HIGH
|
|
20
|
+
title: ClassVar[str] = "NoSQL injection"
|
{coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/engine.py
RENAMED
|
@@ -109,7 +109,7 @@ from coretrace_python.reporters import Report
|
|
|
109
109
|
from coretrace_python.semantic import SEMANTIC_ANALYSES
|
|
110
110
|
from coretrace_python.semantic.imports import ImportAnalysis, ImportResolutionError, ImportTable
|
|
111
111
|
from coretrace_python.semantic.scopes import ScopeAnalysis, ScopeError
|
|
112
|
-
from coretrace_python.semantic.symbols import SymbolAnalysis, SymbolId
|
|
112
|
+
from coretrace_python.semantic.symbols import MembersAnalysis, SymbolAnalysis, SymbolId
|
|
113
113
|
from coretrace_python.source import SourceFile, SourceId, SourceManager, SourceSpan, decode_text
|
|
114
114
|
from coretrace_python.taint import (
|
|
115
115
|
EntryPoint,
|
|
@@ -257,6 +257,7 @@ def build_manager(
|
|
|
257
257
|
manager = _register_all(module)
|
|
258
258
|
table = (models or SecurityModelRegistry()).freeze()
|
|
259
259
|
manager.provide(SecurityModelAnalysis, table)
|
|
260
|
+
manager.provide(MembersAnalysis, table.members_by_class())
|
|
260
261
|
manager.provide(ClearingAnalysis, table.clearing())
|
|
261
262
|
manager.provide(ProjectSummaries, SummaryIndex())
|
|
262
263
|
manager.provide(RegisteredRoutes, _routes_of(manager))
|
|
@@ -311,6 +312,7 @@ def analyze_file(source: SourceFile, plugin_roots: Sequence[Path]) -> FileAnalys
|
|
|
311
312
|
registry = load_plugins(plugin_roots, manager)
|
|
312
313
|
table = plugin_models(loaded.plugin for loaded in registry)
|
|
313
314
|
manager.provide(SecurityModelAnalysis, table)
|
|
315
|
+
manager.provide(MembersAnalysis, table.members_by_class())
|
|
314
316
|
manager.provide(ClearingAnalysis, table.clearing())
|
|
315
317
|
manager.provide(ProjectSummaries, SummaryIndex())
|
|
316
318
|
manager.provide(RegisteredRoutes, _routes_of(manager))
|
|
@@ -402,8 +404,10 @@ def analyze_project(
|
|
|
402
404
|
advisories, origins = _merge_advisories(_contributions(registry, plugins, root, file_advisories))
|
|
403
405
|
affected = affected_symbols(dependencies, advisories)
|
|
404
406
|
models = plugin_models(all_plugins, root).extended(*advisory_sinks(affected))
|
|
407
|
+
members = models.members_by_class()
|
|
405
408
|
for manager in managers.values():
|
|
406
409
|
manager.provide(SecurityModelAnalysis, models)
|
|
410
|
+
manager.provide(MembersAnalysis, members)
|
|
407
411
|
manager.provide(DependencyAnalysis, dependencies)
|
|
408
412
|
|
|
409
413
|
imports: dict[str, ImportTable] = {}
|
|
@@ -698,8 +702,10 @@ def _analyse_batch(batch: _Batch) -> dict[str, dict[str, Any]]:
|
|
|
698
702
|
models = plugin_models(all_plugins, batch.root).extended(*advisory_sinks(affected))
|
|
699
703
|
routes = _decode_routes(batch.routes)
|
|
700
704
|
escaped = frozenset(batch.escaped)
|
|
705
|
+
members = models.members_by_class()
|
|
701
706
|
for manager in managers.values():
|
|
702
707
|
manager.provide(SecurityModelAnalysis, models)
|
|
708
|
+
manager.provide(MembersAnalysis, members)
|
|
703
709
|
manager.provide(DependencyAnalysis, dependencies)
|
|
704
710
|
manager.provide(RegisteredRoutes, routes)
|
|
705
711
|
manager.provide(EscapedTemplates, escaped)
|
|
@@ -176,12 +176,12 @@ class CallGraph:
|
|
|
176
176
|
|
|
177
177
|
|
|
178
178
|
def derive_symbols(
|
|
179
|
-
function: FunctionIR, initial: Mapping[Value, SymbolId] | None = None
|
|
179
|
+
function: FunctionIR, table: SymbolTable, initial: Mapping[Value, SymbolId] | None = None
|
|
180
180
|
) -> dict[Value, SymbolId]:
|
|
181
|
-
"""Symbols of values: ``Symbol`` results, attributes and items of symbol values,
|
|
182
|
-
results of calling a symbol (``sqlite3.connect(p)`` denotes
|
|
183
|
-
and the values a ``with`` on such a result binds. Known
|
|
184
|
-
parameters only through ``initial``, their annotated classes."""
|
|
181
|
+
"""Symbols of values: ``Symbol`` results, attributes and items of symbol values, as
|
|
182
|
+
``table`` types them, results of calling a symbol (``sqlite3.connect(p)`` denotes
|
|
183
|
+
``python.sqlite3.connect``) and the values a ``with`` on such a result binds. Known
|
|
184
|
+
functions derive nothing; parameters only through ``initial``, their annotated classes."""
|
|
185
185
|
|
|
186
186
|
symbols: dict[Value, SymbolId] = dict(initial or {})
|
|
187
187
|
changed = True
|
|
@@ -195,11 +195,9 @@ def derive_symbols(
|
|
|
195
195
|
if isinstance(instruction, Symbol):
|
|
196
196
|
symbol = instruction.symbol_id
|
|
197
197
|
elif isinstance(instruction, GetAttr) and instruction.object in symbols:
|
|
198
|
-
symbol = symbols[instruction.object]
|
|
198
|
+
symbol = table.attribute(symbols[instruction.object], instruction.attribute)
|
|
199
199
|
elif isinstance(instruction, GetItem) and instruction.object in symbols:
|
|
200
|
-
|
|
201
|
-
# carries the container's symbol, so its methods resolve.
|
|
202
|
-
symbol = symbols[instruction.object]
|
|
200
|
+
symbol = table.item(symbols[instruction.object])
|
|
203
201
|
elif isinstance(instruction, Call | WithEnter):
|
|
204
202
|
origin = (
|
|
205
203
|
instruction.callee if isinstance(instruction, Call) else instruction.context
|
|
@@ -263,6 +261,8 @@ def resolve_targets(
|
|
|
263
261
|
owner: str | None = None,
|
|
264
262
|
typed: Mapping[Value, str] | None = None,
|
|
265
263
|
bases: Mapping[str, SymbolId] | None = None,
|
|
264
|
+
*,
|
|
265
|
+
table: SymbolTable,
|
|
266
266
|
) -> tuple[dict[Value, Target], dict[Value, SymbolId]]:
|
|
267
267
|
"""Map every callee value of ``function`` to its target, and every symbol value.
|
|
268
268
|
``nested`` names the functions defined inside this one (``outer.inner``);
|
|
@@ -271,7 +271,8 @@ def resolve_targets(
|
|
|
271
271
|
call to ``App.__init__``, ``app.run()`` and ``self.run()`` calls to ``App.run``.
|
|
272
272
|
``bases`` maps a module class to the symbol of its base: an attribute the class does
|
|
273
273
|
not define is inherited, so ``self.get_argument`` in a ``RequestHandler`` subclass
|
|
274
|
-
denotes ``tornado.web.RequestHandler.get_argument``.
|
|
274
|
+
denotes ``tornado.web.RequestHandler.get_argument``. ``table`` says what the
|
|
275
|
+
attributes and items of a class a ``Members`` model describes give."""
|
|
275
276
|
|
|
276
277
|
module = scopes.module_scope
|
|
277
278
|
classes = classes or {}
|
|
@@ -294,8 +295,8 @@ def resolve_targets(
|
|
|
294
295
|
class_name = instance_of[instruction.object]
|
|
295
296
|
base = (bases or {}).get(class_name)
|
|
296
297
|
if base is not None and instruction.attribute not in classes.get(class_name, ()):
|
|
297
|
-
inherited[instruction.result] =
|
|
298
|
-
symbols = derive_symbols(function, inherited)
|
|
298
|
+
inherited[instruction.result] = table.attribute(base, instruction.attribute)
|
|
299
|
+
symbols = derive_symbols(function, table, inherited)
|
|
299
300
|
targets: dict[Value, Target] = {
|
|
300
301
|
value: ExternalSymbol(symbol) for value, symbol in symbols.items()
|
|
301
302
|
}
|
|
@@ -427,6 +428,7 @@ class CallGraphAnalysis(Analysis[CallGraph]):
|
|
|
427
428
|
owner,
|
|
428
429
|
_typed_with_module_classes(function, ssa, classes),
|
|
429
430
|
bases,
|
|
431
|
+
table=table,
|
|
430
432
|
)
|
|
431
433
|
defs = {i.result: i for block in ssa.blocks for i in block.instructions if i.result is not None}
|
|
432
434
|
found: list[CallSite] = []
|
|
@@ -141,7 +141,7 @@ class _FunctionLowerer:
|
|
|
141
141
|
return self.symbols.resolve(self.scope.id, node.identifier)
|
|
142
142
|
if isinstance(node, nodes.Attribute):
|
|
143
143
|
parent = self.imported_symbol(node.value)
|
|
144
|
-
return
|
|
144
|
+
return self.symbols.attribute(parent, node.name) if parent is not None else None
|
|
145
145
|
return None
|
|
146
146
|
|
|
147
147
|
def expression(self, node: nodes.Expression) -> Value:
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Semantic analyses computed from PyHIR: scopes, imports and symbols."""
|
|
2
|
+
|
|
3
|
+
from coretrace_python.semantic.imports import ImportAnalysis
|
|
4
|
+
from coretrace_python.semantic.scopes import ScopeAnalysis
|
|
5
|
+
from coretrace_python.semantic.symbols import MembersAnalysis, SymbolAnalysis
|
|
6
|
+
|
|
7
|
+
SEMANTIC_ANALYSES = (ScopeAnalysis, ImportAnalysis, MembersAnalysis, SymbolAnalysis)
|
|
8
|
+
|
|
9
|
+
__all__ = ["SEMANTIC_ANALYSES", "ImportAnalysis", "MembersAnalysis", "ScopeAnalysis", "SymbolAnalysis"]
|