coretrace-python-analyzer 0.11.0__tar.gz → 0.13.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 (277) hide show
  1. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/CHANGELOG.md +23 -0
  2. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/PKG-INFO +1 -1
  3. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/docs/plugins.md +3 -1
  4. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/docs/usage.md +4 -3
  5. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/pyproject.toml +1 -1
  6. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/__init__.py +1 -1
  7. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/aiohttp/aiohttp_models.py +22 -10
  8. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/bottle/bottle_models.py +10 -6
  9. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/cli/cli_models.py +4 -4
  10. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/django/django_models.py +19 -0
  11. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/flask/flask_models.py +12 -9
  12. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/python_stdlib/python_stdlib.py +5 -3
  13. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/tornado/tornado_models.py +25 -5
  14. coretrace_python_analyzer-0.13.0/src/coretrace_python/bundled/security/nosql_injection/nosql_injection.py +20 -0
  15. coretrace_python_analyzer-0.13.0/src/coretrace_python/bundled/security/nosql_injection/plugin.toml +9 -0
  16. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/engine.py +7 -1
  17. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/callgraph.py +14 -12
  18. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/lowering.py +1 -1
  19. coretrace_python_analyzer-0.13.0/src/coretrace_python/semantic/__init__.py +9 -0
  20. coretrace_python_analyzer-0.13.0/src/coretrace_python/semantic/symbols.py +177 -0
  21. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/__init__.py +6 -0
  22. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/engine.py +105 -15
  23. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/models.py +41 -1
  24. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_analysis_manager.py +3 -2
  25. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_detectors.py +2 -1
  26. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_http_clients.py +1 -0
  27. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_docs.py +1 -1
  28. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_frameworks.py +47 -2
  29. coretrace_python_analyzer-0.13.0/tests/test_members.py +206 -0
  30. coretrace_python_analyzer-0.13.0/tests/test_nosql_injection.py +332 -0
  31. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_operator_sources.py +6 -4
  32. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_packaging.py +2 -2
  33. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_release.py +1 -1
  34. coretrace_python_analyzer-0.11.0/src/coretrace_python/semantic/__init__.py +0 -9
  35. coretrace_python_analyzer-0.11.0/src/coretrace_python/semantic/symbols.py +0 -87
  36. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/ci.yml +0 -0
  37. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/.github/workflows/release.yml +0 -0
  38. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/.gitignore +0 -0
  39. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/LICENSE +0 -0
  40. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/NOTICE +0 -0
  41. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/README.md +0 -0
  42. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/docs/architecture.md +0 -0
  43. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/examples/add.py +0 -0
  44. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/examples/canonical_symbols.py +0 -0
  45. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/__main__.py +0 -0
  46. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/__init__.py +0 -0
  47. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/constants.py +0 -0
  48. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/heap.py +0 -0
  49. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/ranges.py +0 -0
  50. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/abstract/values.py +0 -0
  51. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/__init__.py +0 -0
  52. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/manager.py +0 -0
  53. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/analysis/provider.py +0 -0
  54. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/dependency_policy/dependency_policy.py +0 -0
  55. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/dependency_policy/plugin.toml +0 -0
  56. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/plugin.toml +0 -0
  57. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/reachable_vulnerability/reachable_vulnerability.py +0 -0
  58. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/sample_advisories/plugin.toml +0 -0
  59. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py +0 -0
  60. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml +0 -0
  61. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py +0 -0
  62. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/aiohttp/plugin.toml +0 -0
  63. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/bottle/plugin.toml +0 -0
  64. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/cli/plugin.toml +0 -0
  65. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/credentials/credential_models.py +0 -0
  66. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/credentials/plugin.toml +0 -0
  67. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/db_drivers/db_driver_models.py +0 -0
  68. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/db_drivers/plugin.toml +0 -0
  69. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/django/plugin.toml +0 -0
  70. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/fastapi/fastapi_models.py +0 -0
  71. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/fastapi/plugin.toml +0 -0
  72. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/flask/plugin.toml +0 -0
  73. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/http_clients/http_client_models.py +0 -0
  74. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/http_clients/plugin.toml +0 -0
  75. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/python_stdlib/plugin.toml +0 -0
  76. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/sqlalchemy/plugin.toml +0 -0
  77. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py +0 -0
  78. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/models/tornado/plugin.toml +0 -0
  79. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/config_secrets/config_secrets.py +0 -0
  80. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/config_secrets/plugin.toml +0 -0
  81. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py +0 -0
  82. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/secrets/hardcoded_secrets/plugin.toml +0 -0
  83. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/code_injection/code_injection.py +0 -0
  84. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/code_injection/plugin.toml +0 -0
  85. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/command_injection/command_injection.py +0 -0
  86. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/command_injection/plugin.toml +0 -0
  87. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/insecure_deserialization/insecure_deserialization.py +0 -0
  88. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/insecure_deserialization/plugin.toml +0 -0
  89. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/open_redirect/open_redirect.py +0 -0
  90. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/open_redirect/plugin.toml +0 -0
  91. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/path_traversal/path_traversal.py +0 -0
  92. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/path_traversal/plugin.toml +0 -0
  93. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py +0 -0
  94. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/plaintext_credentials/plugin.toml +0 -0
  95. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/sql_injection/plugin.toml +0 -0
  96. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/sql_injection/sql_injection.py +0 -0
  97. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/ssrf/plugin.toml +0 -0
  98. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/ssrf/ssrf.py +0 -0
  99. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/xss/plugin.toml +0 -0
  100. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/security/xss/xss.py +0 -0
  101. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py +0 -0
  102. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/dangerous_eval/plugin.toml +0 -0
  103. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/flask_debug/flask_debug.py +0 -0
  104. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/flask_debug/plugin.toml +0 -0
  105. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/insecure_tempfile.py +0 -0
  106. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tempfile/plugin.toml +0 -0
  107. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tls/insecure_tls.py +0 -0
  108. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/insecure_tls/plugin.toml +0 -0
  109. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/missing_timeout/missing_timeout.py +0 -0
  110. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/missing_timeout/plugin.toml +0 -0
  111. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_archive/plugin.toml +0 -0
  112. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_archive/unsafe_archive.py +0 -0
  113. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_xml/plugin.toml +0 -0
  114. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/unsafe_xml/unsafe_xml.py +0 -0
  115. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_crypto/plugin.toml +0 -0
  116. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_crypto/weak_crypto.py +0 -0
  117. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_random/plugin.toml +0 -0
  118. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/bundled/syntax/weak_random/weak_random.py +0 -0
  119. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cache.py +0 -0
  120. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/__init__.py +0 -0
  121. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/builder.py +0 -0
  122. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/dominance.py +0 -0
  123. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cfg/model.py +0 -0
  124. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/cli.py +0 -0
  125. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/__init__.py +0 -0
  126. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/lattice.py +0 -0
  127. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dataflow/solver.py +0 -0
  128. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/__init__.py +0 -0
  129. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/advisories.py +0 -0
  130. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/correlation.py +0 -0
  131. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/graph.py +0 -0
  132. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/policy.py +0 -0
  133. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/sbom.py +0 -0
  134. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/dependency/vex.py +0 -0
  135. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/__init__.py +0 -0
  136. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/baseline.py +0 -0
  137. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/coverage.py +0 -0
  138. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/model.py +0 -0
  139. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/provenance.py +0 -0
  140. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/refutation.py +0 -0
  141. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/findings/suppressions.py +0 -0
  142. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/__init__.py +0 -0
  143. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/ast_adapter.py +0 -0
  144. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/frontend/parser.py +0 -0
  145. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/__init__.py +0 -0
  146. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/nodes.py +0 -0
  147. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/hir/visitors.py +0 -0
  148. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/__init__.py +0 -0
  149. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/modulegraph.py +0 -0
  150. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/interprocedural/summaries.py +0 -0
  151. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/__init__.py +0 -0
  152. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/defuse.py +0 -0
  153. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/model.py +0 -0
  154. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/printer.py +0 -0
  155. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/ir/ssa.py +0 -0
  156. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/__init__.py +0 -0
  157. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/api.py +0 -0
  158. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/detectors.py +0 -0
  159. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/loader.py +0 -0
  160. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/manifest.py +0 -0
  161. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/registry.py +0 -0
  162. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/plugins/secrets.py +0 -0
  163. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/__init__.py +0 -0
  164. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/json_format.py +0 -0
  165. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/report.py +0 -0
  166. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/sarif.py +0 -0
  167. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/reporters/text.py +0 -0
  168. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/identity.py +0 -0
  169. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/imports.py +0 -0
  170. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/semantic/scopes.py +0 -0
  171. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/__init__.py +0 -0
  172. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/manager.py +0 -0
  173. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/source/model.py +0 -0
  174. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/routes.py +0 -0
  175. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/src/coretrace_python/taint/templates.py +0 -0
  176. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Chatbot.json +0 -0
  177. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/DjangoGoat.json +0 -0
  178. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Flask-Image-Editing-Website.json +0 -0
  179. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/InsecureGram.json +0 -0
  180. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/MiniBookApiServer.json +0 -0
  181. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/NPL-IA.json +0 -0
  182. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Spam-Detection-Project.json +0 -0
  183. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/Vulnerable_fastapi.json +0 -0
  184. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/dvpwa.json +0 -0
  185. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/full-stack-fastapi-template.json +0 -0
  186. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/healthchecks.json +0 -0
  187. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/httpie.json +0 -0
  188. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/luigi.json +0 -0
  189. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/my_gdb.json +0 -0
  190. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/network-topologer.json +0 -0
  191. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/pygoat.json +0 -0
  192. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/uploadbox-cloud-storage-solution.json +0 -0
  193. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/vulnerable-flask.json +0 -0
  194. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/expected/yt-fts.json +0 -0
  195. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/README.md +0 -0
  196. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/app.py +0 -0
  197. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/requirements.txt +0 -0
  198. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/fixtures/vulnerable-flask/vulnerable_services.py +0 -0
  199. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/regression/repositories.toml +0 -0
  200. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_advisory_evidence.py +0 -0
  201. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_advisory_feeds.py +0 -0
  202. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_aiohttp_models.py +0 -0
  203. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_architecture.py +0 -0
  204. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_argument_conditions.py +0 -0
  205. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_attacker_arguments.py +0 -0
  206. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_audit_detectors.py +0 -0
  207. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_baseline.py +0 -0
  208. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_cache.py +0 -0
  209. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_cfg.py +0 -0
  210. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_class_keywords_with_targets.py +0 -0
  211. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_cli.py +0 -0
  212. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_closures.py +0 -0
  213. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_code_injection.py +0 -0
  214. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_config_secrets_coverage.py +0 -0
  215. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_constants.py +0 -0
  216. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_correlation.py +0 -0
  217. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_credential_context.py +0 -0
  218. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_dataflow.py +0 -0
  219. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_dependencies.py +0 -0
  220. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_discovery.py +0 -0
  221. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_routes.py +0 -0
  222. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_django_safe_values.py +0 -0
  223. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_dominance.py +0 -0
  224. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_emit_ir.py +0 -0
  225. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_exceptions.py +0 -0
  226. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_factories_match_loop_else.py +0 -0
  227. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_fail_on.py +0 -0
  228. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_fastapi_cli_precision.py +0 -0
  229. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_frontend_gaps.py +0 -0
  230. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_heap.py +0 -0
  231. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_hex_digests.py +0 -0
  232. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_hir_builder.py +0 -0
  233. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_imports.py +0 -0
  234. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_interprocedural.py +0 -0
  235. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_interprocedural_taint.py +0 -0
  236. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_ir.py +0 -0
  237. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_match_patterns.py +0 -0
  238. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_module_level.py +0 -0
  239. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_nonlocal_writeback.py +0 -0
  240. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_parallel.py +0 -0
  241. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_parser.py +0 -0
  242. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_plaintext_credentials.py +0 -0
  243. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_plugins.py +0 -0
  244. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_project.py +0 -0
  245. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_project_models.py +0 -0
  246. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_project_validators.py +0 -0
  247. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_provenance.py +0 -0
  248. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_pyir_imports.py +0 -0
  249. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_read_entry_points.py +0 -0
  250. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_real_code_precision.py +0 -0
  251. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_redirect_targets.py +0 -0
  252. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_refutation.py +0 -0
  253. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_refutation_evidence.py +0 -0
  254. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_regression.py +0 -0
  255. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_relative_paths.py +0 -0
  256. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_remaining_limits.py +0 -0
  257. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_reporters.py +0 -0
  258. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_safe_arguments.py +0 -0
  259. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_scale.py +0 -0
  260. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_scopes.py +0 -0
  261. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_secrets_sbom.py +0 -0
  262. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_self_state.py +0 -0
  263. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_source_manager.py +0 -0
  264. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_ssa.py +0 -0
  265. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_ssrf_targets.py +0 -0
  266. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_summary_sanitizers.py +0 -0
  267. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_suppressions.py +0 -0
  268. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_symbol_resolution.py +0 -0
  269. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_symbols.py +0 -0
  270. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntactic_rules.py +0 -0
  271. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_control_flow.py +0 -0
  272. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_coverage.py +0 -0
  273. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_syntax_expressions.py +0 -0
  274. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_taint.py +0 -0
  275. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_template_escaping.py +0 -0
  276. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_tornado_bottle_models.py +0 -0
  277. {coretrace_python_analyzer-0.11.0 → coretrace_python_analyzer-0.13.0}/tests/test_vex.py +0 -0
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0 (2026-09-26)
4
+
5
+ ### Precision
6
+
7
+ - The text attributes and URL parameters of a request object carry no `NOSQL`. Django, REST framework, aiohttp and tornado views read a request tainted as a whole, so `request.GET["name"]` and a URL parameter were `nosql-injection` like the body. Now `GET`, `POST`, `COOKIES`, `META`, `headers`, `query_params`, aiohttp's `query` and `match_info`, tornado's `arguments` and the other text attributes are text, and so are the URL parameters a view receives after the request. The body, the files, REST framework's `data` and aiohttp's `json()` keep the kinds of the request. The other kinds are unchanged, and so is the regression corpus. A helper function the request is passed to still reads it as a whole (#162).
8
+
9
+ ### Plugins
10
+
11
+ - A `RequestObject(symbol, text=())` model says that the inputs from an entry point, a route registrar, a typed parameter or a source are request objects, and which of their attributes are text (#162).
12
+
13
+ ## 0.12.0 (2026-09-26)
14
+
15
+ ### Detection
16
+
17
+ - A module-level name bound to an attribute, an item or a call of another module-level name resolves in functions, as the same value does in a function body. `cursor = conn.cursor()` after `conn = sqlite3.connect(...)`, `db = client.shop` and `run = os.system` used to leave the calls made through them unknown, so a query built from request data and executed through a module-level cursor was not reported. Names resolve in statement order. The regression corpus is unchanged (#155).
18
+ - `nosql-injection` reports attacker input that may hold a structure reaching a NoSQL sink, such as a MongoDB filter, where a JSON body giving `{"$ne": null}` matches any value. Only input that may hold a structure carries `NOSQL`: JSON, the raw bodies a `json.loads` decodes, files, WebSocket messages and standard input. Text cannot hold an operator. The text sources of Flask, bottle and tornado, the URL parameters of their routes, the command line, the environment and `input()` no longer carry `NOSQL`. Neither does an entry-point parameter annotated with scalars (`str`, `Optional[int]`, `Annotated[str, Query()]`, `list[str]`, …), which FastAPI validates. Two limits remain. Django and aiohttp request objects stay tainted as a whole. The keys of a dict are not followed, so a string concatenated into `$where` is not reported. The engine ships no NoSQL sink: model plugins declare them (#158).
19
+
20
+ ### Plugins
21
+
22
+ - `TEXT_KINDS` holds every kind but `NOSQL`: what text input carries. A model plugin declares its text sources with it (#158).
23
+
24
+ - A `Members(symbol, dynamic=None, defined=(), typed=())` model says what the attributes and items of a class's instances give, for libraries whose objects give others by names the project chooses. A pymongo client gives the database it is dotted or indexed with, a database gives a collection. The names used to end up in the symbol (`MongoClient.shop.users.find`), or an item vanished into its container's symbol (`MongoClient.find`), so no model could list them. With the model, every path to a collection denotes the collection class, the one a parameter annotated `Collection` denotes: `client.shop.users`, `client["shop"]["users"]`, `client.get_database("shop").get_collection("users")`, a module-level `db = client.shop`, an attribute inherited from the client class. A sink on `Collection.find` covers them all. Symbol resolution now depends on these models, which the engine provides to every module and worker (#157).
25
+
3
26
  ## 0.11.0 (2026-09-25)
4
27
 
5
28
  ### Detection
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: coretrace-python-analyzer
3
- Version: 0.11.0
3
+ Version: 0.13.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
@@ -140,7 +140,7 @@ class BottleModels(ModelPlugin):
140
140
 
141
141
  | Model | Declares |
142
142
  |---|---|
143
- | `Source(symbol, label, kinds=ALL)` | A call or attribute whose value is attacker-controlled; `label` names it in messages (`http`, `stdin`, `argv`, `http-response`). `kinds` restricts what it can inject. |
143
+ | `Source(symbol, label, kinds=ALL)` | A call or attribute whose value is attacker-controlled; `label` names it in messages (`http`, `stdin`, `argv`, `http-response`). `kinds` restricts what it can inject. Text input, such as a form field, a header or a URL parameter, carries `TEXT_KINDS`, every kind but `NOSQL`: a string cannot hold a query operator. |
144
144
  | `Sink(symbol, kinds, positions=(), keywords=())` | A call dangerous for `kinds`. `positions` limits a kind to the given argument indexes, and `keywords` to the given keyword names; a restricted kind reaches only the arguments listed there. SQL sinks read the statement only, and the SSRF sinks of HTTP clients read the URL, by position or as `url=`. A starred argument or `**kwargs` reaches no restricted kind. |
145
145
  | `Sanitizer(symbol, kinds)` | A call whose result is safe for `kinds`, including when the call is inside a project function the data goes through. |
146
146
  | `EntryPoint(symbol, label, kinds=ALL)` | A decorator or a class base whose functions receive their parameters as `label` input, such as `flask.Flask.route`. |
@@ -151,7 +151,9 @@ class BottleModels(ModelPlugin):
151
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. |
152
152
  | `TemplateRender(symbol, position=0, keyword="template_name")` | A call rendering, with autoescaping, the template named by its argument at `position` or `keyword`, such as `render_to_string`. What it returns carries no `HTML` when the name is written in the call and the project's template escapes everything it prints (see the usage guide). |
153
153
  | `Validator(symbol, kinds=ALL, argument=0)` | A callable whose truth proves its argument safe for `kinds`, such as `re.fullmatch`. A flow guarded by it is refuted when it carries no other kind into the sink, and is a hotspot otherwise: a redirect validator does not make a shell command safe. `symbol` may be a function of the project, by its project symbol (`python.hc.accounts.views._allow_redirect`), which then counts wherever it is called, in its own module too. |
154
+ | `RequestObject(symbol, text=())` | The inputs from `symbol`, an entry point, a route registrar, a typed parameter or a source, are request objects. A view receives the request first (after `self`), then URL parameters, which are text. Reading one of the request's `text` attributes gives text, as Django's `GET` or aiohttp's `query`; its body and files keep the request's kinds. An attribute read counts only when every source of the value is such a request. |
154
155
  | `AuthorizationGuard(symbol, label)` | A decorator or a condition restricting who reaches the code, such as `login_required`; a flow behind it is a hotspot. |
156
+ | `Members(symbol, dynamic=None, defined=(), typed=())` | What the attributes and items of an instance of the class `symbol` give, for objects that give others by name. `typed` maps an attribute to the class of what it gives, called or not, since a call denotes its callee: pymongo's `get_database` gives a `Database`, as Flask-PyMongo's `db` property does. `dynamic`, when set, is the class of every item and of every attribute outside `defined` and `typed`: a pymongo client gives a `Database` by name (`client.shop`, `client["shop"]`), a database a `Collection`. Every path to a collection then denotes the collection class, so a sink on `Collection.find` covers them all. Without `dynamic`, an attribute keeps its path and an item its container's symbol, as for any class. |
155
157
 
156
158
  Two plugins may describe the same symbol. An identical model is registered once; two
157
159
  `Sink` models merge their kinds, positions and keywords, so a plugin can add `SQL` to a sink
@@ -112,6 +112,7 @@ variable is not treated that way yet.
112
112
  | `command-injection` | Shell or process execution (`os.system`, `subprocess` with a string, …). |
113
113
  | `code-injection` | The code `eval` or `exec` runs; the globals and locals passed with it are data. `dangerous-eval` reports every such call, this rule the ones attacker input reaches. |
114
114
  | `sql-injection` | A database statement (`cursor.execute`, SQLAlchemy `text`, Django `raw`, …). Parameters of a parameterised query are not statements. |
115
+ | `nosql-injection` | A NoSQL query, such as a MongoDB filter, given attacker input that may hold a structure: a JSON body giving `{"$ne": null}` matches any value. The input that may hold a structure is JSON, the raw bodies a `json.loads` decodes, files, WebSocket messages and standard input. Text cannot hold an operator: form fields, query parameters, headers, cookies, URL parameters, the command line, the environment, and an entry-point parameter annotated with scalars (`str`, `int`, `Optional[str]`, `Annotated[str, Query()]`, `list[str]`, …), which FastAPI validates. A Django, REST framework, aiohttp or tornado view reads a request object: its text attributes (`GET`, `POST`, `headers`, `query_params`, `query`, `match_info`, `arguments`, …) and the URL parameters after it are text, its body, files and `data` are not. Limits: a helper function the request is passed to reads it as a whole; the keys of a dict are not followed, so a string concatenated into `$where` is not reported. The analyzer ships no NoSQL sink: model plugins declare them. |
115
116
  | `path-traversal` | A file system path (`open`, `send_file`, `os.remove`, …). |
116
117
  | `ssrf` | The URL of an HTTP client request (Requests, httpx, `urllib`), by position or as `url=`; the body, headers and query parameters are not the destination. |
117
118
  | `xss` | An HTTP response body built without escaping. A Django template rendered by `render_to_string` escapes, where the analyzer can establish it (below). |
@@ -240,7 +241,7 @@ the directory of the checked file; a path outside it is printed as it is.
240
241
  ```json
241
242
  {
242
243
  "schema_version": 1,
243
- "tool": {"name": "coretrace-python-analyzer", "version": "0.11.0"},
244
+ "tool": {"name": "coretrace-python-analyzer", "version": "0.13.0"},
244
245
  "root": "/home/me/project",
245
246
  "findings": [
246
247
  {
@@ -272,7 +273,7 @@ bytes. A single-file check lists no components.
272
273
 
273
274
  ```json
274
275
  "tool": {
275
- "name": "coretrace-python-analyzer", "version": "0.11.0",
276
+ "name": "coretrace-python-analyzer", "version": "0.13.0",
276
277
  "components": [
277
278
  {"kind": "plugin", "name": "curated-advisories", "version": "2026.09.25", "digest": "sha256:…"},
278
279
  {"kind": "advisories", "name": "advisories.json", "digest": "sha256:…"}
@@ -435,7 +436,7 @@ project's behalf would go unseen. Without a lock file, a package is never
435
436
  `not_affected`. The document's `@id` derives from its statements, so two runs with the
436
437
  same result share it; its `author` is `Unknown Author`, as OpenVEX tools write when they
437
438
  cannot know it. Its `tooling` names the analyzer and each component with its digest
438
- (`coretrace-python-analyzer 0.11.0; curated-advisories 2026.09.25 (sha256:…)`), so the
439
+ (`coretrace-python-analyzer 0.13.0; curated-advisories 2026.09.25 (sha256:…)`), so the
439
440
  `@id` changes with the advisory data a statement was decided with.
440
441
 
441
442
  ## Large projects
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "coretrace-python-analyzer"
7
- version = "0.11.0"
7
+ version = "0.13.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.11.0"
3
+ __version__ = "0.13.0"
4
4
 
@@ -12,6 +12,7 @@ from coretrace_python.semantic.symbols import SymbolId
12
12
  from coretrace_python.taint import (
13
13
  EntryPoint,
14
14
  Model,
15
+ RequestObject,
15
16
  RouteRegistrar,
16
17
  Sink,
17
18
  Source,
@@ -22,6 +23,23 @@ from coretrace_python.taint import (
22
23
  _METHODS = ("get", "post", "put", "patch", "delete", "head", "options")
23
24
  _REQUEST_CLASSES = ("aiohttp.web.Request", "aiohttp.web_request.Request")
24
25
  _ROUTERS = ("aiohttp.web.Application.router", "aiohttp.web.UrlDispatcher")
26
+ # ``@routes.get('/')`` on a ``RouteTableDef``.
27
+ _ROUTE_TABLES = tuple(f"aiohttp.web.RouteTableDef.{method}" for method in (*_METHODS, "route", "view"))
28
+ # ``app.router.add_route('GET', '/', handler)``, ``app.router.add_get('/', handler)`` and
29
+ # ``app.add_routes([web.get('/', handler)])``, each with the argument naming the handler.
30
+ _ROUTE_CALLS = (
31
+ *((f"{router}.add_route", 2) for router in _ROUTERS),
32
+ *((f"{router}.add_{method}", 1) for router in _ROUTERS for method in (*_METHODS, "view")),
33
+ ("aiohttp.web.route", 2),
34
+ *((f"aiohttp.web.{method}", 1) for method in (*_METHODS, "view")),
35
+ )
36
+ # What a request gives as text; its body (``json()``, ``text()``, ``read()``) may hold a
37
+ # structure.
38
+ _REQUEST_TEXT = (
39
+ "query", "rel_url", "url", "path", "raw_path", "path_qs", "query_string", "match_info", "headers",
40
+ "raw_headers", "cookies", "method", "host", "scheme", "remote", "forwarded", "content_type", "charset",
41
+ "if_modified_since", "post",
42
+ )
25
43
  _REDIRECTS = ("HTTPFound", "HTTPMovedPermanently", "HTTPSeeOther", "HTTPTemporaryRedirect", "HTTPPermanentRedirect")
26
44
  _CLIENT_FUNCTIONS = (
27
45
  *(f"aiohttp.ClientSession.{method}" for method in (*_METHODS, "request")),
@@ -40,18 +58,12 @@ class AiohttpModels(ModelPlugin):
40
58
  # The whole request is attacker-controlled: its query, match info, headers,
41
59
  # cookies and body all come from the client.
42
60
  *(TypedParameter(_sym(cls), "http") for cls in _REQUEST_CLASSES),
43
- # ``@routes.get('/')`` on a ``RouteTableDef``.
44
- *(EntryPoint(_sym(f"aiohttp.web.RouteTableDef.{method}"), "http") for method in (*_METHODS, "route", "view")),
45
- # ``app.router.add_route('GET', '/', handler)`` and ``app.router.add_get('/', handler)``.
46
- *(RouteRegistrar(_sym(f"{router}.add_route"), 2, "http", keyword="handler") for router in _ROUTERS),
61
+ *(EntryPoint(_sym(table), "http") for table in _ROUTE_TABLES),
62
+ *(RouteRegistrar(_sym(call), argument, "http", keyword="handler") for call, argument in _ROUTE_CALLS),
47
63
  *(
48
- RouteRegistrar(_sym(f"{router}.add_{method}"), 1, "http", keyword="handler")
49
- for router in _ROUTERS
50
- for method in (*_METHODS, "view")
64
+ RequestObject(_sym(symbol), _REQUEST_TEXT)
65
+ for symbol in (*_REQUEST_CLASSES, *_ROUTE_TABLES, *(call for call, _ in _ROUTE_CALLS))
51
66
  ),
52
- # ``app.add_routes([web.get('/', handler)])``: the route definitions name the handler.
53
- RouteRegistrar(_sym("aiohttp.web.route"), 2, "http", keyword="handler"),
54
- *(RouteRegistrar(_sym(f"aiohttp.web.{method}"), 1, "http", keyword="handler") for method in (*_METHODS, "view")),
55
67
  Sink(_sym("aiohttp.web.Response"), TaintKind.HTML),
56
68
  Sink(_sym("aiohttp.web.FileResponse"), TaintKind.PATH),
57
69
  *(Sink(_sym(f"aiohttp.web.{exception}"), TaintKind.REDIRECT) for exception in _REDIRECTS),
@@ -8,13 +8,15 @@ from typing import ClassVar
8
8
 
9
9
  from coretrace_python.plugins import ModelPlugin
10
10
  from coretrace_python.semantic.symbols import SymbolId
11
- from coretrace_python.taint import EntryPoint, Model, Sanitizer, Sink, Source, TaintKind
11
+ from coretrace_python.taint import TEXT_KINDS, EntryPoint, Model, Sanitizer, Sink, Source, TaintKind
12
12
 
13
13
  _METHODS = ("route", "get", "post", "put", "delete", "patch")
14
- _REQUEST_ATTRIBUTES = (
15
- "query", "forms", "params", "json", "body", "headers", "cookies", "GET", "POST", "files",
14
+ # Text, which cannot hold a query operator; JSON, raw bodies and files may hold a structure.
15
+ _TEXT_ATTRIBUTES = (
16
+ "query", "forms", "params", "headers", "cookies", "GET", "POST",
16
17
  "url", "path", "fullpath", "query_string", "get_cookie", "get_header",
17
18
  )
19
+ _PAYLOAD_ATTRIBUTES = ("json", "body", "files")
18
20
  _FIRST_ONLY = lambda kind: ((kind, (0,)),)
19
21
 
20
22
 
@@ -25,9 +27,11 @@ def _sym(path: str) -> SymbolId:
25
27
  class BottleModels(ModelPlugin):
26
28
  name: ClassVar[str] = "bottle-models"
27
29
  models: ClassVar[tuple[Model, ...]] = (
28
- *(EntryPoint(_sym(f"bottle.{method}"), "http") for method in _METHODS),
29
- *(EntryPoint(_sym(f"bottle.Bottle.{method}"), "http") for method in _METHODS),
30
- *(Source(_sym(f"bottle.request.{attribute}"), "http") for attribute in _REQUEST_ATTRIBUTES),
30
+ # A route's parameters are segments of its URL.
31
+ *(EntryPoint(_sym(f"bottle.{method}"), "http", TEXT_KINDS) for method in _METHODS),
32
+ *(EntryPoint(_sym(f"bottle.Bottle.{method}"), "http", TEXT_KINDS) for method in _METHODS),
33
+ *(Source(_sym(f"bottle.request.{attribute}"), "http", TEXT_KINDS) for attribute in _TEXT_ATTRIBUTES),
34
+ *(Source(_sym(f"bottle.request.{attribute}"), "http") for attribute in _PAYLOAD_ATTRIBUTES),
31
35
  Sink(_sym("bottle.redirect"), TaintKind.REDIRECT, _FIRST_ONLY(TaintKind.REDIRECT)),
32
36
  # ``static_file(filename, root)``: the root is the application's own.
33
37
  Sink(_sym("bottle.static_file"), TaintKind.PATH, _FIRST_ONLY(TaintKind.PATH)),
@@ -6,11 +6,11 @@ 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 EntryPoint, Model, TaintKind
9
+ from coretrace_python.taint import TEXT_KINDS, EntryPoint, Model, TaintKind
10
10
 
11
- # A command-line tool is expected to open the paths it is given: argv input carries every
12
- # kind but PATH.
13
- ARGV_KINDS = TaintKind.ALL & ~TaintKind.PATH
11
+ # A command-line tool is expected to open the paths it is given: argv input is text,
12
+ # without PATH.
13
+ ARGV_KINDS = TEXT_KINDS & ~TaintKind.PATH
14
14
 
15
15
  _COMMANDS = (
16
16
  "click.command",
@@ -17,6 +17,7 @@ from coretrace_python.taint import (
17
17
  AuthorizationGuard,
18
18
  EntryPoint,
19
19
  Model,
20
+ RequestObject,
20
21
  RouteRegistrar,
21
22
  Sanitizer,
22
23
  Sink,
@@ -88,6 +89,23 @@ _AUTHORIZATION_DECORATORS = (
88
89
  )
89
90
 
90
91
 
92
+ # What a request object gives as text: the query string, form fields, headers, cookies
93
+ # and path. Its body, its files and REST framework's parsed ``data`` may hold a structure.
94
+ _REQUEST_TEXT = (
95
+ "GET", "POST", "COOKIES", "META", "headers", "path", "path_info", "method", "scheme", "encoding",
96
+ "content_type", "content_params", "resolver_match", "get_full_path", "get_full_path_info",
97
+ "build_absolute_uri", "get_host", "get_port", "get_signed_cookie", "query_params",
98
+ )
99
+ # Every input a request object comes from: an annotated parameter, a class-based view's
100
+ # method or ``self.request``, a decorated view, a view registered in a URL configuration.
101
+ _REQUEST_INPUTS = (
102
+ *_REQUEST_CLASSES,
103
+ *_VIEW_BASES,
104
+ *(f"{base}.request" for base in _VIEW_BASES),
105
+ *_VIEW_DECORATORS,
106
+ *_ROUTE_REGISTRARS,
107
+ )
108
+
91
109
  _TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
92
110
 
93
111
 
@@ -103,6 +121,7 @@ class DjangoModels(ModelPlugin):
103
121
  # ``self.request`` in a class-based view: the attribute is inherited from the base.
104
122
  *(Source(_sym(f"{base}.request"), "http") for base in _VIEW_BASES),
105
123
  *(EntryPoint(_sym(decorator), "http") for decorator in _VIEW_DECORATORS),
124
+ *(RequestObject(_sym(symbol), _REQUEST_TEXT) for symbol in _REQUEST_INPUTS),
106
125
  Sink(_sym("django.db.connection.cursor.execute"), TaintKind.SQL | TaintKind.CREDENTIAL, ((TaintKind.SQL, (0,)),)),
107
126
  Sink(_sym("django.db.connection.cursor.executemany"), TaintKind.SQL | TaintKind.CREDENTIAL, ((TaintKind.SQL, (0,)),)),
108
127
  Sink(_sym("django.db.models.expressions.RawSQL"), TaintKind.SQL | TaintKind.CREDENTIAL),
@@ -7,6 +7,7 @@ from typing import ClassVar
7
7
  from coretrace_python.plugins import ModelPlugin
8
8
  from coretrace_python.semantic.symbols import SymbolId
9
9
  from coretrace_python.taint import (
10
+ TEXT_KINDS,
10
11
  AuthorizationGuard,
11
12
  EntryPoint,
12
13
  Model,
@@ -17,10 +18,10 @@ from coretrace_python.taint import (
17
18
  TaintKind,
18
19
  )
19
20
 
20
- _REQUEST_ATTRIBUTES = (
21
- "args", "form", "values", "json", "data", "cookies", "headers", "files",
22
- "get_json", "get_data", "url", "full_path", "path", "query_string", "stream",
23
- )
21
+ # Strings, or mappings of strings: text, which cannot hold a query operator.
22
+ _TEXT_ATTRIBUTES = ("args", "form", "values", "cookies", "headers", "url", "full_path", "path", "query_string")
23
+ # JSON, and the raw bodies and files a ``json.loads`` may decode into a structure.
24
+ _PAYLOAD_ATTRIBUTES = ("json", "get_json", "data", "get_data", "stream", "files")
24
25
 
25
26
 
26
27
  _TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
@@ -33,11 +34,13 @@ def _sym(path: str) -> SymbolId:
33
34
  class FlaskModels(ModelPlugin):
34
35
  name: ClassVar[str] = "flask-models"
35
36
  models: ClassVar[tuple[Model, ...]] = (
36
- *(Source(_sym(f"flask.request.{attribute}"), "http") for attribute in _REQUEST_ATTRIBUTES),
37
- EntryPoint(_sym("flask.Flask.route"), "http"),
38
- EntryPoint(_sym("flask.Blueprint.route"), "http"),
39
- RouteRegistrar(_sym("flask.Flask.add_url_rule"), 2, "http", keyword="view_func"),
40
- RouteRegistrar(_sym("flask.Blueprint.add_url_rule"), 2, "http", keyword="view_func"),
37
+ *(Source(_sym(f"flask.request.{attribute}"), "http", TEXT_KINDS) for attribute in _TEXT_ATTRIBUTES),
38
+ *(Source(_sym(f"flask.request.{attribute}"), "http") for attribute in _PAYLOAD_ATTRIBUTES),
39
+ # A route's parameters are segments of its URL.
40
+ EntryPoint(_sym("flask.Flask.route"), "http", TEXT_KINDS),
41
+ EntryPoint(_sym("flask.Blueprint.route"), "http", TEXT_KINDS),
42
+ RouteRegistrar(_sym("flask.Flask.add_url_rule"), 2, "http", TEXT_KINDS, keyword="view_func"),
43
+ RouteRegistrar(_sym("flask.Blueprint.add_url_rule"), 2, "http", TEXT_KINDS, keyword="view_func"),
41
44
  Sink(_sym("flask.render_template_string"), TaintKind.HTML),
42
45
  Sink(_sym("flask.make_response"), TaintKind.HTML),
43
46
  Sink(_sym("flask.Response"), TaintKind.HTML),
@@ -7,6 +7,7 @@ from typing import ClassVar
7
7
  from coretrace_python.plugins import ModelPlugin
8
8
  from coretrace_python.semantic.symbols import SymbolId
9
9
  from coretrace_python.taint import (
10
+ TEXT_KINDS,
10
11
  Model,
11
12
  SafeArgument,
12
13
  Sanitizer,
@@ -16,7 +17,7 @@ from coretrace_python.taint import (
16
17
  Validator,
17
18
  )
18
19
 
19
- _ENVIRONMENT_KINDS = TaintKind.ALL & ~(TaintKind.COMMAND | TaintKind.PATH)
20
+ _ENVIRONMENT_KINDS = TEXT_KINDS & ~(TaintKind.COMMAND | TaintKind.PATH)
20
21
  # SafeLoader and BaseLoader build plain data only, in Python and in C, under every spelling.
21
22
  _SAFE_YAML_LOADERS = (
22
23
  "python.yaml.SafeLoader",
@@ -38,13 +39,14 @@ def _sym(path: str) -> SymbolId:
38
39
  class PythonStdlibModels(ModelPlugin):
39
40
  name: ClassVar[str] = "python-stdlib-models"
40
41
  models: ClassVar[tuple[Model, ...]] = (
41
- Source(_sym("builtins.input"), "stdin"),
42
+ # A line of text; the stream may be a payload ``json.load`` decodes.
43
+ Source(_sym("builtins.input"), "stdin", TEXT_KINDS),
42
44
  Source(_sym("sys.stdin"), "stdin"),
43
45
  # Operator-controlled inputs. A command-line tool is expected to open the paths
44
46
  # it is given; the environment is set by whoever runs the program, so a command
45
47
  # or a path built from it is not an injection; the output of a local process is
46
48
  # not a path either, but a downloaded script piped into a shell is a real flaw.
47
- Source(_sym("sys.argv"), "argv", TaintKind.ALL & ~TaintKind.PATH),
49
+ Source(_sym("sys.argv"), "argv", TEXT_KINDS & ~TaintKind.PATH),
48
50
  Source(_sym("os.environ"), "environment", _ENVIRONMENT_KINDS),
49
51
  Source(_sym("subprocess.run.stdout"), "process-output", _PROCESS_OUTPUT_KINDS),
50
52
  Source(_sym("subprocess.check_output"), "process-output", _PROCESS_OUTPUT_KINDS),
@@ -8,12 +8,27 @@ from typing import ClassVar
8
8
 
9
9
  from coretrace_python.plugins import ModelPlugin
10
10
  from coretrace_python.semantic.symbols import SymbolId
11
- from coretrace_python.taint import EntryPoint, Model, Sanitizer, Sink, Source, TaintKind
11
+ from coretrace_python.taint import (
12
+ TEXT_KINDS,
13
+ EntryPoint,
14
+ Model,
15
+ RequestObject,
16
+ Sanitizer,
17
+ Sink,
18
+ Source,
19
+ TaintKind,
20
+ )
12
21
 
13
22
  _HANDLERS = ("tornado.web.RequestHandler", "tornado.websocket.WebSocketHandler")
14
- _REQUEST_METHODS = (
23
+ # Text, which cannot hold a query operator; the request object holds the raw body too.
24
+ _TEXT_METHODS = (
15
25
  "get_argument", "get_arguments", "get_query_argument", "get_query_arguments",
16
- "get_body_argument", "get_body_arguments", "get_cookie", "request", "path_args", "path_kwargs",
26
+ "get_body_argument", "get_body_arguments", "get_cookie", "path_args", "path_kwargs",
27
+ )
28
+ # What ``self.request`` gives as text; its body and files may hold a structure.
29
+ _REQUEST_TEXT = (
30
+ "arguments", "query_arguments", "body_arguments", "query", "uri", "path", "headers", "cookies",
31
+ "method", "host", "host_name", "remote_ip", "protocol", "version", "full_url",
17
32
  )
18
33
  _CLIENTS = ("tornado.httpclient.AsyncHTTPClient.fetch", "tornado.httpclient.HTTPClient.fetch")
19
34
  _TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
@@ -26,8 +41,13 @@ def _sym(path: str) -> SymbolId:
26
41
  class TornadoModels(ModelPlugin):
27
42
  name: ClassVar[str] = "tornado-models"
28
43
  models: ClassVar[tuple[Model, ...]] = (
29
- *(EntryPoint(_sym(handler), "http") for handler in _HANDLERS),
30
- *(Source(_sym(f"{handler}.{method}"), "http") for handler in _HANDLERS for method in _REQUEST_METHODS),
44
+ # A request handler's parameters are segments of its URL; a WebSocket handler also
45
+ # receives the messages, raw payloads a ``json.loads`` may decode.
46
+ EntryPoint(_sym("tornado.web.RequestHandler"), "http", TEXT_KINDS),
47
+ EntryPoint(_sym("tornado.websocket.WebSocketHandler"), "http"),
48
+ *(Source(_sym(f"{handler}.{method}"), "http", TEXT_KINDS) for handler in _HANDLERS for method in _TEXT_METHODS),
49
+ *(Source(_sym(f"{handler}.request"), "http") for handler in _HANDLERS),
50
+ *(RequestObject(_sym(f"{handler}.request"), _REQUEST_TEXT) for handler in _HANDLERS),
31
51
  *(Sink(_sym(f"{handler}.write"), TaintKind.HTML) for handler in _HANDLERS),
32
52
  *(Sink(_sym(f"{handler}.finish"), TaintKind.HTML) for handler in _HANDLERS),
33
53
  *(Sink(_sym(f"{handler}.redirect"), TaintKind.REDIRECT, _TARGET_ONLY) for handler in _HANDLERS),
@@ -0,0 +1,20 @@
1
+ """NoSQL injection: attacker input that may hold a structure reaching a NOSQL sink, such
2
+ as a MongoDB filter, where a JSON body giving ``{"$ne": null}`` matches any value. Text
3
+ input carries no ``NOSQL``: a string cannot hold an operator. The engine ships no NOSQL
4
+ sink; model plugins declare them."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import ClassVar
9
+
10
+ from coretrace_python.findings import Severity
11
+ from coretrace_python.plugins import TaintDetector
12
+ from coretrace_python.taint import TaintKind
13
+
14
+
15
+ class NoSqlInjectionPlugin(TaintDetector):
16
+ name: ClassVar[str] = "nosql-injection"
17
+ rule_id: ClassVar[str] = "nosql-injection"
18
+ kind: ClassVar[TaintKind] = TaintKind.NOSQL
19
+ severity: ClassVar[Severity] = Severity.HIGH
20
+ title: ClassVar[str] = "NoSQL injection"
@@ -0,0 +1,9 @@
1
+ name = "nosql-injection"
2
+ version = "1.0.0"
3
+ plugin_api = ">=1,<2"
4
+ requires = ["taint.flows", "findings.refutation"]
5
+ provides = ["vulnerability.nosql-injection"]
6
+
7
+ [entrypoint]
8
+ module = "nosql_injection"
9
+ class = "NoSqlInjectionPlugin"
@@ -109,7 +109,7 @@ from coretrace_python.reporters import Report
109
109
  from coretrace_python.semantic import SEMANTIC_ANALYSES
110
110
  from coretrace_python.semantic.imports import ImportAnalysis, ImportResolutionError, ImportTable
111
111
  from coretrace_python.semantic.scopes import ScopeAnalysis, ScopeError
112
- from coretrace_python.semantic.symbols import SymbolAnalysis, SymbolId
112
+ from coretrace_python.semantic.symbols import MembersAnalysis, SymbolAnalysis, SymbolId
113
113
  from coretrace_python.source import SourceFile, SourceId, SourceManager, SourceSpan, decode_text
114
114
  from coretrace_python.taint import (
115
115
  EntryPoint,
@@ -257,6 +257,7 @@ def build_manager(
257
257
  manager = _register_all(module)
258
258
  table = (models or SecurityModelRegistry()).freeze()
259
259
  manager.provide(SecurityModelAnalysis, table)
260
+ manager.provide(MembersAnalysis, table.members_by_class())
260
261
  manager.provide(ClearingAnalysis, table.clearing())
261
262
  manager.provide(ProjectSummaries, SummaryIndex())
262
263
  manager.provide(RegisteredRoutes, _routes_of(manager))
@@ -311,6 +312,7 @@ def analyze_file(source: SourceFile, plugin_roots: Sequence[Path]) -> FileAnalys
311
312
  registry = load_plugins(plugin_roots, manager)
312
313
  table = plugin_models(loaded.plugin for loaded in registry)
313
314
  manager.provide(SecurityModelAnalysis, table)
315
+ manager.provide(MembersAnalysis, table.members_by_class())
314
316
  manager.provide(ClearingAnalysis, table.clearing())
315
317
  manager.provide(ProjectSummaries, SummaryIndex())
316
318
  manager.provide(RegisteredRoutes, _routes_of(manager))
@@ -402,8 +404,10 @@ def analyze_project(
402
404
  advisories, origins = _merge_advisories(_contributions(registry, plugins, root, file_advisories))
403
405
  affected = affected_symbols(dependencies, advisories)
404
406
  models = plugin_models(all_plugins, root).extended(*advisory_sinks(affected))
407
+ members = models.members_by_class()
405
408
  for manager in managers.values():
406
409
  manager.provide(SecurityModelAnalysis, models)
410
+ manager.provide(MembersAnalysis, members)
407
411
  manager.provide(DependencyAnalysis, dependencies)
408
412
 
409
413
  imports: dict[str, ImportTable] = {}
@@ -698,8 +702,10 @@ def _analyse_batch(batch: _Batch) -> dict[str, dict[str, Any]]:
698
702
  models = plugin_models(all_plugins, batch.root).extended(*advisory_sinks(affected))
699
703
  routes = _decode_routes(batch.routes)
700
704
  escaped = frozenset(batch.escaped)
705
+ members = models.members_by_class()
701
706
  for manager in managers.values():
702
707
  manager.provide(SecurityModelAnalysis, models)
708
+ manager.provide(MembersAnalysis, members)
703
709
  manager.provide(DependencyAnalysis, dependencies)
704
710
  manager.provide(RegisteredRoutes, routes)
705
711
  manager.provide(EscapedTemplates, escaped)
@@ -176,12 +176,12 @@ class CallGraph:
176
176
 
177
177
 
178
178
  def derive_symbols(
179
- function: FunctionIR, initial: Mapping[Value, SymbolId] | None = None
179
+ function: FunctionIR, table: SymbolTable, initial: Mapping[Value, SymbolId] | None = None
180
180
  ) -> dict[Value, SymbolId]:
181
- """Symbols of values: ``Symbol`` results, attributes and items of symbol values,
182
- results of calling a symbol (``sqlite3.connect(p)`` denotes ``python.sqlite3.connect``)
183
- and the values a ``with`` on such a result binds. Known functions derive nothing;
184
- parameters only through ``initial``, their annotated classes."""
181
+ """Symbols of values: ``Symbol`` results, attributes and items of symbol values, as
182
+ ``table`` types them, results of calling a symbol (``sqlite3.connect(p)`` denotes
183
+ ``python.sqlite3.connect``) and the values a ``with`` on such a result binds. Known
184
+ functions derive nothing; parameters only through ``initial``, their annotated classes."""
185
185
 
186
186
  symbols: dict[Value, SymbolId] = dict(initial or {})
187
187
  changed = True
@@ -195,11 +195,9 @@ def derive_symbols(
195
195
  if isinstance(instruction, Symbol):
196
196
  symbol = instruction.symbol_id
197
197
  elif isinstance(instruction, GetAttr) and instruction.object in symbols:
198
- symbol = symbols[instruction.object].attribute(instruction.attribute)
198
+ symbol = table.attribute(symbols[instruction.object], instruction.attribute)
199
199
  elif isinstance(instruction, GetItem) and instruction.object in symbols:
200
- # An item of a symbol-denoted container (``request.files['f']``)
201
- # carries the container's symbol, so its methods resolve.
202
- symbol = symbols[instruction.object]
200
+ symbol = table.item(symbols[instruction.object])
203
201
  elif isinstance(instruction, Call | WithEnter):
204
202
  origin = (
205
203
  instruction.callee if isinstance(instruction, Call) else instruction.context
@@ -263,6 +261,8 @@ def resolve_targets(
263
261
  owner: str | None = None,
264
262
  typed: Mapping[Value, str] | None = None,
265
263
  bases: Mapping[str, SymbolId] | None = None,
264
+ *,
265
+ table: SymbolTable,
266
266
  ) -> tuple[dict[Value, Target], dict[Value, SymbolId]]:
267
267
  """Map every callee value of ``function`` to its target, and every symbol value.
268
268
  ``nested`` names the functions defined inside this one (``outer.inner``);
@@ -271,7 +271,8 @@ def resolve_targets(
271
271
  call to ``App.__init__``, ``app.run()`` and ``self.run()`` calls to ``App.run``.
272
272
  ``bases`` maps a module class to the symbol of its base: an attribute the class does
273
273
  not define is inherited, so ``self.get_argument`` in a ``RequestHandler`` subclass
274
- denotes ``tornado.web.RequestHandler.get_argument``."""
274
+ denotes ``tornado.web.RequestHandler.get_argument``. ``table`` says what the
275
+ attributes and items of a class a ``Members`` model describes give."""
275
276
 
276
277
  module = scopes.module_scope
277
278
  classes = classes or {}
@@ -294,8 +295,8 @@ def resolve_targets(
294
295
  class_name = instance_of[instruction.object]
295
296
  base = (bases or {}).get(class_name)
296
297
  if base is not None and instruction.attribute not in classes.get(class_name, ()):
297
- inherited[instruction.result] = base.attribute(instruction.attribute)
298
- symbols = derive_symbols(function, inherited)
298
+ inherited[instruction.result] = table.attribute(base, instruction.attribute)
299
+ symbols = derive_symbols(function, table, inherited)
299
300
  targets: dict[Value, Target] = {
300
301
  value: ExternalSymbol(symbol) for value, symbol in symbols.items()
301
302
  }
@@ -427,6 +428,7 @@ class CallGraphAnalysis(Analysis[CallGraph]):
427
428
  owner,
428
429
  _typed_with_module_classes(function, ssa, classes),
429
430
  bases,
431
+ table=table,
430
432
  )
431
433
  defs = {i.result: i for block in ssa.blocks for i in block.instructions if i.result is not None}
432
434
  found: list[CallSite] = []
@@ -141,7 +141,7 @@ class _FunctionLowerer:
141
141
  return self.symbols.resolve(self.scope.id, node.identifier)
142
142
  if isinstance(node, nodes.Attribute):
143
143
  parent = self.imported_symbol(node.value)
144
- return parent.attribute(node.name) if parent is not None else None
144
+ return self.symbols.attribute(parent, node.name) if parent is not None else None
145
145
  return None
146
146
 
147
147
  def expression(self, node: nodes.Expression) -> Value:
@@ -0,0 +1,9 @@
1
+ """Semantic analyses computed from PyHIR: scopes, imports and symbols."""
2
+
3
+ from coretrace_python.semantic.imports import ImportAnalysis
4
+ from coretrace_python.semantic.scopes import ScopeAnalysis
5
+ from coretrace_python.semantic.symbols import MembersAnalysis, SymbolAnalysis
6
+
7
+ SEMANTIC_ANALYSES = (ScopeAnalysis, ImportAnalysis, MembersAnalysis, SymbolAnalysis)
8
+
9
+ __all__ = ["SEMANTIC_ANALYSES", "ImportAnalysis", "MembersAnalysis", "ScopeAnalysis", "SymbolAnalysis"]