coretrace-python-analyzer 0.4.0__tar.gz → 0.6.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.4.0 → coretrace_python_analyzer-0.6.0}/CHANGELOG.md +34 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/docs/plugins.md +11 -4
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/docs/usage.md +55 -12
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/__init__.py +1 -1
- coretrace_python_analyzer-0.6.0/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +82 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +32 -7
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +25 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cache.py +50 -11
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cli.py +33 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dependency/__init__.py +2 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dependency/advisories.py +38 -17
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dependency/correlation.py +69 -13
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dependency/graph.py +83 -11
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dependency/policy.py +6 -2
- coretrace_python_analyzer-0.6.0/src/coretrace_python/dependency/vex.py +182 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/engine.py +26 -6
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/interprocedural/__init__.py +6 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/interprocedural/callgraph.py +112 -11
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/interprocedural/summaries.py +8 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/plugins/api.py +18 -4
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/taint/__init__.py +4 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/taint/engine.py +91 -16
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/taint/models.py +24 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_advisory_evidence.py +8 -1
- coretrace_python_analyzer-0.6.0/tests/test_argument_conditions.py +217 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_cache.py +18 -4
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_dependencies.py +81 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_docs.py +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_heap.py +1 -1
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_project.py +99 -0
- coretrace_python_analyzer-0.6.0/tests/test_read_entry_points.py +123 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_release.py +1 -1
- coretrace_python_analyzer-0.6.0/tests/test_safe_arguments.py +130 -0
- coretrace_python_analyzer-0.6.0/tests/test_vex.py +284 -0
- coretrace_python_analyzer-0.4.0/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -46
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/README.md +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/findings/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/findings/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/findings/refutation.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/frontend/parser.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/ir/defuse.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/ir/lowering.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/ir/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/ir/printer.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/plugins/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/plugins/detectors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/plugins/secrets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/reporters/report.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/reporters/sarif.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/semantic/scopes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/semantic/symbols.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/source/__init__.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/source/manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/source/model.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/luigi.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/pygoat.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_detectors.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_reporters.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/tests/test_tornado_bottle_models.py +0 -0
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 (2026-09-24)
|
|
4
|
+
|
|
5
|
+
### Reports
|
|
6
|
+
|
|
7
|
+
- `--vex PATH` writes an OpenVEX document with one statement per advisory affecting a requirement. A statement is `affected` when the project's code reaches the vulnerability, including findings the policy accepts or a comment suppresses. It is `not_affected` (`vulnerable_code_not_in_execute_path`) only for an advisory with entry points that nothing reaches, in a complete analysis, and when the lock file shows no other package requiring the vulnerable one. Anything else is `under_investigation`, with the reason (#97).
|
|
8
|
+
|
|
9
|
+
### Dependencies
|
|
10
|
+
|
|
11
|
+
- An advisory entry point marked `read` is an attribute whose getter runs the affected code, such as `request.form`: reading it is reachable, called or not — a subscript, an iteration or a method called on it reads it — with one finding per function, where it is first read (#111).
|
|
12
|
+
- A lock file records which packages require which. `DependencyGraph.required_by(name)` gives the other packages requiring a package; the project's own packages are not counted. `ProjectAnalysis.accepted` keeps the findings of advisories the policy accepts (#97).
|
|
13
|
+
|
|
14
|
+
### Detection
|
|
15
|
+
|
|
16
|
+
- `yaml.load_all`, `yaml.full_load_all` and `yaml.unsafe_load_all` are deserialization sinks like their single-document versions; `yaml.load_all` with a safe loader is not, and `yaml.safe_load_all` stays safe (#123).
|
|
17
|
+
|
|
18
|
+
### Precision
|
|
19
|
+
|
|
20
|
+
- A sink call can be made safe by one of its arguments: a `SafeArgument` model names the argument and the values that take kinds off the sink, only when the call gives one explicitly. `yaml.load` with `SafeLoader`, `BaseLoader` or their C versions, under every spelling, is no longer an insecure deserialization; an absent loader, another loader, a variable or unpacked arguments still are (#121).
|
|
21
|
+
|
|
22
|
+
### Plugins
|
|
23
|
+
|
|
24
|
+
- The call graph records the symbols each function reads, called or not, where it first reads each: `CallGraph.reads(function)` gives them as `SymbolRead` records, modules served from the cache keep them, and the cache format is bumped (#111).
|
|
25
|
+
|
|
26
|
+
## 0.5.0 (2026-09-24)
|
|
27
|
+
|
|
28
|
+
### Dependencies
|
|
29
|
+
|
|
30
|
+
- Argument conditions of advisory entry points are decided at each call, with a `position` for positional arguments and a `default` for absent ones: a call meeting them is reported with `conditions_met`, a call passing another known value does not reach the vulnerability and is listed in the requirement's `ruled_out`, and a call the analyzer cannot decide leaves them pending review (#110).
|
|
31
|
+
|
|
32
|
+
### Plugins
|
|
33
|
+
|
|
34
|
+
- `ProjectContext.functions(module)` gives project plugins every function of a module as its call graph names it, with its span and the label of the entry point it is (`http` for a route, `argv` for a command), decided by the taint engine's own rule; modules served from the cache keep them, and the cache format is bumped (#116).
|
|
35
|
+
- Call sites and external calls record what each argument denotes — a symbol, a constant as Python writes it, or nothing known — in an `Arguments` record: `CallSite.arguments` (formerly the argument counts), `ExternalCall.arguments`, and `TaintFlow.sink_arguments` for the sink call of a flow, through callees in other modules too (#110).
|
|
36
|
+
|
|
3
37
|
## 0.4.0 (2026-09-24)
|
|
4
38
|
|
|
5
39
|
### Analysis
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -148,6 +148,7 @@ class BottleModels(ModelPlugin):
|
|
|
148
148
|
| `NamedParameter(pattern, label, kinds)` | A regular expression on parameter names; matching parameters carry `kinds`, as `password` carries `CREDENTIAL`. |
|
|
149
149
|
| `RouteRegistrar(symbol, argument, label, kinds=ALL, keyword=None)` | A call registering a view at argument `argument` (or `keyword`), such as Django's `path`; the view receives `label` input. |
|
|
150
150
|
| `SuffixSink(suffix, kinds, positions=())` | A sink matched by the end of the symbol, for methods of any class such as `objects.raw`. |
|
|
151
|
+
| `SafeArgument(symbol, argument, values, position=None, kinds=ALL)` | A call to the sink `symbol` whose `argument` (by keyword, or at `position`) denotes one of `values` is not a sink for `kinds`, such as `yaml.load` with `Loader=yaml.SafeLoader`. Values are written as the analyzer records arguments: a symbol by its canonical name, a constant as Python writes it (`True`). Only a value given explicitly makes the call safe. |
|
|
151
152
|
| `Validator(symbol, kinds=ALL, argument=0)` | A callable whose truth proves its argument safe, such as `re.fullmatch`; a flow guarded by it is refuted. |
|
|
152
153
|
| `AuthorizationGuard(symbol, label)` | A decorator or a condition restricting who reaches the code, such as `login_required`; a flow behind it is a hotspot. |
|
|
153
154
|
|
|
@@ -222,9 +223,15 @@ rule. Findings carry a redacted preview only.
|
|
|
222
223
|
|
|
223
224
|
Runs once per project after every module is analysed, with a `ProjectContext`: the
|
|
224
225
|
module graph, the dependency graph, every advisory the plugins and advisory files
|
|
225
|
-
contributed, the policy, the project root, and each module's imports
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
contributed, the policy, the project root, and each module's imports, call graph and
|
|
227
|
+
functions. `ctx.functions(module)` gives every function as the call graph names it
|
|
228
|
+
(`Admin.get`, `outer.inner`, `<module>`), with its span, so a finding can be placed in
|
|
229
|
+
one, and the label of the entry point it is (`http` for a route, `argv` for a command)
|
|
230
|
+
when the models make it one. `ctx.call_graph(module)` gives each function's call sites
|
|
231
|
+
(`sites(function)`) and the symbols it reads, called or not (`reads(function)`: reading
|
|
232
|
+
`request.form['name']` reads `python.flask.request.form`), each where it first reads it.
|
|
233
|
+
The shipped `vulnerable-dependency`, `reachable-vulnerability` and `dependency-policy`
|
|
234
|
+
plugins are of this kind.
|
|
228
235
|
|
|
229
236
|
```python
|
|
230
237
|
from collections.abc import Sequence
|
|
@@ -238,7 +245,7 @@ class RequirementsPresent(ProjectPlugin):
|
|
|
238
245
|
name: ClassVar[str] = "requirements-present"
|
|
239
246
|
|
|
240
247
|
def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
|
|
241
|
-
return () # inspect ctx.dependencies, ctx.modules(), ctx.
|
|
248
|
+
return () # inspect ctx.dependencies, ctx.modules, ctx.call_graph(module), ctx.functions(module)
|
|
242
249
|
```
|
|
243
250
|
|
|
244
251
|
### Anything else: `Plugin`
|
|
@@ -50,6 +50,7 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
50
50
|
| `--cache DIR` | Keep per-module results under `DIR` and reuse them for unchanged modules. |
|
|
51
51
|
| `--jobs N` | Analyse independent modules in `N` processes. |
|
|
52
52
|
| `--sbom PATH` | Write a CycloneDX bill of materials of the dependencies to `PATH`. |
|
|
53
|
+
| `--vex PATH` | Write an OpenVEX document to `PATH`: whether each advisory affecting a requirement affects the project. |
|
|
53
54
|
| `--advisories FILE` | Read a local advisory file in addition to `advisories.json` at the root. Repeatable. |
|
|
54
55
|
| `--policy FILE` | Apply this dependency policy instead of `coretrace-policy.toml` at the root. |
|
|
55
56
|
| `--import-advisories SRC OUT` | Convert an OSV dump into the local advisory file `OUT`, then exit. |
|
|
@@ -59,7 +60,7 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
59
60
|
| `--ssa` | With `--emit-ir`, print the static single assignment form. |
|
|
60
61
|
| `--help` | Show the options and exit. |
|
|
61
62
|
|
|
62
|
-
`--cache`, `--jobs`, `--sbom`, `--advisories` and `--policy` apply to a directory check.
|
|
63
|
+
`--cache`, `--jobs`, `--sbom`, `--vex`, `--advisories` and `--policy` apply to a directory check.
|
|
63
64
|
|
|
64
65
|
## What is analysed
|
|
65
66
|
|
|
@@ -109,7 +110,7 @@ a sanitizer for that kind of sink.
|
|
|
109
110
|
| `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
|
|
110
111
|
| `ssrf` | The URL of an HTTP client request (Requests, httpx, `urllib`). |
|
|
111
112
|
| `xss` | An HTTP response body built without escaping. |
|
|
112
|
-
| `insecure-deserialization` | `pickle`, `yaml.load` and
|
|
113
|
+
| `insecure-deserialization` | `pickle`, `marshal`, `dill`, `jsonpickle`, `shelve`, and the YAML loaders (`yaml.load`, `load_all`, `full_load`, `unsafe_load` and their several-document versions); `yaml.load` and `load_all` given a safe loader (`SafeLoader`, `BaseLoader`) are not. |
|
|
113
114
|
| `open-redirect` | An HTTP redirect target. |
|
|
114
115
|
| `plaintext-credential-storage` | A parameter named like a password stored in a database without hashing. Medium confidence, since a name is a hint. |
|
|
115
116
|
| `exploitable-vulnerability` | An API affected by an advisory of a vulnerable requirement, see Dependencies. |
|
|
@@ -151,7 +152,7 @@ sources and in configuration files.
|
|
|
151
152
|
| Rule | Trigger |
|
|
152
153
|
|---|---|
|
|
153
154
|
| `vulnerable-dependency` | A requirement allows a version affected by an advisory, reported at its line. |
|
|
154
|
-
| `reachable-vulnerability` | A call in the project to an API the advisory affects
|
|
155
|
+
| `reachable-vulnerability` | A call in the project to an API the advisory affects, or a read of an entry point marked `read`. |
|
|
155
156
|
| `exploitable-vulnerability` | Attacker-controlled data reaches such a call. Critical. |
|
|
156
157
|
| `denied-dependency` | A package the policy denies. |
|
|
157
158
|
| `unpinned-dependency` | A requirement without an exact pin when the policy requires pins. |
|
|
@@ -201,7 +202,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
201
202
|
```json
|
|
202
203
|
{
|
|
203
204
|
"schema_version": 1,
|
|
204
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
205
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.6.0"},
|
|
205
206
|
"root": "/home/me/project",
|
|
206
207
|
"findings": [
|
|
207
208
|
{
|
|
@@ -271,7 +272,8 @@ affected APIs that feed reachability and correlation:
|
|
|
271
272
|
"symbol": "python.yaml.load",
|
|
272
273
|
"justification": "same path when Loader is FullLoader",
|
|
273
274
|
"conditions": [
|
|
274
|
-
{"kind": "argument", "text": "Loader is FullLoader", "argument": "Loader", "
|
|
275
|
+
{"kind": "argument", "text": "Loader is FullLoader, the default", "argument": "Loader", "position": 1,
|
|
276
|
+
"values": ["python.yaml.FullLoader", "python.yaml.loader.FullLoader"], "default": true},
|
|
275
277
|
{"kind": "semantic", "text": "the document carries a python/object/apply tag"}
|
|
276
278
|
]
|
|
277
279
|
}
|
|
@@ -285,17 +287,40 @@ affected APIs that feed reachability and correlation:
|
|
|
285
287
|
|
|
286
288
|
`affected_symbols` are the functions the fix changed; `entry_points` the public APIs
|
|
287
289
|
through which a project reaches them, each with the justification that ties it to the
|
|
288
|
-
fix — the commit, or the call path — and the `conditions` under which it is affected
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
fix — the commit, or the call path — and the `conditions` under which it is affected. A
|
|
291
|
+
call to either kind of symbol is reachable. `modules` names the top-level modules the
|
|
292
|
+
package installs, so the analyzer can tell an imported package from a merely required
|
|
293
|
+
one; it defaults to the package name.
|
|
294
|
+
|
|
295
|
+
An entry point marked `"read": true` is an attribute whose getter runs the affected code,
|
|
296
|
+
such as a request body parsed on first access
|
|
297
|
+
(`{"symbol": "python.flask.request.form", "justification": "…", "read": true}`): reading
|
|
298
|
+
it is reachable, called or not — `request.form['name']`, `for key in request.form`,
|
|
299
|
+
`request.form.get('name')` all read it. A function reading it several times is reported
|
|
300
|
+
once, where it first reads it.
|
|
301
|
+
|
|
302
|
+
An `argument` condition is decided at each call. It names the argument by keyword and,
|
|
303
|
+
when it may be passed positionally, by `position` (counted from 0, the receiver of a
|
|
304
|
+
method excluded); `values` are what makes the call affected, written as the analyzer
|
|
305
|
+
sees arguments: a symbol by its canonical name (`python.yaml.FullLoader`, and every
|
|
306
|
+
spelling a project may use, such as `python.yaml.loader.FullLoader`), a constant as
|
|
307
|
+
Python writes it (`True`, `None`, `'/static'`); `default` says an absent argument means
|
|
308
|
+
an affected value. A call passing another known value does not reach the vulnerability;
|
|
309
|
+
a call passing a variable, or unpacking `*args` or `**kwargs`, leaves the condition
|
|
310
|
+
pending review. A `semantic` condition — the document's tags, the platform, the
|
|
311
|
+
template's origin — cannot be decided and is always pending review, never dropped.
|
|
293
312
|
|
|
294
313
|
Every dependency finding records the highest level of evidence established in its
|
|
295
314
|
`level` metadata: `declared` (the requirement allows a vulnerable version), `imported`
|
|
296
315
|
(a module of the package is imported somewhere), `reachable` (an entry point or affected
|
|
297
|
-
symbol is called
|
|
298
|
-
|
|
316
|
+
symbol is called, or an entry point marked `read` is read) or `exploitable` (attacker
|
|
317
|
+
input reaches it). The last two carry
|
|
318
|
+
`entry_point`, `justification`, `conditions`, and which of them the call meets
|
|
319
|
+
(`conditions_met`) or leaves to review (`conditions_pending_review`). A requirement whose
|
|
320
|
+
entry points are only called with arguments that contradict a condition stays
|
|
321
|
+
`imported`, and its finding lists those calls in `ruled_out`
|
|
322
|
+
(`app.views:12 python.yaml.load(Loader=python.yaml.SafeLoader)`), the evidence that
|
|
323
|
+
they do not reach the vulnerability.
|
|
299
324
|
|
|
300
325
|
A `coretrace-policy.toml` at the root, or the file passed with `--policy`, denies
|
|
301
326
|
packages, requires pins and lists accepted advisories whose findings are dropped:
|
|
@@ -316,6 +341,24 @@ with its package URL, and the advisories affecting them as vulnerabilities.
|
|
|
316
341
|
coretrace-python-analyzer --check src/ --sbom sbom.json --policy security/policy.toml
|
|
317
342
|
```
|
|
318
343
|
|
|
344
|
+
`--vex PATH` writes an OpenVEX 0.2.0 document with one statement per advisory affecting
|
|
345
|
+
a requirement. In each statement, the required package is a subcomponent of the project,
|
|
346
|
+
identified as `pkg:generic/<directory>`. The status follows from the evidence of the
|
|
347
|
+
check, including findings the policy accepts or a comment suppresses:
|
|
348
|
+
|
|
349
|
+
| Status | When |
|
|
350
|
+
|---|---|
|
|
351
|
+
| `affected` | The project's code reaches the vulnerable code. The notes give each place and its level, `reachable` or `exploitable`. |
|
|
352
|
+
| `not_affected` | Justified as `vulnerable_code_not_in_execute_path`, only when all of these hold: the advisory names entry points, no code of the project reaches them, every file and function was analysed, and a lock file (`uv.lock` or `poetry.lock`) shows that no other package requires the vulnerable one. The impact statement names the entry points and any calls ruled out by their arguments. |
|
|
353
|
+
| `under_investigation` | Anything else. The notes say which condition failed. |
|
|
354
|
+
|
|
355
|
+
`not_affected` needs the lock-file check because the analyzer reads the project's code,
|
|
356
|
+
not the code of installed packages. A framework calling the vulnerable function on the
|
|
357
|
+
project's behalf would go unseen. Without a lock file, a package is never
|
|
358
|
+
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
359
|
+
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
360
|
+
cannot know it.
|
|
361
|
+
|
|
319
362
|
## Large projects
|
|
320
363
|
|
|
321
364
|
`--cache DIR` keeps the results of a directory check on disk, one entry per module,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Calls, anywhere in the project, to an API affected by a vulnerable requirement, and
|
|
2
|
+
reads of an affected attribute whose getter runs the vulnerable code."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Iterator, Sequence
|
|
7
|
+
from typing import ClassVar
|
|
8
|
+
|
|
9
|
+
from coretrace_python.analysis import AnyAnalysis
|
|
10
|
+
from coretrace_python.dependency import Advisory, DependencyAnalysis
|
|
11
|
+
from coretrace_python.dependency.correlation import (
|
|
12
|
+
ConditionCheck,
|
|
13
|
+
affected_symbols,
|
|
14
|
+
check_conditions,
|
|
15
|
+
evidence,
|
|
16
|
+
)
|
|
17
|
+
from coretrace_python.findings import Confidence, Finding
|
|
18
|
+
from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
|
|
19
|
+
from coretrace_python.plugins import ProjectContext, ProjectPlugin
|
|
20
|
+
from coretrace_python.semantic.symbols import SymbolId
|
|
21
|
+
from coretrace_python.source import SourceSpan
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ReachableVulnerabilityPlugin(ProjectPlugin):
|
|
25
|
+
name: ClassVar[str] = "reachable-vulnerability"
|
|
26
|
+
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis, CallGraphAnalysis})
|
|
27
|
+
|
|
28
|
+
def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
|
|
29
|
+
affected = affected_symbols(ctx.dependencies, ctx.advisories)
|
|
30
|
+
if not affected:
|
|
31
|
+
return ()
|
|
32
|
+
findings: list[Finding] = []
|
|
33
|
+
for module in ctx.modules:
|
|
34
|
+
graph = ctx.call_graph(module)
|
|
35
|
+
for function in graph.functions:
|
|
36
|
+
for site in graph.sites(function):
|
|
37
|
+
if not isinstance(site.target, ExternalSymbol):
|
|
38
|
+
continue
|
|
39
|
+
for advisory in affected.get(site.target.symbol, ()):
|
|
40
|
+
entry = advisory.entry_point(site.target.symbol)
|
|
41
|
+
if entry is not None and entry.read:
|
|
42
|
+
continue # a call reads its callee first: reported with the reads
|
|
43
|
+
check = check_conditions(entry, site.arguments)
|
|
44
|
+
if check.contradicted is not None:
|
|
45
|
+
continue
|
|
46
|
+
findings.append(_reached(advisory, site.target.symbol, site.location, function, check))
|
|
47
|
+
# Reading ``request.form.get`` reads ``request.form``: one finding per entry
|
|
48
|
+
# point and function, where the function first reads it.
|
|
49
|
+
reported: set[tuple[Advisory, SymbolId]] = set()
|
|
50
|
+
for read in graph.reads(function):
|
|
51
|
+
for symbol in _read_through(read.symbol):
|
|
52
|
+
for advisory in affected.get(symbol, ()):
|
|
53
|
+
entry = advisory.entry_point(symbol)
|
|
54
|
+
if entry is None or not entry.read or (advisory, symbol) in reported:
|
|
55
|
+
continue
|
|
56
|
+
reported.add((advisory, symbol))
|
|
57
|
+
check = check_conditions(entry, None)
|
|
58
|
+
findings.append(_reached(advisory, symbol, read.location, function, check))
|
|
59
|
+
return findings
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _read_through(symbol: SymbolId) -> Iterator[SymbolId]:
|
|
63
|
+
"""``symbol`` and every symbol it is an attribute of, as reading it reads them all."""
|
|
64
|
+
|
|
65
|
+
components = symbol.canonical_name.split(".")
|
|
66
|
+
for end in range(len(components), 1, -1):
|
|
67
|
+
yield SymbolId(".".join(components[:end]))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _reached(advisory: Advisory, symbol: SymbolId, span: SourceSpan, function: str, check: ConditionCheck) -> Finding:
|
|
71
|
+
return Finding(
|
|
72
|
+
rule_id="reachable-vulnerability",
|
|
73
|
+
message=(
|
|
74
|
+
f"{advisory.id}: {symbol} is affected in the required "
|
|
75
|
+
f"{advisory.package} {advisory.vulnerable}: {advisory.summary}"
|
|
76
|
+
),
|
|
77
|
+
severity=advisory.severity,
|
|
78
|
+
confidence=Confidence.HIGH,
|
|
79
|
+
span=span,
|
|
80
|
+
function=function,
|
|
81
|
+
metadata=evidence(advisory, symbol, "reachable", check),
|
|
82
|
+
)
|
|
@@ -7,23 +7,34 @@ 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 affected_symbols, check_conditions, ruled_out
|
|
10
11
|
from coretrace_python.findings import Confidence, Finding
|
|
12
|
+
from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
|
|
11
13
|
from coretrace_python.plugins import ProjectContext, ProjectPlugin
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class VulnerableDependencyPlugin(ProjectPlugin):
|
|
15
17
|
name: ClassVar[str] = "vulnerable-dependency"
|
|
16
|
-
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis})
|
|
18
|
+
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis, CallGraphAnalysis})
|
|
17
19
|
|
|
18
20
|
def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
|
|
19
21
|
findings: list[Finding] = []
|
|
20
22
|
imported = [s for module in ctx.modules for s in ctx.imports(module).all_symbols()]
|
|
23
|
+
excluded = _ruled_out(ctx)
|
|
21
24
|
for requirement in ctx.dependencies.requirements:
|
|
22
25
|
for advisory in ctx.advisories:
|
|
23
26
|
if not advisory.affects(requirement):
|
|
24
27
|
continue
|
|
25
28
|
pinned = requirement.pinned is not None
|
|
26
29
|
level = "imported" if advisory.imported_by(imported) else "declared"
|
|
30
|
+
metadata = {
|
|
31
|
+
"advisory": advisory.id,
|
|
32
|
+
"package": advisory.package,
|
|
33
|
+
"specifier": requirement.specifier,
|
|
34
|
+
"level": level,
|
|
35
|
+
}
|
|
36
|
+
if excluded.get(advisory.id):
|
|
37
|
+
metadata["ruled_out"] = "; ".join(excluded[advisory.id])
|
|
27
38
|
findings.append(
|
|
28
39
|
Finding(
|
|
29
40
|
rule_id="vulnerable-dependency",
|
|
@@ -35,12 +46,26 @@ class VulnerableDependencyPlugin(ProjectPlugin):
|
|
|
35
46
|
severity=advisory.severity,
|
|
36
47
|
confidence=Confidence.HIGH if pinned else Confidence.MEDIUM,
|
|
37
48
|
span=requirement.span,
|
|
38
|
-
metadata=
|
|
39
|
-
"advisory": advisory.id,
|
|
40
|
-
"package": advisory.package,
|
|
41
|
-
"specifier": requirement.specifier,
|
|
42
|
-
"level": level,
|
|
43
|
-
},
|
|
49
|
+
metadata=metadata,
|
|
44
50
|
)
|
|
45
51
|
)
|
|
46
52
|
return findings
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _ruled_out(ctx: ProjectContext) -> dict[str, list[str]]:
|
|
56
|
+
"""By advisory, the calls to its entry points whose arguments contradict one of its
|
|
57
|
+
conditions: the evidence that the requirement, imported, is not reached there."""
|
|
58
|
+
|
|
59
|
+
affected = affected_symbols(ctx.dependencies, ctx.advisories)
|
|
60
|
+
excluded: dict[str, list[str]] = {}
|
|
61
|
+
for module in sorted(ctx.modules):
|
|
62
|
+
graph = ctx.call_graph(module)
|
|
63
|
+
for function in graph.functions:
|
|
64
|
+
for site in graph.sites(function):
|
|
65
|
+
if not isinstance(site.target, ExternalSymbol):
|
|
66
|
+
continue
|
|
67
|
+
for advisory in affected.get(site.target.symbol, ()):
|
|
68
|
+
check = check_conditions(advisory.entry_point(site.target.symbol), site.arguments)
|
|
69
|
+
if check.contradicted is not None:
|
|
70
|
+
excluded.setdefault(advisory.id, []).append(ruled_out(module, site, check))
|
|
71
|
+
return excluded
|
|
@@ -6,9 +6,28 @@ from typing import ClassVar
|
|
|
6
6
|
|
|
7
7
|
from coretrace_python.plugins import ModelPlugin
|
|
8
8
|
from coretrace_python.semantic.symbols import SymbolId
|
|
9
|
-
from coretrace_python.taint import
|
|
9
|
+
from coretrace_python.taint import (
|
|
10
|
+
Model,
|
|
11
|
+
SafeArgument,
|
|
12
|
+
Sanitizer,
|
|
13
|
+
Sink,
|
|
14
|
+
Source,
|
|
15
|
+
TaintKind,
|
|
16
|
+
Validator,
|
|
17
|
+
)
|
|
10
18
|
|
|
11
19
|
_ENVIRONMENT_KINDS = TaintKind.ALL & ~(TaintKind.COMMAND | TaintKind.PATH)
|
|
20
|
+
# SafeLoader and BaseLoader build plain data only, in Python and in C, under every spelling.
|
|
21
|
+
_SAFE_YAML_LOADERS = (
|
|
22
|
+
"python.yaml.SafeLoader",
|
|
23
|
+
"python.yaml.loader.SafeLoader",
|
|
24
|
+
"python.yaml.BaseLoader",
|
|
25
|
+
"python.yaml.loader.BaseLoader",
|
|
26
|
+
"python.yaml.CSafeLoader",
|
|
27
|
+
"python.yaml.cyaml.CSafeLoader",
|
|
28
|
+
"python.yaml.CBaseLoader",
|
|
29
|
+
"python.yaml.cyaml.CBaseLoader",
|
|
30
|
+
)
|
|
12
31
|
_PROCESS_OUTPUT_KINDS = TaintKind.ALL & ~TaintKind.PATH
|
|
13
32
|
|
|
14
33
|
|
|
@@ -66,6 +85,11 @@ class PythonStdlibModels(ModelPlugin):
|
|
|
66
85
|
Sink(_sym("dill.loads"), TaintKind.DESERIALIZATION),
|
|
67
86
|
Sink(_sym("jsonpickle.decode"), TaintKind.DESERIALIZATION),
|
|
68
87
|
Sink(_sym("yaml.load"), TaintKind.DESERIALIZATION),
|
|
88
|
+
Sink(_sym("yaml.load_all"), TaintKind.DESERIALIZATION),
|
|
89
|
+
Sink(_sym("yaml.full_load_all"), TaintKind.DESERIALIZATION),
|
|
90
|
+
Sink(_sym("yaml.unsafe_load_all"), TaintKind.DESERIALIZATION),
|
|
91
|
+
SafeArgument(_sym("yaml.load"), "Loader", _SAFE_YAML_LOADERS, position=1, kinds=TaintKind.DESERIALIZATION),
|
|
92
|
+
SafeArgument(_sym("yaml.load_all"), "Loader", _SAFE_YAML_LOADERS, position=1, kinds=TaintKind.DESERIALIZATION),
|
|
69
93
|
Sink(_sym("yaml.unsafe_load"), TaintKind.DESERIALIZATION),
|
|
70
94
|
Sink(_sym("yaml.full_load"), TaintKind.DESERIALIZATION),
|
|
71
95
|
Sanitizer(_sym("os.path.basename"), TaintKind.PATH),
|
{coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cache.py
RENAMED
|
@@ -4,9 +4,10 @@ A module's results are stored under a key derived from everything they depend on
|
|
|
4
4
|
source text and identity, the engine, schema and plugin API versions, the plugins and
|
|
5
5
|
their code, the security models, the advisories, the dependency graph, and the keys of
|
|
6
6
|
the project modules it imports transitively. A module whose key is unchanged on a later
|
|
7
|
-
run is served from the cache: its summaries seed the project index, its call sites
|
|
8
|
-
the project plugins and its findings are reported as they were.
|
|
9
|
-
tampered or foreign file can never execute anything; an
|
|
7
|
+
run is served from the cache: its summaries seed the project index, its call sites and
|
|
8
|
+
symbol reads serve the project plugins and its findings are reported as they were.
|
|
9
|
+
Entries are JSON, so a tampered or foreign file can never execute anything; an
|
|
10
|
+
unreadable entry is a miss.
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from __future__ import annotations
|
|
@@ -22,32 +23,36 @@ from typing import Any
|
|
|
22
23
|
|
|
23
24
|
from coretrace_python.findings import Confidence, Finding, Severity
|
|
24
25
|
from coretrace_python.interprocedural import (
|
|
26
|
+
Arguments,
|
|
25
27
|
CallSite,
|
|
26
28
|
ExternalCall,
|
|
27
29
|
ExternalSymbol,
|
|
28
30
|
FunctionSummary,
|
|
29
31
|
KnownFunction,
|
|
32
|
+
ModuleFunction,
|
|
30
33
|
ModuleGraph,
|
|
31
34
|
Mutation,
|
|
32
35
|
NonlocalWrite,
|
|
33
36
|
SummaryIndex,
|
|
37
|
+
SymbolRead,
|
|
34
38
|
Target,
|
|
35
39
|
UnknownTarget,
|
|
36
40
|
)
|
|
37
41
|
from coretrace_python.semantic.symbols import SymbolId
|
|
38
42
|
from coretrace_python.source import SourceId, SourceSpan
|
|
39
43
|
|
|
40
|
-
CACHE_FORMAT =
|
|
44
|
+
CACHE_FORMAT = 8
|
|
41
45
|
|
|
42
46
|
|
|
43
47
|
@dataclass(frozen=True)
|
|
44
48
|
class CachedModule:
|
|
45
49
|
"""Everything a later run needs from one module without lowering it again."""
|
|
46
50
|
|
|
47
|
-
functions: tuple[
|
|
51
|
+
functions: tuple[ModuleFunction, ...]
|
|
48
52
|
summaries: Mapping[str, FunctionSummary]
|
|
49
53
|
sites: tuple[CallSite, ...]
|
|
50
54
|
findings: tuple[Finding, ...]
|
|
55
|
+
reads: tuple[SymbolRead, ...] = ()
|
|
51
56
|
|
|
52
57
|
def __post_init__(self) -> None:
|
|
53
58
|
object.__setattr__(self, "summaries", MappingProxyType(dict(self.summaries)))
|
|
@@ -128,10 +133,11 @@ class ProjectCache:
|
|
|
128
133
|
def encode(module: CachedModule) -> dict[str, Any]:
|
|
129
134
|
return {
|
|
130
135
|
"format": CACHE_FORMAT,
|
|
131
|
-
"functions":
|
|
136
|
+
"functions": [[f.name, _encode_span(f.span), f.entry_point] for f in module.functions],
|
|
132
137
|
"summaries": {name: _encode_summary(s) for name, s in module.summaries.items()},
|
|
133
138
|
"sites": [_encode_site(site) for site in module.sites],
|
|
134
139
|
"findings": [_encode_finding(finding) for finding in module.findings],
|
|
140
|
+
"reads": [[r.function, _encode_span(r.location), str(r.symbol)] for r in module.reads],
|
|
135
141
|
}
|
|
136
142
|
|
|
137
143
|
|
|
@@ -139,10 +145,11 @@ def decode(data: Mapping[str, Any]) -> CachedModule:
|
|
|
139
145
|
if data["format"] != CACHE_FORMAT:
|
|
140
146
|
raise ValueError(f"unsupported cache format {data['format']!r}")
|
|
141
147
|
return CachedModule(
|
|
142
|
-
tuple(
|
|
148
|
+
tuple(_decode_function(function) for function in data["functions"]),
|
|
143
149
|
{_string(name): _decode_summary(s) for name, s in data["summaries"].items()},
|
|
144
150
|
tuple(_decode_site(site) for site in data["sites"]),
|
|
145
151
|
tuple(_decode_finding(finding) for finding in data["findings"]),
|
|
152
|
+
tuple(_decode_read(read) for read in data["reads"]),
|
|
146
153
|
)
|
|
147
154
|
|
|
148
155
|
|
|
@@ -187,6 +194,16 @@ def _decode_span(data: Any) -> SourceSpan:
|
|
|
187
194
|
)
|
|
188
195
|
|
|
189
196
|
|
|
197
|
+
def _decode_function(data: Any) -> ModuleFunction:
|
|
198
|
+
name, span, entry_point = data
|
|
199
|
+
return ModuleFunction(_string(name), _decode_span(span), None if entry_point is None else _string(entry_point))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _decode_read(data: Any) -> SymbolRead:
|
|
203
|
+
function, span, symbol = data
|
|
204
|
+
return SymbolRead(_string(function), _decode_span(span), SymbolId(_string(symbol)))
|
|
205
|
+
|
|
206
|
+
|
|
190
207
|
def _encode_finding(finding: Finding) -> dict[str, Any]:
|
|
191
208
|
return {
|
|
192
209
|
"rule": finding.rule_id,
|
|
@@ -219,6 +236,7 @@ def _encode_call(call: ExternalCall) -> dict[str, Any]:
|
|
|
219
236
|
"keywords": sorted(call.keyword_dependencies),
|
|
220
237
|
"location": _encode_span(call.location),
|
|
221
238
|
"call_site": None if call.call_site is None else _encode_span(call.call_site),
|
|
239
|
+
"given": _encode_arguments(call.arguments),
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
|
|
@@ -230,6 +248,7 @@ def _decode_call(data: Mapping[str, Any]) -> ExternalCall:
|
|
|
230
248
|
_indices(data["keywords"]),
|
|
231
249
|
_decode_span(data["location"]),
|
|
232
250
|
None if site is None else _decode_span(site),
|
|
251
|
+
_decode_arguments(data["given"]),
|
|
233
252
|
)
|
|
234
253
|
|
|
235
254
|
|
|
@@ -311,8 +330,7 @@ def _encode_site(site: CallSite) -> dict[str, Any]:
|
|
|
311
330
|
"caller": site.caller,
|
|
312
331
|
"location": _encode_span(site.location),
|
|
313
332
|
"target": _encode_target(site.target),
|
|
314
|
-
"arguments": site.arguments,
|
|
315
|
-
"keywords": site.keywords,
|
|
333
|
+
"arguments": _encode_arguments(site.arguments),
|
|
316
334
|
}
|
|
317
335
|
|
|
318
336
|
|
|
@@ -321,6 +339,27 @@ def _decode_site(data: Mapping[str, Any]) -> CallSite:
|
|
|
321
339
|
_string(data["caller"]),
|
|
322
340
|
_decode_span(data["location"]),
|
|
323
341
|
_decode_target(data["target"]),
|
|
324
|
-
|
|
325
|
-
|
|
342
|
+
_decode_arguments(data["arguments"]),
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _encode_arguments(arguments: Arguments) -> dict[str, Any]:
|
|
347
|
+
return {
|
|
348
|
+
"positional": list(arguments.positional),
|
|
349
|
+
"keywords": [[name, value] for name, value in arguments.keywords],
|
|
350
|
+
"unpacked": arguments.unpacked,
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _decode_arguments(data: Mapping[str, Any]) -> Arguments:
|
|
355
|
+
def denoted(value: Any) -> str | None:
|
|
356
|
+
return None if value is None else _string(value)
|
|
357
|
+
|
|
358
|
+
unpacked = data["unpacked"]
|
|
359
|
+
if not isinstance(unpacked, bool):
|
|
360
|
+
raise TypeError(f"expected a boolean, got {unpacked!r}")
|
|
361
|
+
return Arguments(
|
|
362
|
+
tuple(denoted(value) for value in data["positional"]),
|
|
363
|
+
tuple((_string(name), denoted(value)) for name, value in data["keywords"]),
|
|
364
|
+
unpacked,
|
|
326
365
|
)
|
{coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.6.0}/src/coretrace_python/cli.py
RENAMED
|
@@ -3,13 +3,20 @@ from __future__ import annotations
|
|
|
3
3
|
import argparse
|
|
4
4
|
import sys
|
|
5
5
|
import zipfile
|
|
6
|
+
from datetime import UTC, datetime
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
from coretrace_python import __version__, engine
|
|
9
10
|
from coretrace_python.analysis import AnalysisError
|
|
10
11
|
from coretrace_python.cache import ProjectCache
|
|
11
12
|
from coretrace_python.cfg import CFGError
|
|
12
|
-
from coretrace_python.dependency import
|
|
13
|
+
from coretrace_python.dependency import (
|
|
14
|
+
dump_advisories,
|
|
15
|
+
import_osv,
|
|
16
|
+
read_osv,
|
|
17
|
+
render_sbom,
|
|
18
|
+
render_vex,
|
|
19
|
+
)
|
|
13
20
|
from coretrace_python.findings import Severity
|
|
14
21
|
from coretrace_python.findings.baseline import Baseline, BaselineError
|
|
15
22
|
from coretrace_python.frontend import HIRBuildError, ParseError, build_hir
|
|
@@ -106,6 +113,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
106
113
|
metavar="PATH",
|
|
107
114
|
help="with --check on a directory, write a CycloneDX bill of materials to PATH",
|
|
108
115
|
)
|
|
116
|
+
parser.add_argument(
|
|
117
|
+
"--vex",
|
|
118
|
+
type=Path,
|
|
119
|
+
default=None,
|
|
120
|
+
metavar="PATH",
|
|
121
|
+
help="with --check on a directory, write to PATH an OpenVEX document saying whether "
|
|
122
|
+
"each advisory affecting a requirement affects the project",
|
|
123
|
+
)
|
|
109
124
|
parser.add_argument(
|
|
110
125
|
"--advisories",
|
|
111
126
|
action="append",
|
|
@@ -182,6 +197,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
182
197
|
if args.sbom is not None and not (args.check and args.path.is_dir()):
|
|
183
198
|
print("error: --sbom only applies to --check on a directory", file=sys.stderr)
|
|
184
199
|
return EXIT_ERROR
|
|
200
|
+
if args.vex is not None and not (args.check and args.path.is_dir()):
|
|
201
|
+
print("error: --vex only applies to --check on a directory", file=sys.stderr)
|
|
202
|
+
return EXIT_ERROR
|
|
185
203
|
if args.advisories and not (args.check and args.path.is_dir()):
|
|
186
204
|
print("error: --advisories only applies to --check on a directory", file=sys.stderr)
|
|
187
205
|
return EXIT_ERROR
|
|
@@ -227,6 +245,20 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
227
245
|
render_sbom(analysis.dependencies, analysis.advisories, engine.TOOL_NAME, __version__),
|
|
228
246
|
encoding="utf-8",
|
|
229
247
|
)
|
|
248
|
+
if args.vex is not None:
|
|
249
|
+
args.vex.write_text(
|
|
250
|
+
render_vex(
|
|
251
|
+
analysis.dependencies,
|
|
252
|
+
analysis.advisories,
|
|
253
|
+
(*analysis.findings, *analysis.suppressed, *analysis.accepted),
|
|
254
|
+
analysis.coverage,
|
|
255
|
+
args.path,
|
|
256
|
+
engine.TOOL_NAME,
|
|
257
|
+
__version__,
|
|
258
|
+
datetime.now(UTC),
|
|
259
|
+
),
|
|
260
|
+
encoding="utf-8",
|
|
261
|
+
)
|
|
230
262
|
else:
|
|
231
263
|
file_analysis = engine.analyze_file(SourceManager().load_file(args.path), plugin_roots)
|
|
232
264
|
findings, coverage = file_analysis.findings, file_analysis.coverage
|