coretrace-python-analyzer 0.22.0__tar.gz → 0.23.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.
Files changed (357) hide show
  1. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/CHANGELOG.md +46 -0
  2. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/PKG-INFO +1 -1
  3. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/docs/plugins.md +12 -3
  4. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/docs/usage.md +89 -16
  5. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/pyproject.toml +1 -1
  6. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/__init__.py +1 -1
  7. coretrace_python_analyzer-0.23.0/src/coretrace_python/abstract/module_constants.py +121 -0
  8. coretrace_python_analyzer-0.23.0/src/coretrace_python/abstract/strings.py +68 -0
  9. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +27 -0
  10. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +2 -1
  11. coretrace_python_analyzer-0.23.0/src/coretrace_python/bundled/security/code_injection/code_injection.py +100 -0
  12. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +1 -1
  13. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +1 -1
  14. coretrace_python_analyzer-0.23.0/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +115 -0
  15. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/cache.py +14 -14
  16. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/cli.py +33 -5
  17. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/engine.py +197 -60
  18. coretrace_python_analyzer-0.23.0/src/coretrace_python/findings/context.py +57 -0
  19. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/findings/refutation.py +210 -27
  20. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/frontend/parser.py +7 -1
  21. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/interprocedural/callgraph.py +14 -1
  22. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/interprocedural/summaries.py +91 -19
  23. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/ir/defuse.py +26 -1
  24. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/ir/lowering.py +2 -1
  25. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/ir/model.py +5 -2
  26. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/ir/printer.py +4 -1
  27. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/plugins/__init__.py +7 -1
  28. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/plugins/api.py +16 -3
  29. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/plugins/detectors.py +21 -3
  30. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/plugins/secrets.py +17 -11
  31. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/reporters/sarif.py +15 -3
  32. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/semantic/symbols.py +2 -1
  33. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/taint/engine.py +114 -27
  34. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/taint/models.py +27 -3
  35. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/luigi.json +7 -0
  36. coretrace_python_analyzer-0.23.0/tests/test_allowlist_shapes.py +402 -0
  37. coretrace_python_analyzer-0.23.0/tests/test_attribute_call_loops.py +91 -0
  38. coretrace_python_analyzer-0.23.0/tests/test_attribute_call_review.py +111 -0
  39. coretrace_python_analyzer-0.23.0/tests/test_attribute_call_sinks.py +271 -0
  40. coretrace_python_analyzer-0.23.0/tests/test_cache_engine_version.py +42 -0
  41. coretrace_python_analyzer-0.23.0/tests/test_code_context.py +152 -0
  42. coretrace_python_analyzer-0.23.0/tests/test_code_context_single_file.py +58 -0
  43. coretrace_python_analyzer-0.23.0/tests/test_credential_project_path.py +91 -0
  44. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_detectors.py +8 -1
  45. coretrace_python_analyzer-0.23.0/tests/test_dynamic_import_sinks.py +176 -0
  46. coretrace_python_analyzer-0.23.0/tests/test_dynamic_import_verdicts.py +101 -0
  47. coretrace_python_analyzer-0.23.0/tests/test_import_path_sinks.py +108 -0
  48. coretrace_python_analyzer-0.23.0/tests/test_jobs_option.py +171 -0
  49. coretrace_python_analyzer-0.23.0/tests/test_jobs_workers.py +99 -0
  50. coretrace_python_analyzer-0.23.0/tests/test_nonlocal_kept_value.py +116 -0
  51. coretrace_python_analyzer-0.23.0/tests/test_parse_warnings.py +59 -0
  52. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_release.py +1 -1
  53. coretrace_python_analyzer-0.23.0/tests/test_selection_sink_proofs.py +69 -0
  54. coretrace_python_analyzer-0.23.0/tests/test_sink_called_result.py +66 -0
  55. coretrace_python_analyzer-0.23.0/tests/test_sink_merge.py +72 -0
  56. coretrace_python_analyzer-0.23.0/tests/test_summary_reachability.py +81 -0
  57. coretrace_python_analyzer-0.23.0/tests/test_unsafe_xml_risk.py +120 -0
  58. coretrace_python_analyzer-0.23.0/tests/test_vendored_context.py +170 -0
  59. coretrace_python_analyzer-0.22.0/src/coretrace_python/abstract/strings.py +0 -98
  60. coretrace_python_analyzer-0.22.0/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -19
  61. coretrace_python_analyzer-0.22.0/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -35
  62. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/.github/workflows/ci.yml +0 -0
  63. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/.github/workflows/release.yml +0 -0
  64. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/.gitignore +0 -0
  65. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/AUTHORS.md +0 -0
  66. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/CODE_OF_CONDUCT.md +0 -0
  67. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/CONTRIBUTING.md +0 -0
  68. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/LICENSE +0 -0
  69. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/NOTICE +0 -0
  70. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/README.md +0 -0
  71. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/SECURITY.md +0 -0
  72. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/docs/architecture.md +0 -0
  73. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/examples/add.py +0 -0
  74. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/examples/canonical_symbols.py +0 -0
  75. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/reports/mitmproxy-2026-10-02/analysis.stderr +0 -0
  76. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/reports/mitmproxy-2026-10-02/findings.json +0 -0
  77. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/reports/mitmproxy-2026-10-02/rapport.md +0 -0
  78. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/reports/mitmproxy-2026-10-02/sbom.json +0 -0
  79. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/reports/mitmproxy-2026-10-02/triage.csv +0 -0
  80. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/reports/mitmproxy-2026-10-02/verification.txt +0 -0
  81. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/reports/mitmproxy-2026-10-02/vex.json +0 -0
  82. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/__main__.py +0 -0
  83. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/abstract/__init__.py +0 -0
  84. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/abstract/constants.py +0 -0
  85. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/abstract/heap.py +0 -0
  86. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/abstract/ranges.py +0 -0
  87. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/abstract/values.py +0 -0
  88. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/analysis/__init__.py +0 -0
  89. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/analysis/manager.py +0 -0
  90. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/analysis/provider.py +0 -0
  91. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
  92. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
  93. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
  94. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
  95. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
  96. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
  97. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
  98. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
  99. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
  100. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
  101. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
  102. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
  103. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
  104. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
  105. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
  106. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
  107. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
  108. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
  109. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
  110. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
  111. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
  112. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
  113. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
  114. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
  115. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
  116. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
  117. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
  118. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
  119. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
  120. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
  121. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
  122. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
  123. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
  124. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
  125. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
  126. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
  127. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
  128. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
  129. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +0 -0
  130. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +0 -0
  131. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
  132. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
  133. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
  134. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
  135. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
  136. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
  137. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
  138. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
  139. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
  140. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
  141. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
  142. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
  143. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
  144. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
  145. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
  146. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
  147. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
  148. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
  149. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
  150. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
  151. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
  152. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
  153. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
  154. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
  155. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
  156. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
  157. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
  158. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
  159. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/cfg/__init__.py +0 -0
  160. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/cfg/builder.py +0 -0
  161. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/cfg/dominance.py +0 -0
  162. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/cfg/model.py +0 -0
  163. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dataflow/__init__.py +0 -0
  164. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dataflow/lattice.py +0 -0
  165. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dataflow/solver.py +0 -0
  166. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dependency/__init__.py +0 -0
  167. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dependency/advisories.py +0 -0
  168. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dependency/correlation.py +0 -0
  169. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dependency/graph.py +0 -0
  170. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dependency/policy.py +0 -0
  171. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dependency/sbom.py +0 -0
  172. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/dependency/vex.py +0 -0
  173. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/findings/__init__.py +0 -0
  174. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/findings/baseline.py +0 -0
  175. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/findings/coverage.py +0 -0
  176. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/findings/model.py +0 -0
  177. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/findings/provenance.py +0 -0
  178. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/findings/suppressions.py +0 -0
  179. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/frontend/__init__.py +0 -0
  180. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
  181. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/hir/__init__.py +0 -0
  182. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/hir/nodes.py +0 -0
  183. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/hir/visitors.py +0 -0
  184. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/interprocedural/__init__.py +0 -0
  185. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
  186. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/ir/__init__.py +0 -0
  187. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/ir/ssa.py +0 -0
  188. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/plugins/loader.py +0 -0
  189. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/plugins/manifest.py +0 -0
  190. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/plugins/registry.py +0 -0
  191. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/reporters/__init__.py +0 -0
  192. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/reporters/json_format.py +0 -0
  193. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/reporters/report.py +0 -0
  194. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/reporters/text.py +0 -0
  195. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/semantic/__init__.py +0 -0
  196. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/semantic/identity.py +0 -0
  197. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/semantic/imports.py +0 -0
  198. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/semantic/scopes.py +0 -0
  199. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/source/__init__.py +0 -0
  200. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/source/manager.py +0 -0
  201. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/source/model.py +0 -0
  202. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/source/positions.py +0 -0
  203. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/taint/__init__.py +0 -0
  204. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/taint/commands.py +0 -0
  205. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/taint/routes.py +0 -0
  206. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/taint/templates.py +0 -0
  207. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/src/coretrace_python/taint/urls.py +0 -0
  208. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/Chatbot.json +0 -0
  209. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/DjangoGoat.json +0 -0
  210. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
  211. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/InsecureGram.json +0 -0
  212. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
  213. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/NPL-IA.json +0 -0
  214. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
  215. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
  216. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/dvpwa.json +0 -0
  217. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
  218. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/healthchecks.json +0 -0
  219. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/httpie.json +0 -0
  220. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/my_gdb.json +0 -0
  221. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/network-topologer.json +0 -0
  222. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/pygoat.json +0 -0
  223. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
  224. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/vulnerable-flask.json +0 -0
  225. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/expected/yt-fts.json +0 -0
  226. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
  227. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
  228. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
  229. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
  230. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/regression/repositories.toml +0 -0
  231. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_advisory_evidence.py +0 -0
  232. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_advisory_feeds.py +0 -0
  233. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_advisory_url_host.py +0 -0
  234. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_aiohttp_models.py +0 -0
  235. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_analysis_manager.py +0 -0
  236. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_architecture.py +0 -0
  237. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_argument_conditions.py +0 -0
  238. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_argument_present_conditions.py +0 -0
  239. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_attacker_arguments.py +0 -0
  240. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_audit_detectors.py +0 -0
  241. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_base_patterns.py +0 -0
  242. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_baseline.py +0 -0
  243. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_baseline_digests.py +0 -0
  244. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_baseline_key.py +0 -0
  245. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_baseline_schema_1.py +0 -0
  246. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_block_super.py +0 -0
  247. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_cache.py +0 -0
  248. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_cfg.py +0 -0
  249. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_class_keywords_with_targets.py +0 -0
  250. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_cli.py +0 -0
  251. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_closures.py +0 -0
  252. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_code_injection.py +0 -0
  253. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_command_arguments.py +0 -0
  254. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_command_decided_cases.py +0 -0
  255. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_command_injection_nature.py +0 -0
  256. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_config_secrets_coverage.py +0 -0
  257. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_config_value_spans.py +0 -0
  258. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_constants.py +0 -0
  259. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_correlation.py +0 -0
  260. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_credential_context.py +0 -0
  261. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_dataflow.py +0 -0
  262. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_declared_entry_points.py +0 -0
  263. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_deep_nesting.py +0 -0
  264. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_dependencies.py +0 -0
  265. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_discovery.py +0 -0
  266. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_django_http_clients.py +0 -0
  267. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_django_routes.py +0 -0
  268. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_django_safe_values.py +0 -0
  269. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_docs.py +0 -0
  270. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_dominance.py +0 -0
  271. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_emit_ir.py +0 -0
  272. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_entry_point_aliases.py +0 -0
  273. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_entry_point_suffixes.py +0 -0
  274. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_exceptions.py +0 -0
  275. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_factories_match_loop_else.py +0 -0
  276. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_fail_on.py +0 -0
  277. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_fastapi_cli_precision.py +0 -0
  278. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_file_locations.py +0 -0
  279. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_folded_secret_literals.py +0 -0
  280. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_frameworks.py +0 -0
  281. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_frontend_gaps.py +0 -0
  282. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_function_aliases_api.py +0 -0
  283. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_heap.py +0 -0
  284. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_hex_digests.py +0 -0
  285. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_hir_builder.py +0 -0
  286. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_imports.py +0 -0
  287. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_interprocedural.py +0 -0
  288. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_interprocedural_taint.py +0 -0
  289. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_ir.py +0 -0
  290. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_key_taint.py +0 -0
  291. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_keyword_name_conditions.py +0 -0
  292. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_line_numbering.py +0 -0
  293. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_lock_dependency_maps.py +0 -0
  294. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_lock_positions.py +0 -0
  295. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_match_patterns.py +0 -0
  296. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_members.py +0 -0
  297. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_module_identity.py +0 -0
  298. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_module_level.py +0 -0
  299. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_nonlocal_writeback.py +0 -0
  300. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_nosql_injection.py +0 -0
  301. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_open_redirect_destinations.py +0 -0
  302. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_operator_sources.py +0 -0
  303. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_packaging.py +0 -0
  304. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_parallel.py +0 -0
  305. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_parser.py +0 -0
  306. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_password_hashes.py +0 -0
  307. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_plaintext_credentials.py +0 -0
  308. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_plugins.py +0 -0
  309. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_powershell_file_operand.py +0 -0
  310. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_project.py +0 -0
  311. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_project_models.py +0 -0
  312. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_project_validators.py +0 -0
  313. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_provenance.py +0 -0
  314. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_pyir_imports.py +0 -0
  315. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_read_entry_points.py +0 -0
  316. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_real_code_precision.py +0 -0
  317. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_redirect_targets.py +0 -0
  318. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_refutation.py +0 -0
  319. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_refutation_evidence.py +0 -0
  320. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_regression.py +0 -0
  321. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_relative_paths.py +0 -0
  322. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_relative_root.py +0 -0
  323. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_remaining_limits.py +0 -0
  324. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_reporters.py +0 -0
  325. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_safe_arguments.py +0 -0
  326. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_sarif_injection.py +0 -0
  327. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_scale.py +0 -0
  328. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_scopes.py +0 -0
  329. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_secrets_sbom.py +0 -0
  330. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_self_state.py +0 -0
  331. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_sequence_conditions.py +0 -0
  332. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_settings_scope.py +0 -0
  333. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_source_manager.py +0 -0
  334. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_ssa.py +0 -0
  335. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_ssrf_destinations.py +0 -0
  336. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_ssrf_targets.py +0 -0
  337. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_structured_positions.py +0 -0
  338. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_summary_sanitizers.py +0 -0
  339. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_suppressions.py +0 -0
  340. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_symbol_resolution.py +0 -0
  341. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_symbols.py +0 -0
  342. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_syntactic_rules.py +0 -0
  343. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_syntax_control_flow.py +0 -0
  344. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_syntax_coverage.py +0 -0
  345. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_syntax_expressions.py +0 -0
  346. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_taint.py +0 -0
  347. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_tar_implementations.py +0 -0
  348. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_template_context_flows.py +0 -0
  349. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_template_escaping.py +0 -0
  350. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_template_filter_calls.py +0 -0
  351. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_template_filter_overrides.py +0 -0
  352. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_template_request_processor.py +0 -0
  353. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_tornado_bottle_models.py +0 -0
  354. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_unreadable_templates.py +0 -0
  355. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_url_proof.py +0 -0
  356. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_url_secret_placeholders.py +0 -0
  357. {coretrace_python_analyzer-0.22.0 → coretrace_python_analyzer-0.23.0}/tests/test_vex.py +0 -0
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.23.0 (2026-10-16)
4
+
5
+ ### Compatibility and migration
6
+
7
+ - 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.
8
+ - `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.
9
+ - 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.
10
+ - 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).
11
+ - Plugin API, all additive:
12
+ - `SymbolCallDetector.describe` returns a `CallReport` (message and confidence) per call (#242);
13
+ - `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);
14
+ - `Sink` gains `selects` and `requires_called_result`, and `Sink.applying` (#240);
15
+ - summaries gain `ExternalCall.invoked_at` and `NonlocalWrite.kept`, flows `TaintFlow.invoked_at`, and `NonlocalResult` its `previous` value (#240, #258);
16
+ - `coretrace_python.findings.context` classifies paths (`is_test_code`, `code_context`, `code_origin`, `path_in_project`).
17
+
18
+ ### Code injection
19
+
20
+ - 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).
21
+ - 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).
22
+ - `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).
23
+ - 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).
24
+ - 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).
25
+
26
+ ### Refutation
27
+
28
+ - 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).
29
+ - 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).
30
+
31
+ ### XML
32
+
33
+ - `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).
34
+
35
+ ### Analysis
36
+
37
+ - 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).
38
+ - 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).
39
+ - Warnings Python raises while parsing an analysed source (an invalid escape sequence) no longer reach the analyzer's standard error (#241).
40
+
41
+ ### Performance
42
+
43
+ - `-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).
44
+
45
+ ### Documentation
46
+
47
+ - `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.
48
+
3
49
  ## 0.22.0 (2026-10-08)
