coretrace-python-analyzer 0.22.0__tar.gz → 0.24.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.22.0 → coretrace_python_analyzer-0.24.0}/CHANGELOG.md +140 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/PKG-INFO +1 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/docs/plugins.md +38 -3
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/docs/usage.md +145 -21
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/pyproject.toml +1 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/__init__.py +1 -1
- coretrace_python_analyzer-0.24.0/src/coretrace_python/abstract/module_constants.py +121 -0
- coretrace_python_analyzer-0.24.0/src/coretrace_python/abstract/strings.py +68 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +12 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +8 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +8 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +27 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +2 -1
- coretrace_python_analyzer-0.24.0/src/coretrace_python/bundled/security/code_injection/code_injection.py +107 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +1 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +7 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/xss/xss.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +8 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +7 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +8 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +7 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +1 -1
- coretrace_python_analyzer-0.24.0/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +121 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +8 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +8 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/cache.py +21 -15
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/cli.py +36 -6
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/engine.py +384 -121
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/findings/__init__.py +10 -1
- coretrace_python_analyzer-0.24.0/src/coretrace_python/findings/context.py +57 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/findings/model.py +11 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/findings/refutation.py +210 -27
- coretrace_python_analyzer-0.24.0/src/coretrace_python/findings/rules.py +16 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/frontend/parser.py +7 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/interprocedural/__init__.py +4 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/interprocedural/callgraph.py +14 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/interprocedural/modulegraph.py +54 -28
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/interprocedural/summaries.py +140 -32
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/ir/defuse.py +26 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/ir/lowering.py +2 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/ir/model.py +5 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/ir/printer.py +4 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/plugins/__init__.py +7 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/plugins/api.py +21 -4
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/plugins/detectors.py +27 -4
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/plugins/secrets.py +33 -13
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/reporters/report.py +6 -2
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/reporters/sarif.py +69 -14
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/semantic/scopes.py +42 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/semantic/symbols.py +2 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/source/__init__.py +9 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/source/manager.py +53 -7
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/source/model.py +12 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/taint/__init__.py +2 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/taint/engine.py +114 -27
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/taint/models.py +27 -3
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/taint/templates.py +686 -77
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/luigi.json +7 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/pygoat.json +3 -27
- coretrace_python_analyzer-0.24.0/tests/test_allowlist_shapes.py +402 -0
- coretrace_python_analyzer-0.24.0/tests/test_annotation_bindings.py +154 -0
- coretrace_python_analyzer-0.24.0/tests/test_annotation_expression_scopes.py +93 -0
- coretrace_python_analyzer-0.24.0/tests/test_attribute_call_loops.py +91 -0
- coretrace_python_analyzer-0.24.0/tests/test_attribute_call_review.py +111 -0
- coretrace_python_analyzer-0.24.0/tests/test_attribute_call_sinks.py +271 -0
- coretrace_python_analyzer-0.24.0/tests/test_cache_engine_version.py +42 -0
- coretrace_python_analyzer-0.24.0/tests/test_code_context.py +152 -0
- coretrace_python_analyzer-0.24.0/tests/test_code_context_single_file.py +58 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_code_injection.py +29 -0
- coretrace_python_analyzer-0.24.0/tests/test_credential_project_path.py +91 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_detectors.py +8 -1
- coretrace_python_analyzer-0.24.0/tests/test_dynamic_import_sinks.py +176 -0
- coretrace_python_analyzer-0.24.0/tests/test_dynamic_import_verdicts.py +101 -0
- coretrace_python_analyzer-0.24.0/tests/test_import_path_sinks.py +108 -0
- coretrace_python_analyzer-0.24.0/tests/test_jobs_option.py +171 -0
- coretrace_python_analyzer-0.24.0/tests/test_jobs_workers.py +99 -0
- coretrace_python_analyzer-0.24.0/tests/test_namespace_module_imports.py +249 -0
- coretrace_python_analyzer-0.24.0/tests/test_namespace_module_names.py +90 -0
- coretrace_python_analyzer-0.24.0/tests/test_namespace_module_visibility.py +275 -0
- coretrace_python_analyzer-0.24.0/tests/test_nonlocal_kept_value.py +116 -0
- coretrace_python_analyzer-0.24.0/tests/test_parse_warnings.py +59 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_release.py +1 -1
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_reporters.py +87 -1
- coretrace_python_analyzer-0.24.0/tests/test_rule_descriptions.py +157 -0
- coretrace_python_analyzer-0.24.0/tests/test_rule_descriptions_of_given_plugins.py +40 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_sarif_injection.py +6 -4
- coretrace_python_analyzer-0.24.0/tests/test_selection_sink_proofs.py +69 -0
- coretrace_python_analyzer-0.24.0/tests/test_sink_called_result.py +66 -0
- coretrace_python_analyzer-0.24.0/tests/test_sink_merge.py +72 -0
- coretrace_python_analyzer-0.24.0/tests/test_summary_external_call_order.py +114 -0
- coretrace_python_analyzer-0.24.0/tests/test_summary_fixpoint.py +264 -0
- coretrace_python_analyzer-0.24.0/tests/test_summary_reachability.py +81 -0
- coretrace_python_analyzer-0.24.0/tests/test_template_comment_continuations.py +221 -0
- coretrace_python_analyzer-0.24.0/tests/test_template_filter_walk.py +279 -0
- coretrace_python_analyzer-0.24.0/tests/test_template_open_constructs.py +239 -0
- coretrace_python_analyzer-0.24.0/tests/test_template_page_composition.py +510 -0
- coretrace_python_analyzer-0.24.0/tests/test_template_tag_backtracking.py +193 -0
- coretrace_python_analyzer-0.24.0/tests/test_unsafe_xml_risk.py +120 -0
- coretrace_python_analyzer-0.24.0/tests/test_vendored_context.py +171 -0
- coretrace_python_analyzer-0.22.0/src/coretrace_python/abstract/strings.py +0 -98
- coretrace_python_analyzer-0.22.0/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -19
- coretrace_python_analyzer-0.22.0/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -35
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/.github/workflows/ci.yml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/.github/workflows/release.yml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/.gitignore +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/AUTHORS.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/CODE_OF_CONDUCT.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/CONTRIBUTING.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/LICENSE +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/NOTICE +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/README.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/SECURITY.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/docs/architecture.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/examples/add.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/examples/canonical_symbols.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/reports/mitmproxy-2026-10-02/analysis.stderr +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/reports/mitmproxy-2026-10-02/findings.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/reports/mitmproxy-2026-10-02/rapport.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/reports/mitmproxy-2026-10-02/sbom.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/reports/mitmproxy-2026-10-02/triage.csv +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/reports/mitmproxy-2026-10-02/verification.txt +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/reports/mitmproxy-2026-10-02/vex.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/__main__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/abstract/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/abstract/constants.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/abstract/heap.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/abstract/ranges.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/abstract/values.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/analysis/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/analysis/manager.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/analysis/provider.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/cfg/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/cfg/builder.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/cfg/dominance.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/cfg/model.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dataflow/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dataflow/lattice.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dataflow/solver.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dependency/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dependency/advisories.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dependency/correlation.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dependency/graph.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dependency/policy.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dependency/sbom.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/dependency/vex.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/findings/baseline.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/findings/coverage.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/findings/provenance.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/findings/suppressions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/frontend/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/hir/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/hir/nodes.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/hir/visitors.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/ir/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/ir/ssa.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/plugins/loader.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/plugins/manifest.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/plugins/registry.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/reporters/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/reporters/json_format.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/reporters/text.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/semantic/__init__.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/semantic/identity.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/semantic/imports.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/source/positions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/taint/commands.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/taint/routes.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/src/coretrace_python/taint/urls.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/Chatbot.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/DjangoGoat.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/InsecureGram.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/NPL-IA.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/dvpwa.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/healthchecks.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/httpie.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/my_gdb.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/network-topologer.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/vulnerable-flask.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/expected/yt-fts.json +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/regression/repositories.toml +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_advisory_evidence.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_advisory_feeds.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_advisory_url_host.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_aiohttp_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_analysis_manager.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_architecture.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_argument_conditions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_argument_present_conditions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_attacker_arguments.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_audit_detectors.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_base_patterns.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_baseline.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_baseline_digests.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_baseline_key.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_baseline_schema_1.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_block_super.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_cache.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_cfg.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_class_keywords_with_targets.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_cli.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_closures.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_command_arguments.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_command_decided_cases.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_command_injection_nature.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_config_secrets_coverage.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_config_value_spans.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_constants.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_correlation.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_credential_context.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_dataflow.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_declared_entry_points.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_deep_nesting.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_dependencies.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_discovery.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_django_http_clients.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_django_routes.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_django_safe_values.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_docs.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_dominance.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_emit_ir.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_entry_point_aliases.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_entry_point_suffixes.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_exceptions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_factories_match_loop_else.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_fail_on.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_fastapi_cli_precision.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_file_locations.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_folded_secret_literals.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_frameworks.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_frontend_gaps.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_function_aliases_api.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_heap.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_hex_digests.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_hir_builder.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_imports.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_interprocedural.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_interprocedural_taint.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_ir.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_key_taint.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_keyword_name_conditions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_line_numbering.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_lock_dependency_maps.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_lock_positions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_match_patterns.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_members.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_module_identity.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_module_level.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_nonlocal_writeback.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_nosql_injection.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_open_redirect_destinations.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_operator_sources.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_packaging.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_parallel.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_parser.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_password_hashes.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_plaintext_credentials.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_plugins.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_powershell_file_operand.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_project.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_project_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_project_validators.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_provenance.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_pyir_imports.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_read_entry_points.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_real_code_precision.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_redirect_targets.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_refutation.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_refutation_evidence.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_regression.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_relative_paths.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_relative_root.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_remaining_limits.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_safe_arguments.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_scale.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_scopes.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_secrets_sbom.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_self_state.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_sequence_conditions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_settings_scope.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_source_manager.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_ssa.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_ssrf_destinations.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_ssrf_targets.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_structured_positions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_summary_sanitizers.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_suppressions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_symbol_resolution.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_symbols.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_syntactic_rules.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_syntax_control_flow.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_syntax_coverage.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_syntax_expressions.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_taint.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_tar_implementations.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_template_context_flows.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_template_escaping.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_template_filter_calls.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_template_filter_overrides.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_template_request_processor.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_tornado_bottle_models.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_unreadable_templates.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_url_proof.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_url_secret_placeholders.py +0 -0
- {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.24.0}/tests/test_vex.py +0 -0
|
@@ -1,5 +1,145 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.24.0 (2026-10-23)
|
|
4
|
+
|
|
5
|
+
### Compatibility and migration
|
|
6
|
+
|
|
7
|
+
- The cache format changes (19): caches built by an earlier version miss once.
|
|
8
|
+
- A module in a directory without `__init__.py` inside a package is named after that
|
|
9
|
+
package, as Python names it (#264): `pkg/sub/app.py` is `pkg.sub.app`. Its relative
|
|
10
|
+
imports now resolve, and a file that was a `syntax-error` (`relative import beyond
|
|
11
|
+
top-level package`) is analysed, flask's `sansio` for one. The project symbols of its
|
|
12
|
+
functions change with its name (`python.pkg.sub.app.view`), which a model declared on
|
|
13
|
+
the former symbol must follow; baseline keys do not change, since they hold no module
|
|
14
|
+
name. A script beside it still imports it by its short name, which answers no import
|
|
15
|
+
from another directory: at the project root, `import os` is the library even beside a
|
|
16
|
+
`pkg/sub/os.py`, which a top-level module of a directory took before. Files that share
|
|
17
|
+
only that short name are no longer noted as `ambiguous-module`, nor is an import that
|
|
18
|
+
only such names from other directories match.
|
|
19
|
+
- SARIF: each rule carries a title (`shortDescription`) and a description (`fullDescription`)
|
|
20
|
+
instead of repeating its id (#270), and each result carries its confidence as the
|
|
21
|
+
`confidence` property (#271). A flow reaching its sink inside a project function points at
|
|
22
|
+
that sink as a related location, which the result's message links to (#271).
|
|
23
|
+
- Django templates are read as a browser reads them (#263, #272, #278): a template that
|
|
24
|
+
leaves a tag, a `<script>` or `<style>` block or an end tag open, or includes one that
|
|
25
|
+
ends inside such a construct, is no longer established as escaping, so `xss` may be
|
|
26
|
+
reported where it was not.
|
|
27
|
+
- Django templates are also read as each page is put together (#273): a variable a
|
|
28
|
+
child's `{% block %}` prints inside its parent's `<script>`, an `{% include %}` that ends
|
|
29
|
+
the attribute value it stands in, or one of a name several files have outside HTML data,
|
|
30
|
+
is reported as `xss` where it was not, as is the variable `block` named anywhere but in
|
|
31
|
+
a bare `{{ block.super }}`. So is a template that includes Django's
|
|
32
|
+
`django/forms/widgets/attrs.html` inside a tag, which prints attribute names, and a
|
|
33
|
+
template including itself inside a tag or a script. A page beyond 8 MiB once put
|
|
34
|
+
together, or a template with more than 64 pages, or 16 MiB in all, through names
|
|
35
|
+
several files have, is not established and is listed in `Coverage.unread_templates`.
|
|
36
|
+
- Plugin API, additive: `Plugin.rules` maps the rules a plugin reports to their
|
|
37
|
+
`RuleDescription`, optional, so that third-party plugins describe their own rules;
|
|
38
|
+
`Finding.related` lists `RelatedLocation`s (a span and a message). `Report.rules` carries the
|
|
39
|
+
descriptions to the reporters.
|
|
40
|
+
- Plugin API: `coretrace_python.interprocedural.starting_summaries` gives the summaries an
|
|
41
|
+
import cycle's members start from, and `coretrace_python.engine.MAX_PROJECT_ITERATIONS`
|
|
42
|
+
is removed, since the project's summaries are iterated until they settle (#261).
|
|
43
|
+
- A summary lists its external calls in source order (#261): a finding naming the first
|
|
44
|
+
call of a callee's summary may name another of that callee, at the same line, as 4 of
|
|
45
|
+
saleor's 228 `orm-n-plus-one` findings do.
|
|
46
|
+
|
|
47
|
+
### Analysis
|
|
48
|
+
|
|
49
|
+
- `--check` ends on projects whose summaries never settled (#261). Around a call cycle of
|
|
50
|
+
three functions or more, a caller copied its callee's external calls in an order that
|
|
51
|
+
rotated forever, so ansible, fastapi, sentry and home-assistant never ended: those calls
|
|
52
|
+
are now kept in source order. Around an import cycle of three modules or more, the
|
|
53
|
+
project's iteration shifted by one module each time and stopped at its bound, which
|
|
54
|
+
could miss a sink reached through the cycle: its members now start from the summaries a
|
|
55
|
+
module starts from, and the iteration goes on until the project's summaries settle.
|
|
56
|
+
- A lambda or comprehension in the annotation of a parameter or a variable no longer
|
|
57
|
+
stops the analysis of a directory with a `KeyError` (#262). An assignment expression in
|
|
58
|
+
an annotation binds its name as Python 3.11 to 3.13 do: in a function, the name is a
|
|
59
|
+
local whether the annotation is evaluated or not; at module level, it exists only once
|
|
60
|
+
the annotation is evaluated.
|
|
61
|
+
|
|
62
|
+
### Templates
|
|
63
|
+
|
|
64
|
+
- Start tags and their attributes are read as the HTML tokenizer reads them, in time
|
|
65
|
+
linear in the size of the template (#263): a few dozen characters under a `templates`
|
|
66
|
+
directory, a PNG or `<a ` followed by 40 letters, stopped the analysis. A tag a template
|
|
67
|
+
leaves open is read to its end and its attributes are checked; a quoted value left open
|
|
68
|
+
stays quoted to the end of the template.
|
|
69
|
+
- A `<script>` or `<style>` block or an end tag a template leaves open runs to the end of
|
|
70
|
+
the template, and a variable inside it is refused, even where the page ends there
|
|
71
|
+
(#272). A template that ends inside such a construct or a tag is not established where
|
|
72
|
+
it is included, since the markup after the include continues it, and a template
|
|
73
|
+
extending another ends where that one ends.
|
|
74
|
+
- A comment ends where a browser ends it (`-->`, `--!>`, at once for `<!-->` and
|
|
75
|
+
`<!--->`), and one left open hides the rest of the template (#272). A comment holding
|
|
76
|
+
template tags may end at any of them, since an include there may close it and a
|
|
77
|
+
condition around its `<!--` may leave it out, or run to its close when they render
|
|
78
|
+
nothing: it is read on from its first tag, and that reading must be in HTML data right
|
|
79
|
+
after each later tag and right after the close (#278). Such a comment left open does
|
|
80
|
+
not end in HTML data.
|
|
81
|
+
|
|
82
|
+
### Performance
|
|
83
|
+
|
|
84
|
+
- Collecting the filters a template's render reaches walks each template once for each set
|
|
85
|
+
of blocks the templates extending it override (#279). It followed every path through
|
|
86
|
+
nested includes: two includes of one template at each of 20 levels took 31 s.
|
|
87
|
+
A template in a cycle of includes is still walked again wherever the cycle cuts its
|
|
88
|
+
walk short.
|
|
89
|
+
|
|
90
|
+
### Documentation
|
|
91
|
+
|
|
92
|
+
- `docs/usage.md` documents how templates are read and each page is put together, the
|
|
93
|
+
modules of a directory without `__init__.py`, assignment expressions in annotations,
|
|
94
|
+
and the rule descriptions and related locations of the SARIF report; `docs/plugins.md`
|
|
95
|
+
documents `Plugin.rules` and `Finding.related`.
|
|
96
|
+
|
|
97
|
+
## 0.23.0 (2026-10-16)
|
|
98
|
+
|
|
99
|
+
### Compatibility and migration
|
|
100
|
+
|
|
101
|
+
- The cache format changes (16) and the PyIR schema version is 2: caches built by an earlier version miss once. Every cache key also holds the engine version, so this release reanalyses every module of a cached project, which delivers the fixes below to it.
|
|
102
|
+
- `code-injection` reports new sinks (dynamic imports, `runpy`, the import path, `getattr` calls), so findings may appear once; each keeps the rule id `code-injection` and is titled by what the input chooses (#240). Refutation proves more allowlists and fewer mutated collections (#239), `unsafe-xml` messages and confidences change (#242), and a sink in code that cannot run, or a call to a returned callable, is no longer reported (#258, #240). Baselines recorded before should be recorded again after review.
|
|
103
|
+
- Findings carry new metadata, exported in JSON and as SARIF result properties: `context` (`test`, besides a rule's own `example`) for test code (#244), and `origin: vendored` for code under `third_party`, `third-party`, `vendor` or `vendored` (#243). Vendored code is still analysed and reported; the metadata changes no verdict.
|
|
104
|
+
- A project located under a directory named `test`, `tests`, `testing`, `fixtures` or `__tests__` no longer has every credential at low confidence: `hardcoded-credential` and `config-secrets` judge a file's path in the project (#253).
|
|
105
|
+
- Plugin API, all additive:
|
|
106
|
+
- `SymbolCallDetector.describe` returns a `CallReport` (message and confidence) per call (#242);
|
|
107
|
+
- `PluginContext.path(source_id)` gives a file's path from the root of the analysis, and `run_plugins` and `analyze_file` take that root (#253, #244);
|
|
108
|
+
- `Sink` gains `selects` and `requires_called_result`, and `Sink.applying` (#240);
|
|
109
|
+
- summaries gain `ExternalCall.invoked_at` and `NonlocalWrite.kept`, flows `TaintFlow.invoked_at`, and `NonlocalResult` its `previous` value (#240, #258);
|
|
110
|
+
- `coretrace_python.findings.context` classifies paths (`is_test_code`, `code_context`, `code_origin`, `path_in_project`).
|
|
111
|
+
|
|
112
|
+
### Code injection
|
|
113
|
+
|
|
114
|
+
- The module `importlib.import_module`, `__import__` or `runpy.run_module` imports and the file `runpy.run_path` runs are sinks, by position or keyword; the package of a relative name, the globals and the run name are data. Titled `Untrusted module import` and `Untrusted script execution`; a constant package prefix gives a hotspot (#240).
|
|
115
|
+
- The directory `sys.path.insert`, `append` or `extend` adds is a hotspot, `Untrusted import path`, as code runs only when a later import resolves there; `site.addsitedir` is a vulnerability, `Untrusted site directory`, its evidence stating that it runs the `.pth` files of a directory the attacker must control. Writes to `sys.path` that are not calls are not covered (#240).
|
|
116
|
+
- `getattr(obj, name)(...)` with a name the input chooses is a hotspot, `Untrusted attribute call`, reported at each call of the result, with `selected_line` and `invoked_line`, in the function and through project functions. A result that is only read, returned or stored before it is called is not reported (#240).
|
|
117
|
+
- These sinks are proven only by a check that limits the value to constants (membership in a constant allowlist, equality with a constant); a check of its shape (`isidentifier()`, `isalnum()`, `re.fullmatch`) leaves a hotspot. For `getattr`, a check where it selects counts past a join, a check before the call counts in the same loop iteration only, and nothing is reported when the selection or the call cannot run (#240).
|
|
118
|
+
- Calling the result of a call, or an item of it, calls what the call returned: its arguments are no longer read as the call's, so `eval('lambda x: x')(data)` or `importlib.import_module('plugins')(data)` reports nothing (#240).
|
|
119
|
+
|
|
120
|
+
### Refutation
|
|
121
|
+
|
|
122
|
+
- A membership test proves the value when every member is a constant that cannot change before the test: a tuple, list or set display, or a dict display by its keys, directly or through `.keys()`; `frozenset`, `set`, `tuple` or `list` of one of these; a module-level tuple or `frozenset` of constants bound once and never rebound. A list, set or dict of the function counts only while the function only reads it; a module-level one never does (#239).
|
|
123
|
+
- A lookup in a dict display gives one of its values, of what was stored in it since, or the default, never a key: input that only selects the entry is no longer passed on (#239).
|
|
124
|
+
|
|
125
|
+
### XML
|
|
126
|
+
|
|
127
|
+
- `unsafe-xml` states what each parser family risks: entity amplification with Expat older than 2.7.2 for the standard library, which reads no local file; external entities in lxml before 5.0, and in `iterparse` and `ETCompatXMLParser` before 6.1 (CVE-2026-41066); `resolve_entities=True` whatever the version, `'internal'` before 5.0. A parser given `resolve_entities=False` is not reported; XML content the project wrote itself, passed to a function that parses its argument as the document, is reported at low confidence (#242).
|
|
128
|
+
|
|
129
|
+
### Analysis
|
|
130
|
+
|
|
131
|
+
- A project function's summary keeps nothing of the code constant propagation proves cannot run: no sink, returned, merged or stored value reaches its callers (#258).
|
|
132
|
+
- A `nonlocal` a nested function may leave unwritten (no write on some path, a write that cannot run, an exception before it) keeps the value the caller's variable held before the call, joined with what may be written (#258).
|
|
133
|
+
- Warnings Python raises while parsing an analysed source (an invalid escape sequence) no longer reach the analyzer's standard error (#241).
|
|
134
|
+
|
|
135
|
+
### Performance
|
|
136
|
+
|
|
137
|
+
- `-j N` (`--jobs N`) analyses independent modules in `N` processes, `0` for one per core the process may run on; the report does not depend on it (#205). Each worker prepares the project once and reads the summaries a component imports from the cache or a directory of the run's own, instead of receiving them with every task: on healthchecks, `-j 8` takes 7.9–9.2 s against 15.3–15.5 s with `-j 1` (#245).
|
|
138
|
+
|
|
139
|
+
### Documentation
|
|
140
|
+
|
|
141
|
+
- `docs/usage.md` documents the new sinks and their limits, constant allowlists, the `context` and `origin` metadata, unreachable code and `nonlocal` writes, and re-measured performance; `docs/plugins.md` documents `describe`, `PluginContext.path` and the new `Sink` fields.
|
|
142
|
+
|
|
3
143
|
## 0.22.0 (2026-10-08)
|
|
4
144
|
|
|
5
145
|
### Compatibility and migration
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.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
|
|
@@ -85,7 +85,10 @@ class SensitiveLoggingPlugin(SymbolCallDetector):
|
|
|
85
85
|
message_template: ClassVar[str] = "call to {symbol} may log sensitive data"
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
The shipped `dangerous-eval` and `weak-crypto` plugins are of this kind.
|
|
88
|
+
The shipped `dangerous-eval` and `weak-crypto` plugins are of this kind. `accepts(call, symbol, defs)`
|
|
89
|
+
keeps only some calls; `describe(ctx, call, symbol, defs)` returns the `CallReport` (message and
|
|
90
|
+
confidence) of one call, the rule's `message_template` and `confidence` by default, for a rule that
|
|
91
|
+
tells its calls apart, as `unsafe-xml` does.
|
|
89
92
|
|
|
90
93
|
### A detector of taint flows: `TaintDetector`
|
|
91
94
|
|
|
@@ -166,7 +169,7 @@ class BottleModels(ModelPlugin):
|
|
|
166
169
|
| Model | Declares |
|
|
167
170
|
|---|---|
|
|
168
171
|
| `Source(symbol, label, kinds=ALL)` | A call or attribute whose value is attacker-controlled; `label` names it in messages (`http`, `stdin`, `argv`, `http-response`). `kinds` restricts what it can inject. Text input, such as a form field, a header or a URL parameter, carries `TEXT_KINDS`, every kind but `NOSQL`: a string cannot hold a query operator. |
|
|
169
|
-
| `Sink(symbol, kinds, positions=(), keywords=())` | A call dangerous for `kinds`. `positions` limits a kind to the given argument indexes, and `keywords` to the given keyword names; a restricted kind reaches only the arguments listed there. SQL sinks read the statement only, and the SSRF sinks of HTTP clients read the URL, by position or as `url=`. A starred argument or `**kwargs` reaches no restricted kind. |
|
|
172
|
+
| `Sink(symbol, kinds, positions=(), keywords=(), selects=False, requires_called_result=NONE)` | A call dangerous for `kinds`. `positions` limits a kind to the given argument indexes, and `keywords` to the given keyword names; a restricted kind reaches only the arguments listed there. SQL sinks read the statement only, and the SSRF sinks of HTTP clients read the URL, by position or as `url=`. A starred argument or `**kwargs` reaches no restricted kind. `selects` marks an argument that chooses by name the code that runs (a module, a file, a directory, an attribute): only a check limiting it to constants proves it. `requires_called_result` lists the kinds that apply only when the call's result is itself called, as for `getattr`'s name: the engine reports them at each invocation of the result, the other kinds at the call. |
|
|
170
173
|
| `Sanitizer(symbol, kinds)` | A call whose result is safe for `kinds`, including when the call is inside a project function the data goes through. |
|
|
171
174
|
| `EntryPoint(symbol, label, kinds=ALL, inputs=None)` | A decorator or a class base whose functions receive their parameters as `label` input, such as `flask.Flask.route`; or a project function or class named by its own symbol (`python.app.handlers.main`), as an integration reading a deployment declaration names a Lambda handler. `inputs` limits the parameters that are input to those positions, after `self` for a method: a handler's event, not its context. |
|
|
172
175
|
| `TypedParameter(symbol, label, kinds=ALL)` | A class whose annotated parameters carry `label` input, such as Django's `HttpRequest`. |
|
|
@@ -184,7 +187,9 @@ class BottleModels(ModelPlugin):
|
|
|
184
187
|
Two plugins may describe the same symbol. An identical model is registered once; two
|
|
185
188
|
`Sink` models merge their kinds, positions and keywords, so a plugin can add `SQL` to a sink
|
|
186
189
|
another plugin declared for `COMMAND`; any other difference is a conflict that stops
|
|
187
|
-
the analysis with a `ModelError` naming both plugins.
|
|
190
|
+
the analysis with a `ModelError` naming both plugins. A merged sink `selects` when either
|
|
191
|
+
does, and a kind requires the called result only when every declaration carrying it
|
|
192
|
+
does: an unconditional declaration is never weakened, whatever the order.
|
|
188
193
|
|
|
189
194
|
A plugin may also read models from the project it analyses: `project_models(self, root)`
|
|
190
195
|
returns them, from a file at `root` for instance. It is called once per directory check,
|
|
@@ -321,6 +326,10 @@ any analysis result: `SSAAnalysis` (`coretrace_python.ir.ssa`) for the code,
|
|
|
321
326
|
`TaintAnalysis` (`coretrace_python.taint`) for the flows, `RefutationAnalysis`
|
|
322
327
|
(`coretrace_python.findings.refutation`) for their verdicts, `DependencyAnalysis`
|
|
323
328
|
(`coretrace_python.dependency`) for the requirements. List what you read in `requires`.
|
|
329
|
+
`ctx.path(source_id)` is the path of a file from the root of the analysis, the one
|
|
330
|
+
`coretrace_python.findings.context` classifies (`is_test_code`, `code_context`,
|
|
331
|
+
`code_origin`): judge test code on it, never on the absolute path, which depends on where
|
|
332
|
+
the project sits. It is `None` for a file outside the root.
|
|
324
333
|
|
|
325
334
|
## Findings
|
|
326
335
|
|
|
@@ -332,6 +341,32 @@ JSON pointer of a value) for a value of a structured file whose line cannot be:
|
|
|
332
341
|
that reads a finding's line checks which it has, and never invents a line for a
|
|
333
342
|
`FileLocation` (`coretrace_python.source.positions` places JSON and TOML values).
|
|
334
343
|
|
|
344
|
+
A plugin describes the rules it reports in its optional `rules` attribute: each rule id to a
|
|
345
|
+
`RuleDescription`, a title and what the rule detects. The SARIF report gives them to its
|
|
346
|
+
consumers (`shortDescription` and `fullDescription`), so that GitHub code scanning names and
|
|
347
|
+
explains the alert; a rule the plugin does not describe is listed by its id alone.
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
from collections.abc import Mapping
|
|
351
|
+
|
|
352
|
+
from coretrace_python.findings import RuleDescription
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class SensitiveLoggingPlugin(SymbolCallDetector):
|
|
356
|
+
...
|
|
357
|
+
rules: ClassVar[Mapping[str, RuleDescription]] = {
|
|
358
|
+
"sensitive-logging": RuleDescription(
|
|
359
|
+
"Sensitive data logged",
|
|
360
|
+
"A logging call at debug level that may write sensitive data to the logs.",
|
|
361
|
+
),
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
A finding may also point at other places it involves, each a `RelatedLocation` (a span and a
|
|
366
|
+
short message) in its `related` tuple, as a taint flow reaching its sink inside a project
|
|
367
|
+
function points at that sink. The SARIF report lists them as `relatedLocations` and links to
|
|
368
|
+
them from the message.
|
|
369
|
+
|
|
335
370
|
## Testing a plugin
|
|
336
371
|
|
|
337
372
|
Load the plugin directory with the engine and check one source or one project:
|
|
@@ -48,7 +48,7 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
48
48
|
| `--plugins DIR` | Load the plugins found under `DIR` on top of the bundled ones. Repeatable. |
|
|
49
49
|
| `--no-bundled-plugins` | Do not load the plugins shipped with the package. |
|
|
50
50
|
| `--cache DIR` | Keep per-module results under `DIR` and reuse them for unchanged modules. |
|
|
51
|
-
| `--jobs N` | Analyse independent modules in `N` processes. |
|
|
51
|
+
| `-j N`, `--jobs N` | Analyse independent modules in `N` processes, `0` for one per available core. Default: `1`. |
|
|
52
52
|
| `--sbom PATH` | Write a CycloneDX bill of materials of the dependencies to `PATH`. |
|
|
53
53
|
| `--vex PATH` | Write an OpenVEX document to `PATH`: whether each advisory affecting a requirement affects the project. |
|
|
54
54
|
| `--advisories FILE` | Read a local advisory file in addition to `advisories.json` at the root. Repeatable. |
|
|
@@ -67,9 +67,16 @@ coretrace-python-analyzer [--check | --emit-ir [--ssa]] [options] [path]
|
|
|
67
67
|
## What is analysed
|
|
68
68
|
|
|
69
69
|
Given a directory, every `.py` file below it is a module of one project, named after
|
|
70
|
-
its package (`app/views.py` is `app.views`).
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
its package (`app/views.py` is `app.views`). A directory without `__init__.py` inside a
|
|
71
|
+
package is a portion of it, as Python reads one: `pkg/sub/app.py` is `pkg.sub.app`, and
|
|
72
|
+
its relative imports resolve against `pkg`. A module there can also be imported by the
|
|
73
|
+
name a script run from its directory gives it, `app`: an absolute import from that
|
|
74
|
+
directory reaches what lies beside it first, then what the package's root holds, and
|
|
75
|
+
files sharing only such a name are told apart by the importer's directory. From another
|
|
76
|
+
directory that name answers nothing, as Python finds no such module there: at the
|
|
77
|
+
project root, `import os` is the library even beside a `pkg/sub/os.py`. Two files
|
|
78
|
+
that would share a module name, `a/app.py` and `z/app.py` outside any package, are told
|
|
79
|
+
apart by their path from the root, `a.app` and `z.app`, and reported: each carries an `ambiguous-module` note, since a symbol
|
|
73
80
|
`python.app.main` may name a function of either and an import of `app` resolves only
|
|
74
81
|
from the importer's own directory, and each is covered as `ambiguous` rather than
|
|
75
82
|
analysed. An import of such a name that no file of the importer's own directory answers
|
|
@@ -120,8 +127,8 @@ variable is not treated that way yet.
|
|
|
120
127
|
|
|
121
128
|
| Rule | Reached sink |
|
|
122
129
|
|---|---|
|
|
123
|
-
| `command-injection` | Shell or process execution (`os.system`, `subprocess` with a string, …), judged on what the input controls in the command. A string a shell runs, a string naming the program, the program itself, or an operand the program runs as a command (after the host for `ssh`, the program of `sudo`, `env`, `xargs`, `timeout`…, any argument of a batch file) is `high`. The script an interpreter runs (`bash run.sh`, `python tool.py`, its first operand when no option such as `-c` gives the code), or the module of `python -m`, is `high` and kept for review: execution is possible, and its exploitability depends on the file the input chooses. The arguments after a script or code are the script's own: options of unknown meaning, reported `medium` (a hotspot after an option of the script), refuted behind a constant prefix. Otherwise an element of an argument list run without a shell can only be read as an option: `medium`, or `high` when a documented option of the program runs a command it chooses (`git push --receive-pack=…`, `ssh -o ProxyCommand=…`). A value a documented option takes is judged by what the option does with it, whatever its prefix: run it (`sh -c`, `cmd /c`, `python -c`: `high`), keep it as data (`git commit -m`: refuted). After an option the rule does not know, the finding stays a hotspot to review. A constant prefix that keeps a standalone input from being an option (`f"v{version}"`), or an operand after `--` for a program that honours it, is refuted. Every case keeps the rule id `command-injection`, so suppressions and baselines match whatever the case. When the rule reads the command element by element and establishes what the input is there, the finding's `injection` metadata (in the JSON report, and as
|
|
124
|
-
| `code-injection` | The code `eval` or `exec` runs; the globals and locals passed with it are data. `dangerous-eval` reports every such call, this rule the ones attacker input reaches. |
|
|
130
|
+
| `command-injection` | Shell or process execution (`os.system`, `subprocess` with a string, …), judged on what the input controls in the command. A string a shell runs, a string naming the program, the program itself, or an operand the program runs as a command (after the host for `ssh`, the program of `sudo`, `env`, `xargs`, `timeout`…, any argument of a batch file) is `high`. The script an interpreter runs (`bash run.sh`, `python tool.py`, its first operand when no option such as `-c` gives the code), or the module of `python -m`, is `high` and kept for review: execution is possible, and its exploitability depends on the file the input chooses. The arguments after a script or code are the script's own: options of unknown meaning, reported `medium` (a hotspot after an option of the script), refuted behind a constant prefix. Otherwise an element of an argument list run without a shell can only be read as an option: `medium`, or `high` when a documented option of the program runs a command it chooses (`git push --receive-pack=…`, `ssh -o ProxyCommand=…`). A value a documented option takes is judged by what the option does with it, whatever its prefix: run it (`sh -c`, `cmd /c`, `python -c`: `high`), keep it as data (`git commit -m`: refuted). After an option the rule does not know, the finding stays a hotspot to review. A constant prefix that keeps a standalone input from being an option (`f"v{version}"`), or an operand after `--` for a program that honours it, is refuted. Every case keeps the rule id `command-injection`, so suppressions and baselines match whatever the case. When the rule reads the command element by element and establishes what the input is there, the finding's `injection` metadata (in the JSON report, and as a property of its SARIF result) says so: `command` when the input runs or chooses what runs (a shell string, the program, an operand run as a command, the value of an option that runs it); `option` when it can only reach the program as an option or as the value of an option not established to run it, and the message is then titled `Option injection`. When that cannot be established (an option whose effect is to review, an unknown program or element before the input, a command that is not a list or tuple display, a sink such as `os.system` the rule does not read element by element), the metadata is absent and the title stays `Command injection`. |
|
|
131
|
+
| `code-injection` | The code `eval` or `exec` runs; the globals and locals passed with it are data. `dangerous-eval` reports every such call, this rule the ones attacker input reaches. Also the module `importlib.import_module`, `__import__` or `runpy.run_module` imports, and the file `runpy.run_path` runs, by position or by keyword (`name`, `mod_name`, `path_name`); the package of a relative name, the globals and the run name are data. An import runs the top-level code of a module the input chooses among those that can be found, a script path the file it chooses: execution is possible, and how far it goes depends on what the input can reach, so these findings are titled `Untrusted module import` and `Untrusted script execution`. A constant package prefix (`"app.plugins." + name`) confines an import to that package: a hotspot. The environment and the command line reach these sinks as they reach `eval`. Also the directory `sys.path.insert` (its second argument), `sys.path.append` and `sys.path.extend` add to the import path, and the directory `site.addsitedir` adds (`sitedir`), judged the same way. A directory on the import path runs code only when a later import resolves to a module the attacker placed there: a hotspot, titled `Untrusted import path`. `site.addsitedir` runs the import lines of the directory's `.pth` files during the call: a vulnerability, titled `Untrusted site directory`, exploitable when the directory holds files the attacker controls, as the finding's evidence states. Coverage of the import path is partial: writes to `sys.path` that are not calls, a slice assignment (`sys.path[:0] = [d]`), `sys.path += [d]` or rebinding `sys.path`, are not reported. Also the name `getattr(obj, name)` receives, when the attribute it selects is called: the finding, titled `Untrusted attribute call`, is placed at each call of the result, one per call, with the lines of the selection and of the call in its metadata (`selected_line`, `invoked_line`), and through project functions at the call that reaches them. What runs depends on what the object exposes, so it is a hotspot, on a module too and behind a constant prefix; a check proves the name only when it limits the value `getattr` received to constants, where `getattr` selects (even past a join) or before the call, and a check of a name rebound since proves nothing; a check before the call does not prove a callable selected in an earlier iteration of a loop, which reaches the call across the loop's back edge: only a check where it was selected does (even when the name never changes, which this version does not establish); nothing is reported when the selection or the call cannot run, in the function and inside a project function. Calling the result of a call, or an item of it, calls what the call returned: its arguments are not read as the call's, so `getattr(self, 'factory')(None, data)` or `eval('lambda x: x')(data)` reports nothing. A `getattr` whose result is only read is not reported, nor, in this version, one whose result is returned to the caller, stored in an attribute or a container, or passed on before it is called, nor `operator.attrgetter`, `operator.methodcaller`, `obj.__getattribute__(name)` or `vars(obj)[name]`; checks made inside a project function are not seen from its callers, as for every sink. |
|
|
125
132
|
| `sql-injection` | A database statement (`cursor.execute`, SQLAlchemy `text`, Django `raw`, …). Parameters of a parameterised query are not statements. |
|
|
126
133
|
| `nosql-injection` | A NoSQL query, such as a MongoDB filter, given attacker input that may hold a structure: a JSON body giving `{"$ne": null}` matches any value. The input that may hold a structure is JSON, the raw bodies a `json.loads` decodes, files, WebSocket messages and standard input. Text cannot hold an operator: form fields, query parameters, headers, cookies, URL parameters, the command line, the environment, and an entry-point parameter annotated with scalars (`str`, `int`, `Optional[str]`, `Annotated[str, Query()]`, `list[str]`, …), which FastAPI validates. A Django, REST framework, aiohttp or tornado view reads a request object: its text attributes (`GET`, `POST`, `headers`, `query_params`, `query`, `match_info`, `arguments`, …) and the URL parameters after it are text, its body, files and `data` are not. Limits: a helper function the request is passed to reads it as a whole; the keys of a dict are not followed, so a string concatenated into `$where` is not reported. The analyzer ships no NoSQL sink: model plugins declare them. |
|
|
127
134
|
| `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
|
|
@@ -132,10 +139,19 @@ variable is not treated that way yet.
|
|
|
132
139
|
| `plaintext-credential-storage` | A parameter named like a password stored in a database without hashing. Medium confidence, since a name is a hint. |
|
|
133
140
|
| `exploitable-vulnerability` | An API affected by an advisory of a vulnerable requirement, see Dependencies. |
|
|
134
141
|
|
|
135
|
-
Each flow is judged before it is reported.
|
|
136
|
-
|
|
142
|
+
Each flow is judged before it is reported. Code constant propagation proves can never
|
|
143
|
+
run (under `if False:`, `while False:`) reports nothing, in the function and through the
|
|
144
|
+
project functions that contain it: their summaries keep nothing of such code, neither a
|
|
145
|
+
sink nor a returned, merged or stored value. Code that may run is kept, whatever decides
|
|
146
|
+
it at run time, a parameter or a module-level name. A dominating guard that proves the value
|
|
147
|
+
safe (`isdigit()`, membership in a constant allowlist, equality with a constant, a
|
|
137
148
|
numeric value proven by `int()`, `len()` or bounded arithmetic, a validator such as
|
|
138
|
-
`re.fullmatch`) refutes it and nothing is reported. A
|
|
149
|
+
`re.fullmatch`) refutes it and nothing is reported. A sink that selects code by name,
|
|
150
|
+
the module of `importlib.import_module`, `__import__` or `runpy.run_module` and the file
|
|
151
|
+
of `runpy.run_path`, is proven only by a check that limits the value to constants:
|
|
152
|
+
membership in a constant allowlist or equality with a constant. A check of its shape
|
|
153
|
+
(`isidentifier()`, `isalnum()`, `re.fullmatch`) limits its characters, not the modules
|
|
154
|
+
or files it can name, and leaves a hotspot. A hotspot is reported at medium
|
|
139
155
|
confidence. Two things make a flow a hotspot:
|
|
140
156
|
|
|
141
157
|
- A guard that examines the data without proving it safe. That covers a test of the value
|
|
@@ -149,6 +165,28 @@ a vulnerability. When the flow passes the object whole, to the sink or to a func
|
|
|
149
165
|
a guard on any of its attributes still counts. The verdict and its evidence are in the
|
|
150
166
|
finding's metadata.
|
|
151
167
|
|
|
168
|
+
A membership test (`in`, `not in`) is a constant allowlist when every member is a constant
|
|
169
|
+
that cannot change before the test:
|
|
170
|
+
|
|
171
|
+
- a tuple, list or set display written in the function, or a dict display by its keys,
|
|
172
|
+
tested directly or through `.keys()`;
|
|
173
|
+
- `frozenset`, `set`, `tuple` or `list` of one of these;
|
|
174
|
+
- a module-level tuple, or `frozenset` of a display, of constants, bound once in its
|
|
175
|
+
module and never rebound there, by another assignment, an augmented assignment or a
|
|
176
|
+
`global` statement.
|
|
177
|
+
|
|
178
|
+
A list, set or dict of the function, a display or the `set` or `list` of one, counts only
|
|
179
|
+
while the function only reads it: changed by a method, stored, or passed to a call before
|
|
180
|
+
the test, it may hold anything. A
|
|
181
|
+
module-level list, set or dict can be changed from any module, so it never counts, and
|
|
182
|
+
the guard leaves a hotspot. Rebinding a module-level name from another module
|
|
183
|
+
(`settings.ALLOWED = ...`) is not seen, as for the module-level strings that fix a URL.
|
|
184
|
+
|
|
185
|
+
A lookup in a dict display, `{"list": "ls"}[key]` or `.get(key, default)`, gives one of
|
|
186
|
+
its values, of what was stored in it since, or the default, never a key: input that only
|
|
187
|
+
selects the entry, or that is itself a key of the display, is not passed on. The same lookup in a module-level dict passes the key on, as any other
|
|
188
|
+
subscript does.
|
|
189
|
+
|
|
152
190
|
Django's `render_to_string(name, context)` returns HTML in which autoescaping escaped
|
|
153
191
|
every variable, so data reaching a response through it is no `xss`, if the template really
|
|
154
192
|
escapes. On a directory check, the analyzer reads the project's templates to decide it.
|
|
@@ -166,6 +204,39 @@ It establishes escaping only when all of the following hold:
|
|
|
166
204
|
or an attribute name.
|
|
167
205
|
- No Python file of the project sets `autoescape` to `False`.
|
|
168
206
|
|
|
207
|
+
Templates are read as a browser's HTML tokenizer reads them, in time linear in the size
|
|
208
|
+
of the template. What a template leaves open at its end is checked too, since once the
|
|
209
|
+
template is included it runs into the markup that follows: a tag's attributes are
|
|
210
|
+
checked, a quoted value stays quoted until its quote closes, and a variable in a
|
|
211
|
+
`<script>` or `<style>` block or an end tag left open is refused, even where the page
|
|
212
|
+
ends there and the browser would never run it. A template including one that ends inside
|
|
213
|
+
such a block, a tag or an end tag is not established either: the markup after the
|
|
214
|
+
include continues it. A template extending another ends where that one ends. A comment
|
|
215
|
+
holding template tags may end at any of them, since an include there may close it and a
|
|
216
|
+
condition around its `<!--` may leave it out, or run to its close when they render
|
|
217
|
+
nothing (#278). The markup is read on from its first tag, and that reading must be in
|
|
218
|
+
HTML data right after each later tag and right after the close, where the others
|
|
219
|
+
resume; where it is not, the template is not established. Such a comment left open does
|
|
220
|
+
not end in HTML data, since it may hide what follows.
|
|
221
|
+
|
|
222
|
+
Each page is also put together as Django renders it, and read once more (#273): the
|
|
223
|
+
templates it extends, each block taking the definition of the most derived one and `{{
|
|
224
|
+
block.super }}` the next one up, and each template it includes in place. What a child's
|
|
225
|
+
block prints inside its parent's `<script>`, or what an include puts inside an
|
|
226
|
+
attribute, is read where it lands. `{{ block.super }}` is put in place only bare: the
|
|
227
|
+
variable `block` named anywhere else, through a filter, a tag or an alias (`{% with
|
|
228
|
+
parent=block %}{{ parent.super }}`), may still print the parent's markup, marked safe,
|
|
229
|
+
and the template is not established. A name several files have, which Django resolves
|
|
230
|
+
through loaders the analyzer does not read, counts as each of them: a page extending it
|
|
231
|
+
must escape with each file, except those already in its chain, which Django leaves out,
|
|
232
|
+
so that a template extending its own name extends another file of it; an include of it
|
|
233
|
+
is accepted only in HTML data, never inside an attribute, a tag, a `<script>` or
|
|
234
|
+
`<style>` block or a comment, and only when each file ends in HTML data, nothing of it
|
|
235
|
+
left open. A template including itself is read the same way. A page beyond 8 MiB once
|
|
236
|
+
put together, or a template with more than 64 pages, or 16 MiB in all, through the files
|
|
237
|
+
the templates it extends may be, is not established, and is listed among the templates
|
|
238
|
+
the analyzer could not read.
|
|
239
|
+
|
|
169
240
|
The analyzer never assumes escaping where it cannot read the template. That covers a
|
|
170
241
|
template file it cannot read, a template named by an expression, a template found
|
|
171
242
|
through a `DIRS` entry not named `templates`, one shipped by an installed package, one
|
|
@@ -181,7 +252,7 @@ reads no template.
|
|
|
181
252
|
| `debug-enabled` | `app.run(debug=True)` on a Flask application. |
|
|
182
253
|
| `missing-timeout` | A Requests or httpx call without a `timeout`. |
|
|
183
254
|
| `insecure-tls` | `verify=False` on a Requests or httpx call, or `ssl._create_unverified_context()`. |
|
|
184
|
-
| `unsafe-xml` |
|
|
255
|
+
| `unsafe-xml` | An XML parser of the standard library or of lxml, reported for what it risks; `defusedxml` is the safe replacement. The standard library's parsers run on Expat, which reads no local file but expands the entities a document declares: a crafted document can exhaust memory with Expat older than 2.7.2, which depends on the Python build at run time (`pyexpat.EXPAT_VERSION`). lxml resolves external entities by default before 5.0, and in `iterparse` and `ETCompatXMLParser` before 6.1 (CVE-2026-41066), which lets a crafted document read local files; `resolve_entities=True` does so whatever the version, and `'internal'` keeps external entities out only from lxml 5.0 on, an earlier lxml reading the string as true; a parser given `resolve_entities=False` is not reported. XML content the project wrote itself, a string constant or a module-level string bound once passed to a function that parses its argument as the document (`fromstring`, `parseString`, `XML`), is reported at low confidence; a constant path keeps its confidence, since whoever can write the file chooses the document. |
|
|
185
256
|
| `unsafe-archive-extraction` | `tarfile` or `zipfile` extraction, or `shutil.unpack_archive`, without a member `filter`. |
|
|
186
257
|
| `insecure-temp-file` | `tempfile.mktemp`, which names a file without creating it. |
|
|
187
258
|
| `weak-random` | A `random` function whose result is assigned to a credential-like name, or produced by a function named like one. Medium confidence. |
|
|
@@ -194,7 +265,7 @@ sources and in configuration files.
|
|
|
194
265
|
| Rule | Confidence | Trigger |
|
|
195
266
|
|---|---|---|
|
|
196
267
|
| `hardcoded-secret` | high | A provider-specific format: AWS, GitHub, Slack, Stripe, Google, private keys, JWTs, SendGrid, Twilio. |
|
|
197
|
-
| `hardcoded-credential` | medium | A credential-like name (`password`, `token`, `api_key`, `app.config['SECRET_KEY']`, …) bound to a real value. Placeholders are excluded. Low confidence in test code, fixtures and template files such as `.env.example`, with the context in the metadata. |
|
|
268
|
+
| `hardcoded-credential` | medium | A credential-like name (`password`, `token`, `api_key`, `app.config['SECRET_KEY']`, …) bound to a real value. Placeholders are excluded. Low confidence in test code, fixtures and template files such as `.env.example`, with the context in the metadata, judged on the file's path in the project, not on where the project sits. |
|
|
198
269
|
| `high-entropy-string` | low | An opaque high-entropy token. Hex digests (MD5, SHA-1, SHA-256, SHA-512 lengths, or a name such as `hash`, `checksum`, `commit`), subresource-integrity hashes (`sha512-…`) and character-set constants are not secrets unless a credential name says so. |
|
|
199
270
|
|
|
200
271
|
### Dependencies
|
|
@@ -303,7 +374,7 @@ the directory of the checked file; a path outside it is printed as it is.
|
|
|
303
374
|
```json
|
|
304
375
|
{
|
|
305
376
|
"schema_version": 1,
|
|
306
|
-
"tool": {"name": "coretrace-python-analyzer", "version": "0.
|
|
377
|
+
"tool": {"name": "coretrace-python-analyzer", "version": "0.24.0"},
|
|
307
378
|
"root": "/home/me/project",
|
|
308
379
|
"findings": [
|
|
309
380
|
{
|
|
@@ -334,6 +405,25 @@ in the file (`"/tool/app/api_token"`). The SARIF log gives such a result no `reg
|
|
|
334
405
|
with the pointer under the location's `properties`, and the text report shows the file
|
|
335
406
|
alone (`settings.toml: high hardcoded-credential: …`).
|
|
336
407
|
|
|
408
|
+
A finding in test code carries `context: test` in its metadata, in the JSON report and as
|
|
409
|
+
a property of its SARIF result. Test code is a file under a `test`, `tests`, `testing`,
|
|
410
|
+
`fixtures` or `__tests__` directory of the project, or a file named `test_*.py`,
|
|
411
|
+
`*_test.py` or `conftest.py`, judged on its path from the root of the report, not on
|
|
412
|
+
where the project sits. For a file checked alone, that root is the file's own directory,
|
|
413
|
+
as the report gives it, so a `test_*.py` module is test code either way, while a test
|
|
414
|
+
directory above the file is seen only when its directory is checked. The context
|
|
415
|
+
changes no verdict: it is there for baselines, `--fail-on`
|
|
416
|
+
wrappers, readers and refiner plugins, which see it. A rule may still give its own
|
|
417
|
+
context, as `hardcoded-credential` does with `example` for template files, and the
|
|
418
|
+
SARIF result carries that context too.
|
|
419
|
+
|
|
420
|
+
A finding in vendored code, under a `third_party`, `third-party`, `vendor` or `vendored`
|
|
421
|
+
directory, carries `origin: vendored`, in the JSON report and as a property of its SARIF
|
|
422
|
+
result. The origin is told apart from the context: the tests of a vendored project
|
|
423
|
+
carry both `context: test` and `origin: vendored`. Vendored code is still analysed and
|
|
424
|
+
its findings reported, so that a flow from the project through a vendored module is
|
|
425
|
+
not lost; the origin changes no verdict either.
|
|
426
|
+
|
|
337
427
|
`root` is the directory the paths are relative to. A file's `status` is `analysed`,
|
|
338
428
|
`ambiguous` (analysed, but another file has its module name, see above), `syntax-error`
|
|
339
429
|
or `unreadable`; only `analysed` files count in `files_analysed`.
|
|
@@ -348,7 +438,7 @@ bytes. A single-file check lists no components.
|
|
|
348
438
|
|
|
349
439
|
```json
|
|
350
440
|
"tool": {
|
|
351
|
-
"name": "coretrace-python-analyzer", "version": "0.
|
|
441
|
+
"name": "coretrace-python-analyzer", "version": "0.24.0",
|
|
352
442
|
"components": [
|
|
353
443
|
{"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
|
|
354
444
|
{"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
|
|
@@ -362,7 +452,14 @@ several sources define the same advisory, it names the one whose definition is u
|
|
|
362
452
|
the next section describes.
|
|
363
453
|
|
|
364
454
|
`--format sarif` prints a SARIF 2.1.0 log, one run with the tool, its rules and one
|
|
365
|
-
result per finding.
|
|
455
|
+
result per finding. Each rule carries a title (`shortDescription`) and what it detects
|
|
456
|
+
(`fullDescription`), so that a code scanning service can explain a finding: the plugin that
|
|
457
|
+
reports a rule describes it, the engine its own notes, and a rule a plugin does not describe
|
|
458
|
+
keeps its id alone. Each result carries the finding's confidence as the `confidence`
|
|
459
|
+
property. A flow that reaches its sink inside a project function is reported at the call, and
|
|
460
|
+
points at the sink as a related location (`relatedLocations`), with the message
|
|
461
|
+
`sink <sink>, reached via <function>`, which the result's message links to. The root is
|
|
462
|
+
declared once as the `SRCROOT` original URI base and
|
|
366
463
|
every location under it is relative to that base, which is what code scanning services
|
|
367
464
|
need to attach results to files. The components of a directory check are the tool's
|
|
368
465
|
`extensions`, their kind and digest in `properties`:
|
|
@@ -718,7 +815,7 @@ project's behalf would go unseen. Without a lock file, a package is never
|
|
|
718
815
|
`not_affected`. The document's `@id` derives from its statements, so two runs with the
|
|
719
816
|
same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
|
|
720
817
|
cannot know it. Its `tooling` names the analyzer and each component with its digest
|
|
721
|
-
(`coretrace-python-analyzer 0.
|
|
818
|
+
(`coretrace-python-analyzer 0.24.0; curated-advisories 2026.09.25 (sha256:…)`), so the
|
|
722
819
|
`@id` changes with the advisory data a statement was decided with.
|
|
723
820
|
|
|
724
821
|
## Large projects
|
|
@@ -730,8 +827,15 @@ graph and the modules it imports. On the next run an unchanged
|
|
|
730
827
|
module is served from the cache, so editing one file re-analyses that file and its
|
|
731
828
|
importers only. Entries are plain data; an unreadable entry is recomputed.
|
|
732
829
|
|
|
733
|
-
`--jobs N` analyses independent modules in `N` processes
|
|
734
|
-
|
|
830
|
+
`--jobs N` (`-j N`) analyses independent modules in `N` processes; `0` takes one per core
|
|
831
|
+
the process may run on, as `runtime.jobs` of `ctrace` does (its CPU affinity, read from
|
|
832
|
+
Python 3.13 on, and on Linux before; the cores of the machine otherwise), and the default
|
|
833
|
+
is one. On Windows, a pool holds at most 61 processes, the limit of Python's process pool
|
|
834
|
+
there, whatever `N`.
|
|
835
|
+
Modules are scheduled imports first, so the result is the same whatever `N`: the report,
|
|
836
|
+
the order of its findings, `--fail-on`, the cache, the baseline and the SBOM and VEX
|
|
837
|
+
documents. The command always waits for the complete report. There is no `--async`
|
|
838
|
+
option: `ctrace` deprecated its own for `-j`/`--jobs`, which this follows.
|
|
735
839
|
|
|
736
840
|
```bash
|
|
737
841
|
coretrace-python-analyzer --check src/ --cache .coretrace --jobs 4
|
|
@@ -750,11 +854,24 @@ Measured on one core of a laptop, single process, cold start, with the bundled p
|
|
|
750
854
|
|
|
751
855
|
Time grows a little faster than linearly with the size of the project, because the
|
|
752
856
|
interprocedural summaries are iterated to a fixpoint over the module graph. A warm
|
|
753
|
-
`--cache` brings an unchanged healthchecks to about one second
|
|
754
|
-
quarter of the cold time at the cost of one process's memory per job. The
|
|
857
|
+
`--cache` brings an unchanged healthchecks to about one second. The
|
|
755
858
|
`healthchecks` repository is part of the regression corpus, so the analysis time of a
|
|
756
859
|
real 45 000-line project is exercised on every change.
|
|
757
860
|
|
|
861
|
+
`--jobs` pays when a wave of the module graph holds many components: each process
|
|
862
|
+
prepares the project once and reads the summaries of the modules a component imports
|
|
863
|
+
from a directory of finished modules, the cache or one of the run's own, at the cost of
|
|
864
|
+
one process's memory per job. Measured on a laptop with 8 cores (4 performance, 4
|
|
865
|
+
efficiency), macOS, Python 3.14, bundled plugins, cold start (October 2026):
|
|
866
|
+
|
|
867
|
+
| Project | Python files | `-j 1` | `-j 8` |
|
|
868
|
+
|---|---:|---:|---:|
|
|
869
|
+
| healthchecks | 653 | 15.3–15.5 s | 7.9–9.2 s |
|
|
870
|
+
| mitmproxy | 583 | 51.6 s | 45.6 s |
|
|
871
|
+
|
|
872
|
+
The modules of a strongly connected import cycle are analysed together by one process,
|
|
873
|
+
and a wave waits for its slowest component: a project whose cycles are large gains less.
|
|
874
|
+
|
|
758
875
|
## Plugins
|
|
759
876
|
|
|
760
877
|
The package ships 35 plugins, loaded by default. Security models for the standard library
|
|
@@ -844,10 +961,17 @@ comprehensions, lambdas, nested functions and classes, `global`, `nonlocal`, `de
|
|
|
844
961
|
assignment expressions (`(y := f(x))`, laid out as an assignment before the statement),
|
|
845
962
|
`yield from`, starred assignment targets (`first, *rest = items`), class keyword arguments
|
|
846
963
|
such as `metaclass=`, `with` targets of any assignable form and `match` with every
|
|
847
|
-
pattern kind.
|
|
964
|
+
pattern kind. An assignment expression in an annotation binds its name as Python 3.11 to
|
|
965
|
+
3.13 do, where the annotation stands: in a function, the name is a local whether the
|
|
966
|
+
annotation is evaluated or not; at module level, it exists only once the annotation is
|
|
967
|
+
evaluated, so under `from __future__ import annotations` a builtin it names stays the
|
|
968
|
+
builtin. `finally` is modelled on the normal path only. A
|
|
848
969
|
`nonlocal` write made by a nested function flows back to the enclosing variable when the
|
|
849
970
|
enclosing function calls that nested function directly by name; a nested function passed
|
|
850
|
-
around and called elsewhere keeps its writes to itself.
|
|
971
|
+
around and called elsewhere keeps its writes to itself. Where the nested function may
|
|
972
|
+
return without writing the name (no write on some path, a write constant propagation
|
|
973
|
+
proves cannot run, or an exception before it), the variable keeps the value it had
|
|
974
|
+
before the call, joined with what may be written; only a write on every path replaces it. Methods of module-level classes are
|
|
851
975
|
analysed like functions.
|
|
852
976
|
|
|
853
977
|
## Looking at the intermediate representation
|