coretrace-python-analyzer 0.20.0__tar.gz → 0.21.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.20.0 → coretrace_python_analyzer-0.21.0}/CHANGELOG.md +12 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/docs/plugins.md +6 -2
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/docs/usage.md +77 -3
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/__init__.py +1 -1
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +19 -13
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +10 -3
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/cache.py +17 -1
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dependency/advisories.py +36 -1
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dependency/correlation.py +151 -13
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dependency/graph.py +20 -4
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/engine.py +32 -7
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/interprocedural/__init__.py +2 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/interprocedural/callgraph.py +131 -5
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/taint/models.py +10 -7
- coretrace_python_analyzer-0.21.0/tests/test_argument_present_conditions.py +205 -0
- coretrace_python_analyzer-0.21.0/tests/test_entry_point_suffixes.py +259 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.21.0/tests/test_sequence_conditions.py +461 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/README.md +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/abstract/strings.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/cli.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/interprocedural/summaries.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/plugins/api.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/taint/__init__.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/taint/engine.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/taint/templates.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/taint/urls.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/pygoat.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_advisory_url_host.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_base_patterns.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_block_super.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_code_injection.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_declared_entry_points.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_entry_point_aliases.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_function_aliases_api.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_key_taint.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_keyword_name_conditions.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_members.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_module_identity.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_open_redirect_destinations.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_settings_scope.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_ssrf_destinations.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_template_context_flows.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_template_filter_calls.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_template_filter_overrides.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_template_request_processor.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_unreadable_templates.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_url_proof.py +0 -0
- {coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.21.0 (2026-09-30)
|
|
4
|
+
|
|
5
|
+
### Advisories
|
|
6
|
+
|
|
7
|
+
- An `argument` condition may carry `present: true`: the argument being passed at all, whatever its value, is the condition, as a `\r` in `web.Response(reason=…)` needs `reason` given, constant or not. It names the argument by keyword or position as today and excludes `values` and `default`. A call giving the argument — positionally, by keyword, or through a `**` literal with constant keys, folded into the keywords — meets it even when the value is unknown, so attacker input there makes the call exploitable; a call surely not giving it is ruled out (`app:5 python.aiohttp.web.Response(reason absent)`) and a project passing only such calls stays `not_affected`; a call unpacking `*args` or a `**mapping` whose keys are not written leaves it pending review (#195).
|
|
8
|
+
- A curated entry point may declare `suffixes`, for receivers the engine cannot type: `Item.objects.annotate(...)` derives the project's own `python.app.models.Item.objects.annotate`, which the canonical `python.django.db.models.QuerySet.annotate` entry point never matches. A suffix matches any symbol of the project's own modules ending with a dot plus the suffix, on dot boundaries as a `SuffixSink` matches, never another package's symbol; a queryset chain is declared explicitly (`objects.filter.annotate`), and a suffix is at least two dot-separated names. The finding keeps the advisory's symbol as `entry_point` and the derived one as `symbol`; conditions, `attacker_arguments` and the ruled-out evidence of contradicted calls apply through a suffix as through the exact symbol. An exact advisory sink whose symbol a bundled suffix sink also matches (`objects.extra`) now composes its kinds with the suffix sink's instead of shadowing them (#197).
|
|
9
|
+
- An advisory condition of kind `sequence` says the flaw spans two calls on one receiver: the first call leaves state on the object the entry point — the second call — reuses. It names the `method` the first call must be, by canonical name or `.`-suffix, and, optionally, the argument that call must carry (`{"kind": "sequence", "method": "Session.get", "argument": "verify", "values": ["False"], ...}`), so `s = requests.Session(); s.get(url, verify=False); s.get(other)` marks the second `get` reachable with the prior call as evidence (requests CVE-2024-35195). A receiver followed whole — bound once to the result of a call or a `with` and used only to call its methods — with no matching call on every path to the entry call rules it out (`app.fetch:6 python.requests.Session.get(no prior Session.get on the receiver)`), and a project with only such calls stays `not_affected`. A receiver the engine cannot follow (a parameter, a module-level instance, one passed to another function or mutated through an attribute), a matching call only in a branch or textually later, an unpacking that leaves the first call's arguments undecided, or an entry call that itself carries the named argument — its own prior across the iterations of a loop — leaves the condition pending review (#194).
|
|
10
|
+
|
|
11
|
+
### Plugins
|
|
12
|
+
|
|
13
|
+
- `CallSite.prior_calls` records, for a receiver the engine follows whole, the other external calls on the same receiver (`PriorCall`: symbol, location, arguments, and whether it surely executes before this one), in source order; `CallGraph.prior_calls_at` reads it by location, and `check_conditions` takes it to decide `sequence` conditions. The cache format changes.
|
|
14
|
+
|
|
3
15
|
## 0.20.0 (2026-09-30)
|
|
4
16
|
|
|
5
17
|
### Advisories
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.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
|
|
@@ -195,8 +195,12 @@ A model plugin may also carry `advisories`, a tuple of `Advisory` values
|
|
|
195
195
|
symbols, as the shipped `sample-advisories` plugin does. Requirements matching them are
|
|
196
196
|
reported, calls to the affected symbols become reachable vulnerabilities and tainted
|
|
197
197
|
calls become exploitable ones. An advisory's entry points carry `conditions`, of kind
|
|
198
|
-
`argument
|
|
199
|
-
|
|
198
|
+
`argument` (on the value, or with `present` on the argument being passed at all),
|
|
199
|
+
`host`, `semantic`, `keyword_name` or `sequence`, and `attacker_arguments`, decided as
|
|
200
|
+
the [usage guide](usage.md) describes. An entry point may also declare `suffixes`,
|
|
201
|
+
matched against the tail of a derived symbol of the project's own modules on dot
|
|
202
|
+
boundaries, as a `SuffixSink` matches, for receivers the engine cannot type (see the
|
|
203
|
+
usage guide).
|
|
200
204
|
|
|
201
205
|
### Reclassifying findings: `ProjectPlugin.refine`
|
|
202
206
|
|
|
@@ -247,7 +247,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
247
247
|
```json
|
|
248
248
|
{
|
|
249
249
|
"schema_version": 1,
|
|
250
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
250
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.21.0"},
|
|
251
251
|
"root": "/home/me/project",
|
|
252
252
|
"findings": [
|
|
253
253
|
{
|
|
@@ -281,7 +281,7 @@ bytes. A single-file check lists no components.
|
|
|
281
281
|
|
|
282
282
|
```json
|
|
283
283
|
"tool": {
|
|
284
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
284
|
+
"name": "coretrace-python-analyzer", "version": "0.21.0",
|
|
285
285
|
"components": [
|
|
286
286
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
287
287
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -370,6 +370,29 @@ it is reachable, called or not — `request.form['name']`, `for key in request.f
|
|
|
370
370
|
`request.form.get('name')` all read it. A function reading it several times is reported
|
|
371
371
|
once, where it first reads it.
|
|
372
372
|
|
|
373
|
+
An entry point may declare `suffixes`, for receivers the engine cannot type. A project
|
|
374
|
+
reaches `python.django.db.models.QuerySet.annotate` through a manager on its own model,
|
|
375
|
+
`Item.objects.annotate(...)`, whose call derives the project's own symbol
|
|
376
|
+
`python.app.models.Item.objects.annotate` — `Item` is a project class, `objects` an
|
|
377
|
+
attribute the engine does not type — so the canonical entry point never matches. A
|
|
378
|
+
suffix matches any symbol of the project's own modules ending with a dot plus the
|
|
379
|
+
suffix:
|
|
380
|
+
|
|
381
|
+
```json
|
|
382
|
+
{"symbol": "python.django.db.models.QuerySet.annotate", "justification": "…",
|
|
383
|
+
"suffixes": ["objects.annotate", "objects.filter.annotate"]}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
A queryset chain is declared explicitly: `Item.objects.filter(...).annotate(...)`
|
|
387
|
+
derives `…objects.filter.annotate`, which only the second suffix matches. A suffix is
|
|
388
|
+
at least two dot-separated names, so a bare method name, matching every project method
|
|
389
|
+
of that name, cannot be declared; another package's symbol, such as
|
|
390
|
+
`other_lib.objects.annotate`, never matches. The finding names the advisory's symbol as
|
|
391
|
+
`entry_point` and the derived one as `symbol`, and the entry point's conditions and
|
|
392
|
+
`attacker_arguments` apply through a suffix as through the exact symbol. A project
|
|
393
|
+
attribute chain genuinely ending with a declared suffix matches too — the vulnerable
|
|
394
|
+
version range and the conditions still gate what is reported.
|
|
395
|
+
|
|
373
396
|
A filter of Django's own libraries that a project template applies is a call to the
|
|
374
397
|
function behind it, placed at the template's line: `{{ bio|striptags }}` calls
|
|
375
398
|
`python.django.template.defaultfilters.striptags`, and so do filters in tag arguments
|
|
@@ -471,6 +494,16 @@ a call passing a variable, or unpacking `*args` or `**kwargs`, leaves the condit
|
|
|
471
494
|
pending review. A `semantic` condition — the document's tags, the platform, the
|
|
472
495
|
template's origin — cannot be decided and is always pending review, never dropped.
|
|
473
496
|
|
|
497
|
+
An `argument` condition with `"present": true` says the argument being passed at all,
|
|
498
|
+
whatever its value, is the condition, as a `\r` in `web.Response(reason=…)` needs
|
|
499
|
+
`reason` given, constant or not: `{"kind": "argument", "text": "…", "argument":
|
|
500
|
+
"reason", "position": 1, "present": true}`, exclusive with `values` and `default`. A
|
|
501
|
+
call giving the argument — positionally, by keyword, or through a `**` literal with
|
|
502
|
+
constant keys, which is folded into the keywords — meets it even when the value is
|
|
503
|
+
unknown; a call surely not giving it does not reach the vulnerability and is ruled out
|
|
504
|
+
(`(reason absent)`); a call unpacking `*args` or a `**mapping` whose keys the engine
|
|
505
|
+
cannot read leaves it pending review.
|
|
506
|
+
|
|
474
507
|
A `host` condition says the attacker must choose the host of the URL passed as
|
|
475
508
|
`argument` (or at `position`), as credentials leaking to the host of a crafted URL
|
|
476
509
|
need: `{"kind": "host", "text": "…", "argument": "url", "position": 0}`. It is
|
|
@@ -514,6 +547,47 @@ Keys passed through a project wrapper stay pending, and `mapping.update(other)`,
|
|
|
514
547
|
taint. A literal the function fills after building it (`m = {}; m[alias] = expr`)
|
|
515
548
|
has keys the literal does not write, so the call is not ruled out.
|
|
516
549
|
|
|
550
|
+
A `sequence` condition says the flaw spans two calls on one receiver: a first call
|
|
551
|
+
leaves state on the object, and the entry point — the second call — reuses it, as
|
|
552
|
+
`Session.get(url, verify=False)` disables certificate verification for the pooled
|
|
553
|
+
connection a later `Session.get(other)` on the same session reuses (requests
|
|
554
|
+
CVE-2024-35195). It names the `method` the first call must be, by canonical name or
|
|
555
|
+
`.`-suffix, and, optionally, the argument that call must carry, read as an argument
|
|
556
|
+
condition reads it:
|
|
557
|
+
|
|
558
|
+
```json
|
|
559
|
+
{
|
|
560
|
+
"kind": "sequence",
|
|
561
|
+
"text": "a prior call on the same session disabled certificate verification",
|
|
562
|
+
"method": "Session.get",
|
|
563
|
+
"argument": "verify",
|
|
564
|
+
"values": ["False"]
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
The engine follows a receiver bound once in the analysed function to the result of a
|
|
569
|
+
call or a `with` and used only to call its methods. The outcome has three cases:
|
|
570
|
+
|
|
571
|
+
- A prior call on the same receiver, on every path to the entry call, matches the
|
|
572
|
+
method and carries a value in `values` (`default` for an absent argument): the
|
|
573
|
+
condition is met, and the evidence names that call's line.
|
|
574
|
+
- The receiver's whole lifetime is visible and no such prior call can precede — one
|
|
575
|
+
call alone on the receiver, or the two calls on different receivers: the condition
|
|
576
|
+
is contradicted, the call is ruled out
|
|
577
|
+
(`app.fetch:6 python.requests.Session.get(no prior Session.get on the receiver)`)
|
|
578
|
+
and a project with only such calls stays `not_affected`.
|
|
579
|
+
- Otherwise the condition stays pending review: a receiver the engine cannot follow —
|
|
580
|
+
a parameter, a module-level instance, one passed to another function or mutated
|
|
581
|
+
through an attribute such as `s.verify = False` — a prior call whose arguments a
|
|
582
|
+
`*`/`**` unpacking leaves undecided, or a flow reaching the entry point inside a
|
|
583
|
+
callee.
|
|
584
|
+
|
|
585
|
+
Only a prior call on every path to the entry call counts as before it: a matching
|
|
586
|
+
call in a branch that may not run, or a textually later one, leaves the condition
|
|
587
|
+
pending, since a loop may bring it before the entry call. A call that itself carries
|
|
588
|
+
what the condition requires of the first call is never ruled out either: across the
|
|
589
|
+
iterations of a loop it is its own prior.
|
|
590
|
+
|
|
517
591
|
Every dependency finding records the highest level of evidence established in its
|
|
518
592
|
`level` metadata: `declared` (the requirement allows a vulnerable version), `imported`
|
|
519
593
|
(a module of the package is imported somewhere), `reachable` (an entry point or affected
|
|
@@ -577,7 +651,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
577
651
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
578
652
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
579
653
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
580
|
-
(`coretrace-python-analyzer 0.
|
|
654
|
+
(`coretrace-python-analyzer 0.21.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
581
655
|
`@id` changes with the advisory data a statement was decided with.
|
|
582
656
|
|
|
583
657
|
## Large projects
|
|
@@ -8,12 +8,14 @@ from collections.abc import Iterator, Sequence
|
|
|
8
8
|
from typing import ClassVar
|
|
9
9
|
|
|
10
10
|
from coretrace_python.analysis import AnyAnalysis
|
|
11
|
-
from coretrace_python.dependency import Advisory, DependencyAnalysis
|
|
11
|
+
from coretrace_python.dependency import Advisory, AdvisoryEntryPoint, DependencyAnalysis
|
|
12
12
|
from coretrace_python.dependency.correlation import (
|
|
13
13
|
ConditionCheck,
|
|
14
|
+
affected_entries,
|
|
14
15
|
affected_symbols,
|
|
15
16
|
check_conditions,
|
|
16
17
|
evidence,
|
|
18
|
+
suffix_index,
|
|
17
19
|
)
|
|
18
20
|
from coretrace_python.findings import Confidence, Finding
|
|
19
21
|
from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
|
|
@@ -30,6 +32,7 @@ class ReachableVulnerabilityPlugin(ProjectPlugin):
|
|
|
30
32
|
affected = affected_symbols(ctx.dependencies, ctx.advisories)
|
|
31
33
|
if not affected:
|
|
32
34
|
return ()
|
|
35
|
+
suffixes = suffix_index(affected, ctx.modules)
|
|
33
36
|
findings: list[Finding] = []
|
|
34
37
|
for module in ctx.modules:
|
|
35
38
|
graph = ctx.call_graph(module)
|
|
@@ -37,30 +40,28 @@ class ReachableVulnerabilityPlugin(ProjectPlugin):
|
|
|
37
40
|
for site in graph.sites(function):
|
|
38
41
|
if not isinstance(site.target, ExternalSymbol):
|
|
39
42
|
continue
|
|
40
|
-
for advisory in
|
|
41
|
-
entry = advisory.entry_point(site.target.symbol)
|
|
43
|
+
for advisory, entry in affected_entries(site.target.symbol, affected, suffixes):
|
|
42
44
|
if entry is not None and entry.read:
|
|
43
45
|
continue # a call reads its callee first: reported with the reads
|
|
44
|
-
check = check_conditions(entry, site.arguments)
|
|
46
|
+
check = check_conditions(entry, site.arguments, prior_calls=site.prior_calls)
|
|
45
47
|
if check.contradicted is not None:
|
|
46
48
|
continue
|
|
47
|
-
findings.append(_reached(advisory, site.target.symbol, site.location, function, check))
|
|
49
|
+
findings.append(_reached(advisory, site.target.symbol, site.location, function, check, entry))
|
|
48
50
|
# Reading ``request.form.get`` reads ``request.form``: one finding per entry
|
|
49
51
|
# point and function, where the function first reads it.
|
|
50
52
|
reported: set[tuple[Advisory, SymbolId]] = set()
|
|
51
53
|
for read in graph.reads(function):
|
|
52
54
|
for symbol in _read_through(read.symbol):
|
|
53
|
-
for advisory in
|
|
54
|
-
entry = advisory.entry_point(symbol)
|
|
55
|
+
for advisory, entry in affected_entries(symbol, affected, suffixes):
|
|
55
56
|
if entry is None or not entry.read or (advisory, symbol) in reported:
|
|
56
57
|
continue
|
|
57
58
|
reported.add((advisory, symbol))
|
|
58
59
|
check = check_conditions(entry, None)
|
|
59
|
-
findings.append(_reached(advisory, symbol, read.location, function, check))
|
|
60
|
+
findings.append(_reached(advisory, symbol, read.location, function, check, entry))
|
|
60
61
|
for call in ctx.templates.calls:
|
|
61
|
-
for advisory in
|
|
62
|
-
check = check_conditions(
|
|
63
|
-
findings.append(_reached(advisory, call.symbol, call.span, None, check))
|
|
62
|
+
for advisory, entry in affected_entries(call.symbol, affected, suffixes):
|
|
63
|
+
check = check_conditions(entry, None)
|
|
64
|
+
findings.append(_reached(advisory, call.symbol, call.span, None, check, entry))
|
|
64
65
|
return findings
|
|
65
66
|
|
|
66
67
|
|
|
@@ -73,7 +74,12 @@ def _read_through(symbol: SymbolId) -> Iterator[SymbolId]:
|
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
def _reached(
|
|
76
|
-
advisory: Advisory,
|
|
77
|
+
advisory: Advisory,
|
|
78
|
+
symbol: SymbolId,
|
|
79
|
+
span: SourceSpan,
|
|
80
|
+
function: str | None,
|
|
81
|
+
check: ConditionCheck,
|
|
82
|
+
entry: AdvisoryEntryPoint | None,
|
|
77
83
|
) -> Finding:
|
|
78
84
|
return Finding(
|
|
79
85
|
rule_id="reachable-vulnerability",
|
|
@@ -85,5 +91,5 @@ def _reached(
|
|
|
85
91
|
confidence=Confidence.HIGH,
|
|
86
92
|
span=span,
|
|
87
93
|
function=function,
|
|
88
|
-
metadata=evidence(advisory, symbol, "reachable", check),
|
|
94
|
+
metadata=evidence(advisory, symbol, "reachable", check, entry),
|
|
89
95
|
)
|
|
@@ -7,7 +7,13 @@ from typing import ClassVar
|
|
|
7
7
|
|
|
8
8
|
from coretrace_python.analysis import AnyAnalysis
|
|
9
9
|
from coretrace_python.dependency import DependencyAnalysis
|
|
10
|
-
from coretrace_python.dependency.correlation import
|
|
10
|
+
from coretrace_python.dependency.correlation import (
|
|
11
|
+
affected_entries,
|
|
12
|
+
affected_symbols,
|
|
13
|
+
check_conditions,
|
|
14
|
+
ruled_out,
|
|
15
|
+
suffix_index,
|
|
16
|
+
)
|
|
11
17
|
from coretrace_python.findings import Confidence, Finding
|
|
12
18
|
from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
|
|
13
19
|
from coretrace_python.plugins import ProjectContext, ProjectPlugin
|
|
@@ -57,6 +63,7 @@ def _ruled_out(ctx: ProjectContext) -> dict[str, list[str]]:
|
|
|
57
63
|
conditions: the evidence that the requirement, imported, is not reached there."""
|
|
58
64
|
|
|
59
65
|
affected = affected_symbols(ctx.dependencies, ctx.advisories)
|
|
66
|
+
suffixes = suffix_index(affected, ctx.modules)
|
|
60
67
|
excluded: dict[str, list[str]] = {}
|
|
61
68
|
for module in sorted(ctx.modules):
|
|
62
69
|
graph = ctx.call_graph(module)
|
|
@@ -64,8 +71,8 @@ def _ruled_out(ctx: ProjectContext) -> dict[str, list[str]]:
|
|
|
64
71
|
for site in graph.sites(function):
|
|
65
72
|
if not isinstance(site.target, ExternalSymbol):
|
|
66
73
|
continue
|
|
67
|
-
for advisory in
|
|
68
|
-
check = check_conditions(
|
|
74
|
+
for advisory, entry in affected_entries(site.target.symbol, affected, suffixes):
|
|
75
|
+
check = check_conditions(entry, site.arguments, prior_calls=site.prior_calls)
|
|
69
76
|
if check.contradicted is not None:
|
|
70
77
|
excluded.setdefault(advisory.id, []).append(ruled_out(module, site, check))
|
|
71
78
|
return excluded
|
{coretrace_python_analyzer-0.20.0 → coretrace_python_analyzer-0.21.0}/src/coretrace_python/cache.py
RENAMED
|
@@ -34,6 +34,7 @@ from coretrace_python.interprocedural import (
|
|
|
34
34
|
ModuleGraph,
|
|
35
35
|
Mutation,
|
|
36
36
|
NonlocalWrite,
|
|
37
|
+
PriorCall,
|
|
37
38
|
SummaryIndex,
|
|
38
39
|
SymbolRead,
|
|
39
40
|
Target,
|
|
@@ -42,7 +43,7 @@ from coretrace_python.interprocedural import (
|
|
|
42
43
|
from coretrace_python.semantic.symbols import SymbolId
|
|
43
44
|
from coretrace_python.source import SourceId, SourceSpan
|
|
44
45
|
|
|
45
|
-
CACHE_FORMAT =
|
|
46
|
+
CACHE_FORMAT = 13
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
@dataclass(frozen=True)
|
|
@@ -351,18 +352,33 @@ def _encode_site(site: CallSite) -> dict[str, Any]:
|
|
|
351
352
|
"location": _encode_span(site.location),
|
|
352
353
|
"target": _encode_target(site.target),
|
|
353
354
|
"arguments": _encode_arguments(site.arguments),
|
|
355
|
+
"prior": None
|
|
356
|
+
if site.prior_calls is None
|
|
357
|
+
else [
|
|
358
|
+
[str(p.symbol), _encode_span(p.location), _encode_arguments(p.arguments), p.dominates]
|
|
359
|
+
for p in site.prior_calls
|
|
360
|
+
],
|
|
354
361
|
}
|
|
355
362
|
|
|
356
363
|
|
|
357
364
|
def _decode_site(data: Mapping[str, Any]) -> CallSite:
|
|
365
|
+
prior = data["prior"]
|
|
358
366
|
return CallSite(
|
|
359
367
|
_string(data["caller"]),
|
|
360
368
|
_decode_span(data["location"]),
|
|
361
369
|
_decode_target(data["target"]),
|
|
362
370
|
_decode_arguments(data["arguments"]),
|
|
371
|
+
None if prior is None else tuple(_decode_prior(entry) for entry in prior),
|
|
363
372
|
)
|
|
364
373
|
|
|
365
374
|
|
|
375
|
+
def _decode_prior(data: Any) -> PriorCall:
|
|
376
|
+
symbol, span, arguments, dominates = data
|
|
377
|
+
if not isinstance(dominates, bool):
|
|
378
|
+
raise TypeError(f"expected a boolean, got {dominates!r}")
|
|
379
|
+
return PriorCall(SymbolId(_string(symbol)), _decode_span(span), _decode_arguments(arguments), dominates)
|
|
380
|
+
|
|
381
|
+
|
|
366
382
|
def _encode_arguments(arguments: Arguments) -> dict[str, Any]:
|
|
367
383
|
return {
|
|
368
384
|
"positional": list(arguments.positional),
|
|
@@ -6,7 +6,7 @@ events into a version specifier; ``dump_advisories`` writes them as a small JSON
|
|
|
6
6
|
that a project keeps at its root as ``advisories.json`` or passes with ``--advisories``.
|
|
7
7
|
OSV records name no affected APIs, so imported advisories feed the requirement checks
|
|
8
8
|
and the SBOM; a file completed by hand with ``affected_symbols``, ``entry_points`` and
|
|
9
|
-
their ``conditions`` also feeds the reachability and correlation checks.
|
|
9
|
+
their ``conditions`` and ``suffixes`` also feeds the reachability and correlation checks.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
@@ -161,6 +161,8 @@ def _entry_point_entry(entry_point: AdvisoryEntryPoint) -> dict[str, Any]:
|
|
|
161
161
|
}
|
|
162
162
|
if entry_point.read:
|
|
163
163
|
entry["read"] = True
|
|
164
|
+
if entry_point.suffixes:
|
|
165
|
+
entry["suffixes"] = list(entry_point.suffixes)
|
|
164
166
|
if entry_point.attacker_arguments:
|
|
165
167
|
entry["attacker_arguments"] = [_attacker_argument_entry(a) for a in entry_point.attacker_arguments]
|
|
166
168
|
return entry
|
|
@@ -187,6 +189,10 @@ def _condition_entry(condition: Condition) -> dict[str, Any]:
|
|
|
187
189
|
entry["position"] = condition.position
|
|
188
190
|
if condition.default:
|
|
189
191
|
entry["default"] = True
|
|
192
|
+
if condition.present:
|
|
193
|
+
entry["present"] = True
|
|
194
|
+
if condition.method is not None:
|
|
195
|
+
entry["method"] = condition.method
|
|
190
196
|
return entry
|
|
191
197
|
|
|
192
198
|
|
|
@@ -226,9 +232,20 @@ def _entry_point(entry: Mapping[str, Any]) -> AdvisoryEntryPoint:
|
|
|
226
232
|
tuple(_condition(c) for c in entry.get("conditions") or []),
|
|
227
233
|
read,
|
|
228
234
|
tuple(_attacker_argument(a) for a in entry.get("attacker_arguments") or []),
|
|
235
|
+
tuple(_suffix(s) for s in entry.get("suffixes") or []),
|
|
229
236
|
)
|
|
230
237
|
|
|
231
238
|
|
|
239
|
+
def _suffix(value: Any) -> str:
|
|
240
|
+
if not isinstance(value, str):
|
|
241
|
+
raise TypeError(f"an entry point suffix must be a string, got {value!r}")
|
|
242
|
+
parts = value.split(".")
|
|
243
|
+
# A single name (``annotate``) would match every project method of that name.
|
|
244
|
+
if len(parts) < 2 or not all(p.isidentifier() for p in parts):
|
|
245
|
+
raise ValueError(f"an entry point suffix is at least two dot-separated names, got {value!r}")
|
|
246
|
+
return value
|
|
247
|
+
|
|
248
|
+
|
|
232
249
|
def _attacker_argument(entry: Mapping[str, Any]) -> AttackerArgument:
|
|
233
250
|
argument, position, variadic = entry.get("argument"), entry.get("position"), entry.get("variadic", False)
|
|
234
251
|
if argument is not None and not isinstance(argument, str):
|
|
@@ -251,6 +268,9 @@ def _condition(entry: Mapping[str, Any]) -> Condition:
|
|
|
251
268
|
default = entry.get("default", False)
|
|
252
269
|
if not isinstance(default, bool):
|
|
253
270
|
raise TypeError(f"condition default must be true or false, got {default!r}")
|
|
271
|
+
present = entry.get("present", False)
|
|
272
|
+
if not isinstance(present, bool):
|
|
273
|
+
raise TypeError(f"condition present must be true or false, got {present!r}")
|
|
254
274
|
condition = Condition(
|
|
255
275
|
str(entry["kind"]),
|
|
256
276
|
str(entry["text"]),
|
|
@@ -258,7 +278,22 @@ def _condition(entry: Mapping[str, Any]) -> Condition:
|
|
|
258
278
|
tuple(str(v) for v in entry.get("values") or []),
|
|
259
279
|
position,
|
|
260
280
|
default,
|
|
281
|
+
present,
|
|
282
|
+
None if entry.get("method") is None else str(entry["method"]),
|
|
261
283
|
)
|
|
284
|
+
if condition.present and (
|
|
285
|
+
condition.kind != "argument"
|
|
286
|
+
or (condition.argument is None and condition.position is None)
|
|
287
|
+
or condition.values
|
|
288
|
+
or condition.default
|
|
289
|
+
):
|
|
290
|
+
raise ValueError("a present condition names the argument that must be passed, by keyword or position, and nothing else")
|
|
291
|
+
if condition.kind == "sequence" and not condition.method:
|
|
292
|
+
raise ValueError("a sequence condition names the method of the prior call")
|
|
293
|
+
if condition.kind == "sequence" and condition.values and condition.argument is None:
|
|
294
|
+
raise ValueError("the values of a sequence condition need the argument they constrain")
|
|
295
|
+
if condition.kind != "sequence" and condition.method is not None:
|
|
296
|
+
raise ValueError(f"only a sequence condition names a method, not {condition.kind!r}")
|
|
262
297
|
if condition.kind == "host" and (
|
|
263
298
|
(condition.argument is None and condition.position is None) or condition.values or condition.default
|
|
264
299
|
):
|