4
50
 
5
51
  ### Compatibility and migration
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: coretrace-python-analyzer
3
- Version: 0.22.0
3
+ Version: 0.23.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
 
@@ -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. |
@@ -120,8 +120,8 @@ variable is not treated that way yet.
120
120
 
121
121
  | Rule | Reached sink |
122
122
  |---|---|
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 the only 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`. |
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. |
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 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`. |
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. 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
125
  | `sql-injection` | A database statement (`cursor.execute`, SQLAlchemy `text`, Django `raw`, …). Parameters of a parameterised query are not statements. |
126
126
  | `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
127
  | `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
@@ -132,10 +132,19 @@ variable is not treated that way yet.
132
132
  | `plaintext-credential-storage` | A parameter named like a password stored in a database without hashing. Medium confidence, since a name is a hint. |
133
133
  | `exploitable-vulnerability` | An API affected by an advisory of a vulnerable requirement, see Dependencies. |
134
134
 
135
- Each flow is judged before it is reported. A dominating guard that proves the value
136
- safe (`isdigit()`, membership in a constant collection, equality with a constant, a
135
+ Each flow is judged before it is reported. Code constant propagation proves can never
136
+ run (under `if False:`, `while False:`) reports nothing, in the function and through the
137
+ project functions that contain it: their summaries keep nothing of such code, neither a
138
+ sink nor a returned, merged or stored value. Code that may run is kept, whatever decides
139
+ it at run time, a parameter or a module-level name. A dominating guard that proves the value
140
+ safe (`isdigit()`, membership in a constant allowlist, equality with a constant, a
137
141
  numeric value proven by `int()`, `len()` or bounded arithmetic, a validator such as
138
- `re.fullmatch`) refutes it and nothing is reported. A hotspot is reported at medium
142
+ `re.fullmatch`) refutes it and nothing is reported. A sink that selects code by name,
143
+ the module of `importlib.import_module`, `__import__` or `runpy.run_module` and the file
144
+ of `runpy.run_path`, is proven only by a check that limits the value to constants:
145
+ membership in a constant allowlist or equality with a constant. A check of its shape
146
+ (`isidentifier()`, `isalnum()`, `re.fullmatch`) limits its characters, not the modules
147
+ or files it can name, and leaves a hotspot. A hotspot is reported at medium
139
148
  confidence. Two things make a flow a hotspot:
