coretrace-python-analyzer 0.4.0__tar.gz → 0.5.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 (254) hide show
  1. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/CHANGELOG.md +11 -0
  2. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/PKG-INFO +1 -1
  3. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/docs/plugins.md +7 -4
  4. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/docs/usage.md +25 -9
  5. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/pyproject.toml +1 -1
  6. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/__init__.py +1 -1
  7. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +5 -2
  8. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +1 -1
  9. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +32 -7
  10. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cache.py +37 -8
  11. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/advisories.py +22 -9
  12. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/correlation.py +81 -13
  13. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/graph.py +7 -2
  14. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/engine.py +9 -4
  15. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/__init__.py +4 -0
  16. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/callgraph.py +62 -9
  17. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/summaries.py +8 -1
  18. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/api.py +18 -4
  19. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/__init__.py +2 -0
  20. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/engine.py +76 -14
  21. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_advisory_evidence.py +8 -1
  22. coretrace_python_analyzer-0.5.0/tests/test_argument_conditions.py +217 -0
  23. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_cache.py +15 -4
  24. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_dependencies.py +1 -1
  25. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_heap.py +1 -1
  26. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_project.py +99 -0
  27. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_release.py +1 -1
  28. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/.github/workflows/ci.yml +0 -0
  29. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/.github/workflows/release.yml +0 -0
  30. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/.gitignore +0 -0
  31. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/LICENSE +0 -0
  32. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/NOTICE +0 -0
  33. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/README.md +0 -0
  34. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/docs/architecture.md +0 -0
  35. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/examples/add.py +0 -0
  36. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/examples/canonical_symbols.py +0 -0
  37. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/__main__.py +0 -0
  38. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/__init__.py +0 -0
  39. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/constants.py +0 -0
  40. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/heap.py +0 -0
  41. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/ranges.py +0 -0
  42. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/abstract/values.py +0 -0
  43. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/analysis/__init__.py +0 -0
  44. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/analysis/manager.py +0 -0
  45. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/analysis/provider.py +0 -0
  46. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
  47. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
  48. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
  49. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
  50. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
  51. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +0 -0
  52. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
  53. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +0 -0
  54. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
  55. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/cli/cli_models.py +0 -0
  56. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
  57. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
  58. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
  59. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
  60. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
  61. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/django/django_models.py +0 -0
  62. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
  63. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
  64. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
  65. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/flask/flask_models.py +0 -0
  66. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
  67. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
  68. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
  69. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
  70. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +0 -0
  71. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
  72. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
  73. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
  74. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +0 -0
  75. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
  76. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
  77. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
  78. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
  79. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
  80. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
  81. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
  82. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
  83. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
  84. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
  85. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
  86. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
  87. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
  88. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
  89. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
  90. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
  91. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
  92. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
  93. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
  94. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
  95. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
  96. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
  97. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
  98. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
  99. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
  100. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
  101. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
  102. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
  103. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
  104. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
  105. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
  106. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
  107. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
  108. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
  109. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
  110. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
  111. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
  112. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
  113. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/__init__.py +0 -0
  114. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/builder.py +0 -0
  115. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/dominance.py +0 -0
  116. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cfg/model.py +0 -0
  117. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/cli.py +0 -0
  118. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dataflow/__init__.py +0 -0
  119. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dataflow/lattice.py +0 -0
  120. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dataflow/solver.py +0 -0
  121. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/__init__.py +0 -0
  122. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/policy.py +0 -0
  123. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/dependency/sbom.py +0 -0
  124. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/__init__.py +0 -0
  125. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/baseline.py +0 -0
  126. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/coverage.py +0 -0
  127. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/model.py +0 -0
  128. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/refutation.py +0 -0
  129. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/findings/suppressions.py +0 -0
  130. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/frontend/__init__.py +0 -0
  131. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
  132. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/frontend/parser.py +0 -0
  133. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/hir/__init__.py +0 -0
  134. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/hir/nodes.py +0 -0
  135. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/hir/visitors.py +0 -0
  136. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
  137. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/__init__.py +0 -0
  138. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/defuse.py +0 -0
  139. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/lowering.py +0 -0
  140. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/model.py +0 -0
  141. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/printer.py +0 -0
  142. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/ir/ssa.py +0 -0
  143. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/__init__.py +0 -0
  144. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/detectors.py +0 -0
  145. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/loader.py +0 -0
  146. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/manifest.py +0 -0
  147. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/registry.py +0 -0
  148. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/plugins/secrets.py +0 -0
  149. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/__init__.py +0 -0
  150. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/json_format.py +0 -0
  151. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/report.py +0 -0
  152. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/sarif.py +0 -0
  153. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/reporters/text.py +0 -0
  154. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/__init__.py +0 -0
  155. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/identity.py +0 -0
  156. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/imports.py +0 -0
  157. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/scopes.py +0 -0
  158. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/semantic/symbols.py +0 -0
  159. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/source/__init__.py +0 -0
  160. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/source/manager.py +0 -0
  161. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/source/model.py +0 -0
  162. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/models.py +0 -0
  163. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/src/coretrace_python/taint/routes.py +0 -0
  164. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Chatbot.json +0 -0
  165. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/DjangoGoat.json +0 -0
  166. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
  167. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/InsecureGram.json +0 -0
  168. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
  169. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/NPL-IA.json +0 -0
  170. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
  171. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
  172. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/dvpwa.json +0 -0
  173. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
  174. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/healthchecks.json +0 -0
  175. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/httpie.json +0 -0
  176. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/luigi.json +0 -0
  177. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/my_gdb.json +0 -0
  178. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/network-topologer.json +0 -0
  179. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/pygoat.json +0 -0
  180. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
  181. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/vulnerable-flask.json +0 -0
  182. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/expected/yt-fts.json +0 -0
  183. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
  184. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
  185. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
  186. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
  187. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/regression/repositories.toml +0 -0
  188. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_advisory_feeds.py +0 -0
  189. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_aiohttp_models.py +0 -0
  190. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_analysis_manager.py +0 -0
  191. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_architecture.py +0 -0
  192. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_audit_detectors.py +0 -0
  193. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_baseline.py +0 -0
  194. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_cfg.py +0 -0
  195. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_class_keywords_with_targets.py +0 -0
  196. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_cli.py +0 -0
  197. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_closures.py +0 -0
  198. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_config_secrets_coverage.py +0 -0
  199. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_constants.py +0 -0
  200. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_correlation.py +0 -0
  201. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_credential_context.py +0 -0
  202. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_dataflow.py +0 -0
  203. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_detectors.py +0 -0
  204. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_discovery.py +0 -0
  205. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_django_http_clients.py +0 -0
  206. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_django_routes.py +0 -0
  207. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_docs.py +0 -0
  208. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_dominance.py +0 -0
  209. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_emit_ir.py +0 -0
  210. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_exceptions.py +0 -0
  211. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_factories_match_loop_else.py +0 -0
  212. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_fail_on.py +0 -0
  213. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_fastapi_cli_precision.py +0 -0
  214. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_frameworks.py +0 -0
  215. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_frontend_gaps.py +0 -0
  216. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_hex_digests.py +0 -0
  217. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_hir_builder.py +0 -0
  218. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_imports.py +0 -0
  219. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_interprocedural.py +0 -0
  220. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_interprocedural_taint.py +0 -0
  221. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_ir.py +0 -0
  222. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_match_patterns.py +0 -0
  223. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_module_level.py +0 -0
  224. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_nonlocal_writeback.py +0 -0
  225. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_operator_sources.py +0 -0
  226. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_packaging.py +0 -0
  227. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_parallel.py +0 -0
  228. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_parser.py +0 -0
  229. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_plaintext_credentials.py +0 -0
  230. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_plugins.py +0 -0
  231. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_pyir_imports.py +0 -0
  232. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_real_code_precision.py +0 -0
  233. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_redirect_targets.py +0 -0
  234. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_refutation.py +0 -0
  235. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_refutation_evidence.py +0 -0
  236. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_regression.py +0 -0
  237. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_relative_paths.py +0 -0
  238. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_remaining_limits.py +0 -0
  239. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_reporters.py +0 -0
  240. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_scale.py +0 -0
  241. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_scopes.py +0 -0
  242. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_secrets_sbom.py +0 -0
  243. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_self_state.py +0 -0
  244. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_source_manager.py +0 -0
  245. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_ssa.py +0 -0
  246. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_suppressions.py +0 -0
  247. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_symbol_resolution.py +0 -0
  248. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_symbols.py +0 -0
  249. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntactic_rules.py +0 -0
  250. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntax_control_flow.py +0 -0
  251. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntax_coverage.py +0 -0
  252. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_syntax_expressions.py +0 -0
  253. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_taint.py +0 -0
  254. {coretrace_python_analyzer-0.4.0 → coretrace_python_analyzer-0.5.0}/tests/test_tornado_bottle_models.py +0 -0
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 (2026-09-24)
4
+
5
+ ### Dependencies
6
+
7
+ - Argument conditions of advisory entry points are decided at each call, with a `position` for positional arguments and a `default` for absent ones: a call meeting them is reported with `conditions_met`, a call passing another known value does not reach the vulnerability and is listed in the requirement's `ruled_out`, and a call the analyzer cannot decide leaves them pending review (#110).
8
+
9
+ ### Plugins
10
+
11
+ - `ProjectContext.functions(module)` gives project plugins every function of a module as its call graph names it, with its span and the label of the entry point it is (`http` for a route, `argv` for a command), decided by the taint engine's own rule; modules served from the cache keep them, and the cache format is bumped (#116).
12
+ - Call sites and external calls record what each argument denotes — a symbol, a constant as Python writes it, or nothing known — in an `Arguments` record: `CallSite.arguments` (formerly the argument counts), `ExternalCall.arguments`, and `TaintFlow.sink_arguments` for the sink call of a flow, through callees in other modules too (#110).
13
+
3
14
  ## 0.4.0 (2026-09-24)
4
15
 
5
16
  ### Analysis
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: coretrace-python-analyzer
3
- Version: 0.4.0
3
+ Version: 0.5.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
@@ -222,9 +222,12 @@ rule. Findings carry a redacted preview only.
222
222
 
223
223
  Runs once per project after every module is analysed, with a `ProjectContext`: the
224
224
  module graph, the dependency graph, every advisory the plugins and advisory files
225
- contributed, the policy, the project root, and each module's imports and call graph. The
226
- shipped `vulnerable-dependency`, `reachable-vulnerability` and `dependency-policy` plugins
227
- are of this kind.
225
+ contributed, the policy, the project root, and each module's imports, call graph and
226
+ functions. `ctx.functions(module)` gives every function as the call graph names it
227
+ (`Admin.get`, `outer.inner`, `<module>`), with its span, so a finding can be placed in
228
+ 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.
228
231
 
229
232
  ```python
230
233
  from collections.abc import Sequence
@@ -238,7 +241,7 @@ class RequirementsPresent(ProjectPlugin):
238
241
  name: ClassVar[str] = "requirements-present"
239
242
 
240
243
  def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
241
- return () # inspect ctx.dependencies, ctx.modules(), ctx.call_graph(module)
244
+ return () # inspect ctx.dependencies, ctx.modules, ctx.call_graph(module), ctx.functions(module)
242
245
  ```
243
246
 
244
247
  ### Anything else: `Plugin`
@@ -201,7 +201,7 @@ the directory of the checked file; a path outside it is printed as it is.
201
201
  ```json
202
202
  {
203
203
  "schema_version": 1,
204
- "tool": {"name": "coretrace-python-analyzer", "version": "0.4.0"},
204
+ "tool": {"name": "coretrace-python-analyzer", "version": "0.5.0"},
205
205
  "root": "/home/me/project",
206
206
  "findings": [
207
207
  {
@@ -271,7 +271,8 @@ affected APIs that feed reachability and correlation:
271
271
  "symbol": "python.yaml.load",
272
272
  "justification": "same path when Loader is FullLoader",
273
273
  "conditions": [
274
- {"kind": "argument", "text": "Loader is FullLoader", "argument": "Loader", "values": ["python.yaml.FullLoader"]},
274
+ {"kind": "argument", "text": "Loader is FullLoader, the default", "argument": "Loader", "position": 1,
275
+ "values": ["python.yaml.FullLoader", "python.yaml.loader.FullLoader"], "default": true},
275
276
  {"kind": "semantic", "text": "the document carries a python/object/apply tag"}
276
277
  ]
277
278
  }
@@ -285,17 +286,32 @@ affected APIs that feed reachability and correlation:
285
286
 
286
287
  `affected_symbols` are the functions the fix changed; `entry_points` the public APIs
287
288
  through which a project reaches them, each with the justification that ties it to the
288
- fix — the commit, or the call path — and the `conditions` under which it is affected: an
289
- argument value the engine can check, or a `semantic` condition it cannot yet, kept on the
290
- finding as pending review rather than dropped. A call to either kind of symbol is
291
- reachable. `modules` names the top-level modules the package installs, so the analyzer
292
- can tell an imported package from a merely required one; it defaults to the package name.
289
+ fix — the commit, or the call path — and the `conditions` under which it is affected. A
290
+ call to either kind of symbol is reachable. `modules` names the top-level modules the
291
+ package installs, so the analyzer can tell an imported package from a merely required
292
+ one; it defaults to the package name.
293
+
294
+ An `argument` condition is decided at each call. It names the argument by keyword and,
295
+ when it may be passed positionally, by `position` (counted from 0, the receiver of a
296
+ method excluded); `values` are what makes the call affected, written as the analyzer
297
+ sees arguments: a symbol by its canonical name (`python.yaml.FullLoader`, and every
298
+ spelling a project may use, such as `python.yaml.loader.FullLoader`), a constant as
299
+ Python writes it (`True`, `None`, `'/static'`); `default` says an absent argument means
300
+ an affected value. A call passing another known value does not reach the vulnerability;
301
+ a call passing a variable, or unpacking `*args` or `**kwargs`, leaves the condition
302
+ pending review. A `semantic` condition — the document's tags, the platform, the
303
+ template's origin — cannot be decided and is always pending review, never dropped.
293
304
 
294
305
  Every dependency finding records the highest level of evidence established in its
295
306
  `level` metadata: `declared` (the requirement allows a vulnerable version), `imported`
296
307
  (a module of the package is imported somewhere), `reachable` (an entry point or affected
297
- symbol is called) or `exploitable` (attacker input reaches it), with `entry_point`,
298
- `justification`, `conditions` and `conditions_pending_review` on the last two.
308
+ symbol is called) or `exploitable` (attacker input reaches it). The last two carry
309
+ `entry_point`, `justification`, `conditions`, and which of them the call meets
310
+ (`conditions_met`) or leaves to review (`conditions_pending_review`). A requirement whose
311
+ entry points are only called with arguments that contradict a condition stays
312
+ `imported`, and its finding lists those calls in `ruled_out`
313
+ (`app.views:12 python.yaml.load(Loader=python.yaml.SafeLoader)`), the evidence that
314
+ they do not reach the vulnerability.
299
315
 
300
316
  A `coretrace-policy.toml` at the root, or the file passed with `--policy`, denies
301
317
  packages, requires pins and lists accepted advisories whose findings are dropped:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "coretrace-python-analyzer"
7
- version = "0.4.0"
7
+ version = "0.5.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.4.0"
3
+ __version__ = "0.5.0"
4
4
 
@@ -7,7 +7,7 @@ from typing import ClassVar
7
7
 
8
8
  from coretrace_python.analysis import AnyAnalysis
9
9
  from coretrace_python.dependency import DependencyAnalysis
10
- from coretrace_python.dependency.correlation import affected_symbols, evidence
10
+ from coretrace_python.dependency.correlation import affected_symbols, check_conditions, evidence
11
11
  from coretrace_python.findings import Confidence, Finding
12
12
  from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
13
13
  from coretrace_python.plugins import ProjectContext, ProjectPlugin
@@ -29,6 +29,9 @@ class ReachableVulnerabilityPlugin(ProjectPlugin):
29
29
  if not isinstance(site.target, ExternalSymbol):
30
30
  continue
31
31
  for advisory in affected.get(site.target.symbol, ()):
32
+ check = check_conditions(advisory.entry_point(site.target.symbol), site.arguments)
33
+ if check.contradicted is not None:
34
+ continue
32
35
  findings.append(
33
36
  Finding(
34
37
  rule_id="reachable-vulnerability",
@@ -40,7 +43,7 @@ class ReachableVulnerabilityPlugin(ProjectPlugin):
40
43
  confidence=Confidence.HIGH,
41
44
  span=site.location,
42
45
  function=function,
43
- metadata=evidence(advisory, site.target.symbol, "reachable"),
46
+ metadata=evidence(advisory, site.target.symbol, "reachable", check),
44
47
  )
45
48
  )
46
49
  return findings
@@ -1,7 +1,7 @@
1
1
  name = "vulnerable-dependency"
2
2
  version = "1.0.0"
3
3
  plugin_api = ">=1,<2"
4
- requires = ["dependency.graph"]
4
+ requires = ["dependency.graph", "interprocedural.callgraph"]
5
5
  provides = ["vulnerability.vulnerable-dependency"]
6
6
 
7
7
  [entrypoint]
@@ -7,23 +7,34 @@ from typing import ClassVar
7
7
 
8
8
  from coretrace_python.analysis import AnyAnalysis
9
9
  from coretrace_python.dependency import DependencyAnalysis
10
+ from coretrace_python.dependency.correlation import affected_symbols, check_conditions, ruled_out
10
11
  from coretrace_python.findings import Confidence, Finding
12
+ from coretrace_python.interprocedural import CallGraphAnalysis, ExternalSymbol
11
13
  from coretrace_python.plugins import ProjectContext, ProjectPlugin
12
14
 
13
15
 
14
16
  class VulnerableDependencyPlugin(ProjectPlugin):
15
17
  name: ClassVar[str] = "vulnerable-dependency"
16
- requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis})
18
+ requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({DependencyAnalysis, CallGraphAnalysis})
17
19
 
18
20
  def analyze_project(self, ctx: ProjectContext) -> Sequence[Finding]:
19
21
  findings: list[Finding] = []
20
22
  imported = [s for module in ctx.modules for s in ctx.imports(module).all_symbols()]
23
+ excluded = _ruled_out(ctx)
21
24
  for requirement in ctx.dependencies.requirements:
22
25
  for advisory in ctx.advisories:
23
26
  if not advisory.affects(requirement):
24
27
  continue
25
28
  pinned = requirement.pinned is not None
26
29
  level = "imported" if advisory.imported_by(imported) else "declared"
30
+ metadata = {
31
+ "advisory": advisory.id,
32
+ "package": advisory.package,
33
+ "specifier": requirement.specifier,
34
+ "level": level,
35
+ }
36
+ if excluded.get(advisory.id):
37
+ metadata["ruled_out"] = "; ".join(excluded[advisory.id])
27
38
  findings.append(
28
39
  Finding(
29
40
  rule_id="vulnerable-dependency",
@@ -35,12 +46,26 @@ class VulnerableDependencyPlugin(ProjectPlugin):
35
46
  severity=advisory.severity,
36
47
  confidence=Confidence.HIGH if pinned else Confidence.MEDIUM,
37
48
  span=requirement.span,
38
- metadata={
39
- "advisory": advisory.id,
40
- "package": advisory.package,
41
- "specifier": requirement.specifier,
42
- "level": level,
43
- },
49
+ metadata=metadata,
44
50
  )
45
51
  )
46
52
  return findings
53
+
54
+
55
+ def _ruled_out(ctx: ProjectContext) -> dict[str, list[str]]:
56
+ """By advisory, the calls to its entry points whose arguments contradict one of its
57
+ conditions: the evidence that the requirement, imported, is not reached there."""
58
+
59
+ affected = affected_symbols(ctx.dependencies, ctx.advisories)
60
+ excluded: dict[str, list[str]] = {}
61
+ for module in sorted(ctx.modules):
62
+ graph = ctx.call_graph(module)
63
+ for function in graph.functions:
64
+ for site in graph.sites(function):
65
+ if not isinstance(site.target, ExternalSymbol):
66
+ continue
67
+ for advisory in affected.get(site.target.symbol, ()):
68
+ check = check_conditions(advisory.entry_point(site.target.symbol), site.arguments)
69
+ if check.contradicted is not None:
70
+ excluded.setdefault(advisory.id, []).append(ruled_out(module, site, check))
71
+ return excluded
@@ -22,11 +22,13 @@ from typing import Any
22
22
 
23
23
  from coretrace_python.findings import Confidence, Finding, Severity
24
24
  from coretrace_python.interprocedural import (
25
+ Arguments,
25
26
  CallSite,
26
27
  ExternalCall,
27
28
  ExternalSymbol,
28
29
  FunctionSummary,
29
30
  KnownFunction,
31
+ ModuleFunction,
30
32
  ModuleGraph,
31
33
  Mutation,
32
34
  NonlocalWrite,
@@ -37,14 +39,14 @@ from coretrace_python.interprocedural import (
37
39
  from coretrace_python.semantic.symbols import SymbolId
38
40
  from coretrace_python.source import SourceId, SourceSpan
39
41
 
40
- CACHE_FORMAT = 5
42
+ CACHE_FORMAT = 7
41
43
 
42
44
 
43
45
  @dataclass(frozen=True)
44
46
  class CachedModule:
45
47
  """Everything a later run needs from one module without lowering it again."""
46
48
 
47
- functions: tuple[str, ...]
49
+ functions: tuple[ModuleFunction, ...]
48
50
  summaries: Mapping[str, FunctionSummary]
49
51
  sites: tuple[CallSite, ...]
50
52
  findings: tuple[Finding, ...]
@@ -128,7 +130,7 @@ class ProjectCache:
128
130
  def encode(module: CachedModule) -> dict[str, Any]:
129
131
  return {
130
132
  "format": CACHE_FORMAT,
131
- "functions": list(module.functions),
133
+ "functions": [[f.name, _encode_span(f.span), f.entry_point] for f in module.functions],
132
134
  "summaries": {name: _encode_summary(s) for name, s in module.summaries.items()},
133
135
  "sites": [_encode_site(site) for site in module.sites],
134
136
  "findings": [_encode_finding(finding) for finding in module.findings],
@@ -139,7 +141,7 @@ def decode(data: Mapping[str, Any]) -> CachedModule:
139
141
  if data["format"] != CACHE_FORMAT:
140
142
  raise ValueError(f"unsupported cache format {data['format']!r}")
141
143
  return CachedModule(
142
- tuple(_string(name) for name in data["functions"]),
144
+ tuple(_decode_function(function) for function in data["functions"]),
143
145
  {_string(name): _decode_summary(s) for name, s in data["summaries"].items()},
144
146
  tuple(_decode_site(site) for site in data["sites"]),
145
147
  tuple(_decode_finding(finding) for finding in data["findings"]),
@@ -187,6 +189,11 @@ def _decode_span(data: Any) -> SourceSpan:
187
189
  )
188
190
 
189
191
 
192
+ def _decode_function(data: Any) -> ModuleFunction:
193
+ name, span, entry_point = data
194
+ return ModuleFunction(_string(name), _decode_span(span), None if entry_point is None else _string(entry_point))
195
+
196
+
190
197
  def _encode_finding(finding: Finding) -> dict[str, Any]:
191
198
  return {
192
199
  "rule": finding.rule_id,
@@ -219,6 +226,7 @@ def _encode_call(call: ExternalCall) -> dict[str, Any]:
219
226
  "keywords": sorted(call.keyword_dependencies),
220
227
  "location": _encode_span(call.location),
221
228
  "call_site": None if call.call_site is None else _encode_span(call.call_site),
229
+ "given": _encode_arguments(call.arguments),
222
230
  }
223
231
 
224
232
 
@@ -230,6 +238,7 @@ def _decode_call(data: Mapping[str, Any]) -> ExternalCall:
230
238
  _indices(data["keywords"]),
231
239
  _decode_span(data["location"]),
232
240
  None if site is None else _decode_span(site),
241
+ _decode_arguments(data["given"]),
233
242
  )
234
243
 
235
244
 
@@ -311,8 +320,7 @@ def _encode_site(site: CallSite) -> dict[str, Any]:
311
320
  "caller": site.caller,
312
321
  "location": _encode_span(site.location),
313
322
  "target": _encode_target(site.target),
314
- "arguments": site.arguments,
315
- "keywords": site.keywords,
323
+ "arguments": _encode_arguments(site.arguments),
316
324
  }
317
325
 
318
326
 
@@ -321,6 +329,27 @@ def _decode_site(data: Mapping[str, Any]) -> CallSite:
321
329
  _string(data["caller"]),
322
330
  _decode_span(data["location"]),
323
331
  _decode_target(data["target"]),
324
- _integer(data["arguments"]),
325
- _integer(data["keywords"]),
332
+ _decode_arguments(data["arguments"]),
333
+ )
334
+
335
+
336
+ def _encode_arguments(arguments: Arguments) -> dict[str, Any]:
337
+ return {
338
+ "positional": list(arguments.positional),
339
+ "keywords": [[name, value] for name, value in arguments.keywords],
340
+ "unpacked": arguments.unpacked,
341
+ }
342
+
343
+
344
+ def _decode_arguments(data: Mapping[str, Any]) -> Arguments:
345
+ def denoted(value: Any) -> str | None:
346
+ return None if value is None else _string(value)
347
+
348
+ unpacked = data["unpacked"]
349
+ if not isinstance(unpacked, bool):
350
+ raise TypeError(f"expected a boolean, got {unpacked!r}")
351
+ return Arguments(
352
+ tuple(denoted(value) for value in data["positional"]),
353
+ tuple((_string(name), denoted(value)) for name, value in data["keywords"]),
354
+ unpacked,
326
355
  )
@@ -160,6 +160,10 @@ def _condition_entry(condition: Condition) -> dict[str, Any]:
160
160
  entry["argument"] = condition.argument
161
161
  if condition.values:
162
162
  entry["values"] = list(condition.values)
163
+ if condition.position is not None:
164
+ entry["position"] = condition.position
165
+ if condition.default:
166
+ entry["default"] = True
163
167
  return entry
164
168
 
165
169
 
@@ -193,13 +197,22 @@ def _entry_point(entry: Mapping[str, Any]) -> AdvisoryEntryPoint:
193
197
  return AdvisoryEntryPoint(
194
198
  SymbolId(str(entry["symbol"])),
195
199
  str(entry["justification"]),
196
- tuple(
197
- Condition(
198
- str(c["kind"]),
199
- str(c["text"]),
200
- None if c.get("argument") is None else str(c["argument"]),
201
- tuple(str(v) for v in c.get("values") or []),
202
- )
203
- for c in entry.get("conditions") or []
204
- ),
200
+ tuple(_condition(c) for c in entry.get("conditions") or []),
201
+ )
202
+
203
+
204
+ def _condition(entry: Mapping[str, Any]) -> Condition:
205
+ position = entry.get("position")
206
+ if position is not None and (not isinstance(position, int) or isinstance(position, bool) or position < 0):
207
+ raise TypeError(f"condition position must be a non-negative integer, got {position!r}")
208
+ default = entry.get("default", False)
209
+ if not isinstance(default, bool):
210
+ raise TypeError(f"condition default must be true or false, got {default!r}")
211
+ return Condition(
212
+ str(entry["kind"]),
213
+ str(entry["text"]),
214
+ None if entry.get("argument") is None else str(entry["argument"]),
215
+ tuple(str(v) for v in entry.get("values") or []),
216
+ position,
217
+ default,
205
218
  )
@@ -10,10 +10,18 @@ are correlated here into one high-confidence ``exploitable-vulnerability`` findi
10
10
  from __future__ import annotations
11
11
 
12
12
  from collections.abc import Iterable, Mapping
13
-
14
- from coretrace_python.dependency.graph import DIRECT, Advisory, DependencyGraph
13
+ from dataclasses import dataclass
14
+
15
+ from coretrace_python.dependency.graph import (
16
+ DIRECT,
17
+ Advisory,
18
+ AdvisoryEntryPoint,
19
+ Condition,
20
+ DependencyGraph,
21
+ )
15
22
  from coretrace_python.findings import Confidence, Finding, Severity
16
23
  from coretrace_python.findings.refutation import Status, Verdict, Verdicts
24
+ from coretrace_python.interprocedural import Arguments, CallSite, ExternalSymbol
17
25
  from coretrace_python.semantic.symbols import SymbolId
18
26
  from coretrace_python.taint import Sink, TaintFlow, TaintKind
19
27
 
@@ -40,10 +48,68 @@ def advisory_sinks(affected: Affected) -> tuple[Sink, ...]:
40
48
  return tuple(Sink(symbol, TaintKind.ADVISORY) for symbol in affected)
41
49
 
42
50
 
43
- def evidence(advisory: Advisory, symbol: SymbolId, level: str) -> dict[str, str]:
51
+ @dataclass(frozen=True)
52
+ class ConditionCheck:
53
+ """What one call tells of an entry point's conditions: those it meets, those left to
54
+ review (every semantic one, and every argument one the call does not decide), and
55
+ the argument condition it contradicts with what it passes instead (None: absent),
56
+ in which case the call does not reach the vulnerability."""
57
+
58
+ met: tuple[Condition, ...] = ()
59
+ pending: tuple[Condition, ...] = ()
60
+ contradicted: Condition | None = None
61
+ passed: str | None = None
62
+
63
+
64
+ def check_conditions(entry: AdvisoryEntryPoint | None, arguments: Arguments | None) -> ConditionCheck:
65
+ """Decide ``entry``'s conditions against what the call's ``arguments`` denote."""
66
+
67
+ if entry is None:
68
+ return ConditionCheck()
69
+ met: list[Condition] = []
70
+ pending: list[Condition] = []
71
+ for condition in entry.conditions:
72
+ given = _given(condition, arguments) if condition.checkable and arguments is not None else None
73
+ if given is None:
74
+ pending.append(condition)
75
+ continue
76
+ explicit, value = given
77
+ if explicit and value is None:
78
+ pending.append(condition)
79
+ continue
80
+ affected = value in condition.values if explicit else condition.default
81
+ if not affected:
82
+ return ConditionCheck(tuple(met), tuple(pending), condition, value)
83
+ met.append(condition)
84
+ return ConditionCheck(tuple(met), tuple(pending))
85
+
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
+ def ruled_out(module: str, site: CallSite, check: ConditionCheck) -> str:
100
+ """Which call a contradicted condition rules out, and why: ``app:12
101
+ python.yaml.load(Loader=python.yaml.SafeLoader)``."""
102
+
103
+ assert check.contradicted is not None and isinstance(site.target, ExternalSymbol)
104
+ argument = check.contradicted.argument or f"#{check.contradicted.position}"
105
+ passed = f"{argument} absent" if check.passed is None else f"{argument}={check.passed}"
106
+ return f"{module}:{site.location.start_line} {site.target.symbol}({passed})"
107
+
108
+
109
+ def evidence(advisory: Advisory, symbol: SymbolId, level: str, check: ConditionCheck) -> dict[str, str]:
44
110
  """What a finding keeps of the advisory for ``symbol``: the level of evidence
45
- established, how the symbol relates to the vulnerability and under which
46
- conditions, the ones the engine could not check listed as pending review."""
111
+ established, how the symbol relates to the vulnerability, and its conditions — those
112
+ the call meets and those left to review."""
47
113
 
48
114
  metadata = {"advisory": advisory.id, "package": advisory.package, "symbol": str(symbol), "level": level}
49
115
  entry = advisory.entry_point(symbol)
@@ -54,9 +120,10 @@ def evidence(advisory: Advisory, symbol: SymbolId, level: str) -> dict[str, str]
54
120
  metadata["justification"] = entry.justification
55
121
  if entry.conditions:
56
122
  metadata["conditions"] = "; ".join(c.text for c in entry.conditions)
57
- # ponytail: no condition is checked yet, so every one awaits review; argument
58
- # conditions get checked at the call site once call sites carry argument symbols.
59
- metadata["conditions_pending_review"] = "; ".join(c.text for c in entry.conditions)
123
+ if check.met:
124
+ metadata["conditions_met"] = "; ".join(c.text for c in check.met)
125
+ if check.pending:
126
+ metadata["conditions_pending_review"] = "; ".join(c.text for c in check.pending)
60
127
  return metadata
61
128
 
62
129
 
@@ -77,21 +144,22 @@ def correlate(
77
144
  if verdict is not None and verdict.status is Status.REFUTED:
78
145
  continue
79
146
  hotspot = verdict is not None and verdict.status is Status.HOTSPOT
80
- findings.extend(
81
- _exploitable(function, flow, advisory, verdict, hotspot) for advisory in affected.get(flow.sink.symbol, ())
82
- )
147
+ for advisory in affected.get(flow.sink.symbol, ()):
148
+ check = check_conditions(advisory.entry_point(flow.sink.symbol), flow.sink_arguments)
149
+ if check.contradicted is None:
150
+ findings.append(_exploitable(function, flow, advisory, verdict, hotspot, check))
83
151
  return tuple(findings)
84
152
 
85
153
 
86
154
  def _exploitable(
87
- function: str, flow: TaintFlow, advisory: Advisory, verdict: Verdict | None, hotspot: bool
155
+ function: str, flow: TaintFlow, advisory: Advisory, verdict: Verdict | None, hotspot: bool, check: ConditionCheck
88
156
  ) -> Finding:
89
157
  message = (
90
158
  f"{advisory.id}: {flow.source.label} input reaches {flow.sink.symbol}, affected in "
91
159
  f"the required {advisory.package} {advisory.vulnerable}: {advisory.summary}"
92
160
  )
93
161
  metadata = {
94
- **evidence(advisory, flow.sink.symbol, "exploitable"),
162
+ **evidence(advisory, flow.sink.symbol, "exploitable", check),
95
163
  "source": str(flow.source.symbol),
96
164
  "source_label": flow.source.label,
97
165
  "verdict": "hotspot" if hotspot else "vulnerability",
@@ -147,13 +147,18 @@ class Condition:
147
147
  """What must hold for an entry point to reach, or exploit, the affected code: the
148
148
  value of an argument, the loader or algorithm used, the format decoded, the kind of
149
149
  input, a configuration. ``kind`` says whether the engine can check it: an
150
- ``argument`` condition names the argument and the values that satisfy it; a
151
- ``semantic`` condition cannot be checked yet and is reported as pending review."""
150
+ ``argument`` condition names the argument by keyword, and by ``position`` when it may
151
+ be passed positionally, and lists the ``values`` that satisfy it — symbols
152
+ (``python.yaml.FullLoader``) or constants as Python writes them (``True``);
153
+ ``default`` says an absent argument means a vulnerable value. A ``semantic``
154
+ condition cannot be checked and is reported as pending review."""
152
155
 
153
156
  kind: str
154
157
  text: str
155
158
  argument: str | None = None
156
159
  values: tuple[str, ...] = ()
160
+ position: int | None = None
161
+ default: bool = False
157
162
 
158
163
  @property
159
164
  def checkable(self) -> bool:
@@ -71,6 +71,7 @@ from coretrace_python.interprocedural import (
71
71
  CallGraphAnalysis,
72
72
  CallSite,
73
73
  FunctionSummary,
74
+ ModuleFunction,
74
75
  ModuleGraph,
75
76
  ProjectSummaries,
76
77
  SummaryAnalysis,
@@ -106,6 +107,7 @@ from coretrace_python.semantic.symbols import SymbolAnalysis, SymbolId
106
107
  from coretrace_python.source import SourceFile, SourceId, SourceManager, SourceSpan, decode_text
107
108
  from coretrace_python.taint import (
108
109
  EntryPoint,
110
+ EntryPointAnalysis,
109
111
  ModelTable,
110
112
  RegisteredRoutes,
111
113
  Routes,
@@ -139,6 +141,7 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
139
141
  ProjectSummaries,
140
142
  SecurityModelAnalysis,
141
143
  TaintAnalysis,
144
+ EntryPointAnalysis,
142
145
  RefutationAnalysis,
143
146
  DependencyAnalysis,
144
147
  RegisteredRoutes,
@@ -147,7 +150,7 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
147
150
 
148
151
  # Named so the set stays right whichever analyses a build registers.
149
152
  _PROJECT_DEPENDANT_NAMES = frozenset(
150
- {"interprocedural.project", "interprocedural.summaries", "taint.flows", "findings.refutation"}
153
+ {"interprocedural.project", "interprocedural.summaries", "taint.flows", "taint.entry_points", "findings.refutation"}
151
154
  )
152
155
  _PROJECT_DEPENDANTS: frozenset[AnyAnalysis] = frozenset(
153
156
  a for a in ALL_ANALYSES if a.name in _PROJECT_DEPENDANT_NAMES
@@ -451,6 +454,7 @@ def analyze_project(
451
454
 
452
455
  index = _seed(results, graph, frozenset())
453
456
  call_graphs: dict[str, CallGraph] = {}
457
+ functions: dict[str, tuple[ModuleFunction, ...]] = {}
454
458
  for name in sorted(analysable):
455
459
  entry = results[name]
456
460
  findings.extend(entry.findings)
@@ -458,11 +462,12 @@ def analyze_project(
458
462
  coverage.append(
459
463
  FileCoverage(str(files[name].source_id), "analysed", len(entry.functions), len(entry.functions) - unsupported)
460
464
  )
461
- sites: dict[str, list[CallSite]] = {function: [] for function in entry.functions}
465
+ functions[name] = entry.functions
466
+ sites: dict[str, list[CallSite]] = {function.name: [] for function in entry.functions}
462
467
  for site in entry.sites:
463
468
  sites.setdefault(site.caller, []).append(site)
464
469
  call_graphs[name] = CallGraph({}, {f: tuple(s) for f, s in sites.items()}, frozenset())
465
- context = ProjectContext(graph, dependencies, advisories, analysable, call_graphs, policy, root)
470
+ context = ProjectContext(graph, dependencies, advisories, analysable, call_graphs, policy, root, functions)
466
471
  for plugin in all_plugins:
467
472
  if isinstance(plugin, ProjectPlugin):
468
473
  findings.extend(plugin.analyze_project(context))
@@ -662,7 +667,7 @@ def _analyse_module(
662
667
  )
663
668
  )
664
669
  return CachedModule(
665
- graph.functions,
670
+ manager.get(EntryPointAnalysis),
666
671
  _summaries_of(manager),
667
672
  tuple(site for function in graph.functions for site in graph.sites(function)),
668
673
  (*findings, *correlated),
@@ -1,11 +1,13 @@
1
1
  """Call graph and function summaries (architecture §19, §20)."""
2
2
 
3
3
  from coretrace_python.interprocedural.callgraph import (
4
+ Arguments,
4
5
  CallGraph,
5
6
  CallGraphAnalysis,
6
7
  CallSite,
7
8
  ExternalSymbol,
8
9
  KnownFunction,
10
+ ModuleFunction,
9
11
  Target,
10
12
  UnknownTarget,
11
13
  )
@@ -28,6 +30,7 @@ from coretrace_python.interprocedural.summaries import (
28
30
  )
29
31
 
30
32
  __all__ = [
33
+ "Arguments",
31
34
  "CallGraph",
32
35
  "CallGraphAnalysis",
33
36
  "CallSite",
@@ -35,6 +38,7 @@ __all__ = [
35
38
  "ExternalSymbol",
36
39
  "FunctionSummary",
37
40
  "KnownFunction",
41
+ "ModuleFunction",
38
42
  "ModuleGraph",
39
43
  "Mutation",
40
44
  "NonlocalWrite",