coretrace-python-analyzer 0.5.0__tar.gz → 0.6.0__tar.gz

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