140
149
 
141
150
  - A guard that examines the data without proving it safe. That covers a test of the value
@@ -149,6 +158,28 @@ a vulnerability. When the flow passes the object whole, to the sink or to a func
149
158
  a guard on any of its attributes still counts. The verdict and its evidence are in the
150
159
  finding's metadata.
151
160
 
161
+ A membership test (`in`, `not in`) is a constant allowlist when every member is a constant
162
+ that cannot change before the test:
163
+
164
+ - a tuple, list or set display written in the function, or a dict display by its keys,
165
+ tested directly or through `.keys()`;
166
+ - `frozenset`, `set`, `tuple` or `list` of one of these;
167
+ - a module-level tuple, or `frozenset` of a display, of constants, bound once in its
168
+ module and never rebound there, by another assignment, an augmented assignment or a
169
+ `global` statement.
170
+
171
+ A list, set or dict of the function, a display or the `set` or `list` of one, counts only
172
+ while the function only reads it: changed by a method, stored, or passed to a call before
173
+ the test, it may hold anything. A
174
+ module-level list, set or dict can be changed from any module, so it never counts, and
175
+ the guard leaves a hotspot. Rebinding a module-level name from another module
176
+ (`settings.ALLOWED = ...`) is not seen, as for the module-level strings that fix a URL.
177
+
178
+ A lookup in a dict display, `{"list": "ls"}[key]` or `.get(key, default)`, gives one of
179
+ its values, of what was stored in it since, or the default, never a key: input that only
180
+ 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
181
+ subscript does.
182
+
152
183
  Django's `render_to_string(name, context)` returns HTML in which autoescaping escaped
153
184
  every variable, so data reaching a response through it is no `xss`, if the template really
154
185
  escapes. On a directory check, the analyzer reads the project's templates to decide it.
@@ -181,7 +212,7 @@ reads no template.
181
212
  | `debug-enabled` | `app.run(debug=True)` on a Flask application. |
182
213
  | `missing-timeout` | A Requests or httpx call without a `timeout`. |
183
214
  | `insecure-tls` | `verify=False` on a Requests or httpx call, or `ssl._create_unverified_context()`. |
184
- | `unsafe-xml` | A standard-library or lxml XML parser, which expands entities; `defusedxml` is the safe replacement. |
215
+ | `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
216
  | `unsafe-archive-extraction` | `tarfile` or `zipfile` extraction, or `shutil.unpack_archive`, without a member `filter`. |
186
217
  | `insecure-temp-file` | `tempfile.mktemp`, which names a file without creating it. |
187
218
  | `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 +225,7 @@ sources and in configuration files.
194
225
  | Rule | Confidence | Trigger |
195
226
  |---|---|---|
196
227
  | `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. |
228
+ | `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
229
  | `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
230
 
200
231
  ### Dependencies
@@ -303,7 +334,7 @@ the directory of the checked file; a path outside it is printed as it is.
303
334
  ```json
304
335
  {
305
336
  "schema_version": 1,
306
- "tool": {"name": "coretrace-python-analyzer", "version": "0.22.0"},
337
+ "tool": {"name": "coretrace-python-analyzer", "version": "0.23.0"},
307
338
  "root": "/home/me/project",
308
339
  "findings": [
309
340
  {
@@ -334,6 +365,25 @@ in the file (`"/tool/app/api_token"`). The SARIF log gives such a result no `reg
334
365
  with the pointer under the location's `properties`, and the text report shows the file
335
366
  alone (`settings.toml: high hardcoded-credential: …`).
336
367
 
368
+ A finding in test code carries `context: test` in its metadata, in the JSON report and as
369
+ a property of its SARIF result. Test code is a file under a `test`, `tests`, `testing`,
370
+ `fixtures` or `__tests__` directory of the project, or a file named `test_*.py`,
371
+ `*_test.py` or `conftest.py`, judged on its path from the root of the report, not on
372
+ where the project sits. For a file checked alone, that root is the file's own directory,
373
+ as the report gives it, so a `test_*.py` module is test code either way, while a test
374
+ directory above the file is seen only when its directory is checked. The context
375
+ changes no verdict: it is there for baselines, `--fail-on`
376
+ wrappers, readers and refiner plugins, which see it. A rule may still give its own
377
+ context, as `hardcoded-credential` does with `example` for template files, and the
378
+ SARIF result carries that context too.
379
+
380
+ A finding in vendored code, under a `third_party`, `third-party`, `vendor` or `vendored`
381
+ directory, carries `origin: vendored`, in the JSON report and as a property of its SARIF
382
+ result. The origin is told apart from the context: the tests of a vendored project
383
+ carry both `context: test` and `origin: vendored`. Vendored code is still analysed and
384
+ its findings reported, so that a flow from the project through a vendored module is
385
+ not lost; the origin changes no verdict either.
386
+
337
387
  `root` is the directory the paths are relative to. A file's `status` is `analysed`,
338
388
  `ambiguous` (analysed, but another file has its module name, see above), `syntax-error`
339
389
  or `unreadable`; only `analysed` files count in `files_analysed`.
@@ -348,7 +398,7 @@ bytes. A single-file check lists no components.
348
398
 
349
399
  ```json
350
400
  "tool": {
351
- "name": "coretrace-python-analyzer", "version": "0.22.0",
401
+ "name": "coretrace-python-analyzer", "version": "0.23.0",
352
402
  "components": [
353
403
  {"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
354
404
  {"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
@@ -718,7 +768,7 @@ project's behalf would go unseen. Without a lock file, a package is never
718
768
  `not_affected`. The document's `@id` derives from its statements, so two runs with the
719
769
  same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
720
770
  cannot know it. Its `tooling` names the analyzer and each component with its digest
721
- (`coretrace-python-analyzer 0.22.0; curated-advisories 2026.09.25 (sha256:…)`), so the
771
+ (`coretrace-python-analyzer 0.23.0; curated-advisories 2026.09.25 (sha256:…)`), so the
722
772
  `@id` changes with the advisory data a statement was decided with.
723
773
 
724
774
  ## Large projects
@@ -730,8 +780,15 @@ graph and the modules it imports. On the next run an unchanged
730
780
  module is served from the cache, so editing one file re-analyses that file and its
731
781
  importers only. Entries are plain data; an unreadable entry is recomputed.
732
782
 
733
- `--jobs N` analyses independent modules in `N` processes. Modules are scheduled imports
734
- first, so the result is the same whatever `N`.
783
+ `--jobs N` (`-j N`) analyses independent modules in `N` processes; `0` takes one per core
784
+ the process may run on, as `runtime.jobs` of `ctrace` does (its CPU affinity, read from
785
+ Python 3.13 on, and on Linux before; the cores of the machine otherwise), and the default
786
+ is one. On Windows, a pool holds at most 61 processes, the limit of Python's process pool
787
+ there, whatever `N`.
788
+ Modules are scheduled imports first, so the result is the same whatever `N`: the report,
789
+ the order of its findings, `--fail-on`, the cache, the baseline and the SBOM and VEX
790
+ documents. The command always waits for the complete report. There is no `--async`
791
+ option: `ctrace` deprecated its own for `-j`/`--jobs`, which this follows.
735
792
 
736
793
  ```bash
737
794
  coretrace-python-analyzer --check src/ --cache .coretrace --jobs 4
@@ -750,11 +807,24 @@ Measured on one core of a laptop, single process, cold start, with the bundled p
750
807
 
751
808
  Time grows a little faster than linearly with the size of the project, because the
752
809
  interprocedural summaries are iterated to a fixpoint over the module graph. A warm
753
- `--cache` brings an unchanged healthchecks to about one second, `--jobs 4` saves a
754
- quarter of the cold time at the cost of one process's memory per job. The
810
+ `--cache` brings an unchanged healthchecks to about one second. The
755
811
  `healthchecks` repository is part of the regression corpus, so the analysis time of a
756
812
  real 45 000-line project is exercised on every change.
757
813
 
814
+ `--jobs` pays when a wave of the module graph holds many components: each process
815
+ prepares the project once and reads the summaries of the modules a component imports
816
+ from a directory of finished modules, the cache or one of the run's own, at the cost of
817
+ one process's memory per job. Measured on a laptop with 8 cores (4 performance, 4
818
+ efficiency), macOS, Python 3.14, bundled plugins, cold start (October 2026):
819
+
820
+ | Project | Python files | `-j 1` | `-j 8` |
821
+ |---|---:|---:|---:|
822
+ | healthchecks | 653 | 15.3–15.5 s | 7.9–9.2 s |
823
+ | mitmproxy | 583 | 51.6 s | 45.6 s |
824
+
825
+ The modules of a strongly connected import cycle are analysed together by one process,
826
+ and a wave waits for its slowest component: a project whose cycles are large gains less.
827
+
758
828
  ## Plugins
759
829
 
760
830
  The package ships 35 plugins, loaded by default. Security models for the standard library
@@ -847,7 +917,10 @@ such as `metaclass=`, `with` targets of any assignable form and `match` with eve
847
917
  pattern kind. `finally` is modelled on the normal path only. A
848
918
  `nonlocal` write made by a nested function flows back to the enclosing variable when the
849
919
  enclosing function calls that nested function directly by name; a nested function passed
850
- around and called elsewhere keeps its writes to itself. Methods of module-level classes are
920
+ around and called elsewhere keeps its writes to itself. Where the nested function may
921
+ return without writing the name (no write on some path, a write constant propagation
922
+ proves cannot run, or an exception before it), the variable keeps the value it had
923
+ before the call, joined with what may be written; only a write on every path replaces it. Methods of module-level classes are
851
924
  analysed like functions.
852
925
 
853
926
  ## Looking at the intermediate representation
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "coretrace-python-analyzer"
7
- version = "0.22.0"
7
+ version = "0.23.0"
8
8
  description = "Static security analysis infrastructure for Python code"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,4 +1,4 @@
1
1
  """CoreTrace's Python static analysis frontend."""
2
2
 
3
- __version__ = "0.22.0"
3
+ __version__ = "0.23.0"
4
4
 
@@ -0,0 +1,121 @@
1
+ """Module-level names whose value is known (architecture §18): the names bound once and
2
+ never rebound, and among them the ones bound to an immutable collection of constants,
3
+ which an allowlist reads."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from collections.abc import Mapping
8
+ from types import MappingProxyType
9
+ from typing import ClassVar
10
+
11
+ from coretrace_python.analysis import Analysis, AnalysisContext, AnyAnalysis
12
+ from coretrace_python.cfg import CFGError
13
+ from coretrace_python.hir import nodes
14
+ from coretrace_python.ir.lowering import (
15
+ MODULE_BODY,
16
+ LoweringError,
17
+ PyIRAnalysis,
18
+ analyzable_functions,
19
+ )
20
+ from coretrace_python.ir.model import (
21
+ BuildDict,
22
+ BuildList,
23
+ BuildSet,
24
+ BuildTuple,
25
+ Call,
26
+ Constant,
27
+ ForNext,
28
+ Instruction,
29
+ SetGlobal,
30
+ StoreLocal,
31
+ Symbol,
32
+ Value,
33
+ )
34
+ from coretrace_python.semantic.symbols import SymbolId
35
+
36
+ FROZENSET = SymbolId("python.builtins.frozenset")
37
+
38
+
39
+ def module_bindings(ctx: AnalysisContext) -> tuple[Mapping[str, Instruction], Mapping[Value, Instruction]]:
40
+ """The module-level names bound once and never rebound, by no other statement of the
41
+ module body, no function assigning them as ``global``, no definition of that name,
42
+ each with the instruction that makes its value; and the definitions of the module
43
+ body's values. A module that does not lower entirely has none."""
44
+
45
+ try:
46
+ functions = [ctx.get(PyIRAnalysis, function) for function in analyzable_functions(ctx.module)]
47
+ except (LoweringError, CFGError):
48
+ return MappingProxyType({}), MappingProxyType({})
49
+ body = next((function for function in functions if function.name == MODULE_BODY), None)
50
+ if body is None:
51
+ return MappingProxyType({}), MappingProxyType({})
52
+ defs = {i.result: i for block in body.blocks for i in block.instructions if i.result is not None}
53
+ stored: dict[str, list[Value | None]] = {}
54
+ for block in body.blocks:
55
+ for instruction in block.instructions:
56
+ if isinstance(instruction, StoreLocal):
57
+ stored.setdefault(instruction.name, []).append(instruction.value)
58
+ if isinstance(block.terminator, ForNext):
59
+ # A ``for`` binds its target to each item.
60
+ stored.setdefault(block.terminator.target, []).append(None)
61
+ rebound = {
62
+ i.name for function in functions for block in function.blocks for i in block.instructions
63
+ if isinstance(i, SetGlobal)
64
+ }
65
+ rebound |= {s.name for s in ctx.module.body if isinstance(s, nodes.Function | nodes.Class)}
66
+ bound: dict[str, Instruction] = {}
67
+ for name, values in stored.items():
68
+ value = values[0] if len(values) == 1 and name not in rebound else None
69
+ made = defs.get(value) if value is not None else None
70
+ if made is not None:
71
+ bound[name] = made
72
+ return MappingProxyType(bound), MappingProxyType(defs)
73
+
74
+
75
+ def constant_members(display: Instruction | None, defs: Mapping[Value, Instruction]) -> bool:
76
+ """Whether ``display`` is a tuple, list or set display whose items are all constants,
77
+ or a dict display whose keys are, with nothing unpacked into it: the members a
78
+ membership test reads."""
79
+
80
+ if isinstance(display, BuildList | BuildTuple | BuildSet):
81
+ return not display.unpacked and all(isinstance(defs.get(e), Constant) for e in display.elements)
82
+ if isinstance(display, BuildDict):
83
+ return not display.unpacked and all(isinstance(defs.get(key), Constant) for key, _ in display.items)
84
+ return False
85
+
86
+
87
+ def converts(call: Call, defs: Mapping[Value, Instruction], symbols: frozenset[SymbolId]) -> Value | None:
88
+ """The one positional argument of ``call`` when it calls one of ``symbols`` with
89
+ nothing else (``frozenset(x)``), otherwise ``None``."""
90
+
91
+ callee = defs.get(call.callee)
92
+ if not isinstance(callee, Symbol) or callee.symbol_id not in symbols:
93
+ return None
94
+ if len(call.arguments) != 1 or call.keywords or call.starred:
95
+ return None
96
+ return call.arguments[0]
97
+
98
+
99
+ class ModuleCollectionsAnalysis(Analysis[frozenset[str]]):
100
+ """The module-level names bound once and never rebound (``module_bindings``) to an
101
+ immutable collection of constants: a tuple display, or ``frozenset`` of a display
102
+ whose members are constants (``constant_members``). A list, a set or a dict can still
103
+ change after it is bound, so it is not one."""
104
+
105
+ name: ClassVar[str] = "abstract.module_collections"
106
+ requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({PyIRAnalysis})
107
+
108
+ @classmethod
109
+ def compute(cls, ctx: AnalysisContext) -> frozenset[str]:
110
+ bound, defs = module_bindings(ctx)
111
+ found: set[str] = set()
112
+ for name, made in bound.items():
113
+ collection: Instruction | None = made
114
+ if isinstance(made, Call):
115
+ argument = converts(made, defs, frozenset({FROZENSET}))
116
+ collection = defs.get(argument) if argument is not None else None
117
+ elif not isinstance(made, BuildTuple):
118
+ continue
119
+ if constant_members(collection, defs):
120
+ found.add(name)
121
+ return frozenset(found)
@@ -0,0 +1,68 @@
1
+ """The constant text a string starts with (architecture §18): what ``+`` and f-strings
2
+ build from string literals and from module-level names bound once to one. Anything
3
+ else, a parameter, a call result, an import, ``%`` or ``format``, is text of unknown
4
+ value."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from collections.abc import Mapping
9
+ from types import MappingProxyType
10
+ from typing import ClassVar
11
+
12
+ from coretrace_python.abstract.module_constants import module_bindings
13
+ from coretrace_python.analysis import Analysis, AnalysisContext, AnyAnalysis
14
+ from coretrace_python.ir.lowering import PyIRAnalysis
15
+ from coretrace_python.ir.model import (
16
+ BinaryOp,
17
+ BuildString,
18
+ Constant,
19
+ Global,
20
+ Instruction,
21
+ Value,
22
+ )
23
+
24
+
25
+ def leading_text(value: Value, defs: Mapping[Value, Instruction], strings: Mapping[str, str]) -> tuple[str, bool]:
26
+ """The constant text ``value`` starts with, and whether that text is all of it, given
27
+ the definitions of the function's values and the module's ``strings``."""
28
+
29
+ made = defs.get(value)
30
+ if isinstance(made, Constant):
31
+ return (made.value, True) if isinstance(made.value, str) else ("", False)
32
+ if isinstance(made, Global):
33
+ known = strings.get(made.name)
34
+ return ("", False) if known is None else (known, True)
35
+ if isinstance(made, BinaryOp) and made.operator == "add":
36
+ left, whole = leading_text(made.left, defs, strings)
37
+ if not whole:
38
+ return left, False
39
+ right, whole = leading_text(made.right, defs, strings)
40
+ return left + right, whole
41
+ if isinstance(made, BuildString):
42
+ text = ""
43
+ for part in made.parts:
44
+ piece, whole = leading_text(part, defs, strings)
45
+ text += piece
46
+ if not whole:
47
+ return text, False
48
+ return text, True
49
+ return "", False
50
+
51
+
52
+ class ModuleStringsAnalysis(Analysis[Mapping[str, str]]):
53
+ """The module-level names bound once and never rebound (``module_bindings``) to a
54
+ string literal, with that literal."""
55
+
56
+ name: ClassVar[str] = "abstract.module_strings"
57
+ requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({PyIRAnalysis})
58
+
59
+ @classmethod
60
+ def compute(cls, ctx: AnalysisContext) -> Mapping[str, str]:
61
+ bound, _ = module_bindings(ctx)
62
+ return MappingProxyType(
63
+ {
64
+ name: made.value
65
+ for name, made in bound.items()
66
+ if isinstance(made, Constant) and isinstance(made.value, str)
67
+ }
68
+ )
@@ -62,6 +62,33 @@ class PythonStdlibModels(ModelPlugin):
62
62
  # The code is the first argument; the globals and locals it runs with are data.
63
63
  Sink(_sym("builtins.eval"), TaintKind.CODE, ((TaintKind.CODE, (0,)),)),
64
64
  Sink(_sym("builtins.exec"), TaintKind.CODE, ((TaintKind.CODE, (0,)),)),
65
+ # The module or the file the call imports or runs; its globals, its run name and
66
+ # the package of a relative name are data (#240).
67
+ *(
68
+ Sink(_sym(symbol), TaintKind.CODE, ((TaintKind.CODE, (0,)),), ((TaintKind.CODE, (keyword,)),), selects=True)
69
+ for symbol, keyword in (
70
+ ("importlib.import_module", "name"),
71
+ ("builtins.__import__", "name"),
72
+ ("importlib.__import__", "name"),
73
+ ("runpy.run_module", "mod_name"),
74
+ ("runpy.run_path", "path_name"),
75
+ )
76
+ ),
77
+ # The directory imports are looked up in, and the site directory whose ``.pth``
78
+ # files run; the index of ``insert`` is data (#240).
79
+ Sink(_sym("sys.path.insert"), TaintKind.CODE, ((TaintKind.CODE, (1,)),), selects=True),
80
+ Sink(_sym("sys.path.append"), TaintKind.CODE, ((TaintKind.CODE, (0,)),), selects=True),
81
+ Sink(_sym("sys.path.extend"), TaintKind.CODE, ((TaintKind.CODE, (0,)),), selects=True),
82
+ Sink(_sym("site.addsitedir"), TaintKind.CODE, ((TaintKind.CODE, (0,)),), ((TaintKind.CODE, ("sitedir",)),), selects=True),
83
+ # The name of the attribute ``getattr`` selects, when what it selects is called;
84
+ # the object and the default are data (#240).
85
+ Sink(
86
+ _sym("builtins.getattr"),
87
+ TaintKind.CODE,
88
+ ((TaintKind.CODE, (1,)),),
89
+ selects=True,
90
+ requires_called_result=TaintKind.CODE,
91
+ ),
65
92
  Sink(_sym("builtins.open"), TaintKind.PATH),
66
93
  Sink(_sym("os.remove"), TaintKind.PATH),
67
94
  Sink(_sym("os.unlink"), TaintKind.PATH),
@@ -9,6 +9,7 @@ from collections.abc import Sequence
9
9
  from typing import ClassVar
10
10
 
11
11
  from coretrace_python.findings import Finding
12
+ from coretrace_python.findings.context import path_in_project
12
13
  from coretrace_python.plugins import PluginContext, ProjectContext, ProjectPlugin
13
14
  from coretrace_python.plugins.secrets import (
14
15
  DEFAULT_CREDENTIAL_NAMES,
@@ -32,7 +33,7 @@ class ConfigSecrets(SecretDetector, ProjectPlugin):
32
33
  return ()
33
34
  findings: list[Finding] = []
34
35
  for value, name, span, function in config_literals(ctx.root):
35
- finding = self.judge(value, name, span, function)
36
+ finding = self.judge(value, name, span, function, path=path_in_project(span.source_id, ctx.root))
36
37
  if finding is not None:
37
38
  findings.append(finding)
38
39
  return findings