@duckmind/dm-linux-x64 0.66.0 → 0.66.2

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 (546) hide show
  1. package/dm +2 -2
  2. package/extensions/.dm-extensions.json +12 -546
  3. package/extensions/dm-lens/clients/dispatch/runners/utils/runner-helpers.js +1 -1
  4. package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +1 -1
  5. package/extensions/dm-ponytail/hooks/ponytail-activate.js +1 -1
  6. package/extensions/dm-ponytail/hooks/ponytail-instructions.js +1 -1
  7. package/extensions/dm-subagents/src/runs/background/runner-aliases.js +2 -2
  8. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +4 -1
  9. package/extensions/dm-subagents/src/runs/shared/child-session.js +8 -6
  10. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +9 -1
  11. package/extensions/dm-subagents/src/watchdog/review.js +4 -1
  12. package/package.json +1 -1
  13. package/extensions/dm-lens/dist/clients/actionable-warnings-logger.js +0 -24
  14. package/extensions/dm-lens/dist/clients/actionable-warnings.js +0 -1014
  15. package/extensions/dm-lens/dist/clients/advisory-provenance.js +0 -385
  16. package/extensions/dm-lens/dist/clients/agent-behavior-client.js +0 -94
  17. package/extensions/dm-lens/dist/clients/agent-nudge.js +0 -151
  18. package/extensions/dm-lens/dist/clients/analysed-root.js +0 -1
  19. package/extensions/dm-lens/dist/clients/ast-grep-client.js +0 -576
  20. package/extensions/dm-lens/dist/clients/ast-grep-rule-manager.js +0 -114
  21. package/extensions/dm-lens/dist/clients/ast-grep-tool-logger.js +0 -85
  22. package/extensions/dm-lens/dist/clients/ast-grep-types.js +0 -1
  23. package/extensions/dm-lens/dist/clients/ast-grep-yaml-synth.js +0 -109
  24. package/extensions/dm-lens/dist/clients/atomic-write-staging.js +0 -66
  25. package/extensions/dm-lens/dist/clients/atomic-write.js +0 -37
  26. package/extensions/dm-lens/dist/clients/availability-probe-flight.js +0 -11
  27. package/extensions/dm-lens/dist/clients/bash-file-access.js +0 -830
  28. package/extensions/dm-lens/dist/clients/biome-client.js +0 -221
  29. package/extensions/dm-lens/dist/clients/blocker-freshness.js +0 -385
  30. package/extensions/dm-lens/dist/clients/blocker-past-eof.js +0 -49
  31. package/extensions/dm-lens/dist/clients/bootstrap.js +0 -246
  32. package/extensions/dm-lens/dist/clients/bounded-cache.js +0 -105
  33. package/extensions/dm-lens/dist/clients/bounded-pid-file-lock.js +0 -173
  34. package/extensions/dm-lens/dist/clients/bounded-telemetry.js +0 -104
  35. package/extensions/dm-lens/dist/clients/build-identity.js +0 -45
  36. package/extensions/dm-lens/dist/clients/bundled-resource-health.js +0 -41
  37. package/extensions/dm-lens/dist/clients/bus-events-logger.js +0 -48
  38. package/extensions/dm-lens/dist/clients/bus-publish.js +0 -117
  39. package/extensions/dm-lens/dist/clients/cache/rule-cache.js +0 -103
  40. package/extensions/dm-lens/dist/clients/cache-manager.js +0 -340
  41. package/extensions/dm-lens/dist/clients/cache-observability.js +0 -847
  42. package/extensions/dm-lens/dist/clients/call-graph.js +0 -628
  43. package/extensions/dm-lens/dist/clients/cargo-manifest.js +0 -151
  44. package/extensions/dm-lens/dist/clients/cascade-budget.js +0 -52
  45. package/extensions/dm-lens/dist/clients/cascade-format.js +0 -160
  46. package/extensions/dm-lens/dist/clients/cascade-logger.js +0 -22
  47. package/extensions/dm-lens/dist/clients/cascade-types.js +0 -1
  48. package/extensions/dm-lens/dist/clients/child-unref.js +0 -59
  49. package/extensions/dm-lens/dist/clients/code-quality-warnings.js +0 -192
  50. package/extensions/dm-lens/dist/clients/codebase-model.js +0 -110
  51. package/extensions/dm-lens/dist/clients/collateral-test-role.js +0 -8
  52. package/extensions/dm-lens/dist/clients/complexity-client.js +0 -398
  53. package/extensions/dm-lens/dist/clients/config-core/deny.js +0 -117
  54. package/extensions/dm-lens/dist/clients/config-core/index.js +0 -2
  55. package/extensions/dm-lens/dist/clients/config-core/merge.js +0 -228
  56. package/extensions/dm-lens/dist/clients/config-core/normalize.js +0 -217
  57. package/extensions/dm-lens/dist/clients/config-core/process-spec.js +0 -147
  58. package/extensions/dm-lens/dist/clients/config-core/provenance.js +0 -54
  59. package/extensions/dm-lens/dist/clients/config-core/records.js +0 -79
  60. package/extensions/dm-lens/dist/clients/config-core/resolve.js +0 -48
  61. package/extensions/dm-lens/dist/clients/config-core/safe-object.js +0 -22
  62. package/extensions/dm-lens/dist/clients/config-core/schema.js +0 -90
  63. package/extensions/dm-lens/dist/clients/config-diagnostic-codes.js +0 -90
  64. package/extensions/dm-lens/dist/clients/config-locations.js +0 -175
  65. package/extensions/dm-lens/dist/clients/config-resolve.js +0 -359
  66. package/extensions/dm-lens/dist/clients/config-schema.js +0 -128
  67. package/extensions/dm-lens/dist/clients/config-warn.js +0 -146
  68. package/extensions/dm-lens/dist/clients/console-guard-install.js +0 -4
  69. package/extensions/dm-lens/dist/clients/cooperative-budget.js +0 -36
  70. package/extensions/dm-lens/dist/clients/dead-code-client.js +0 -342
  71. package/extensions/dm-lens/dist/clients/dead-code-logger.js +0 -18
  72. package/extensions/dm-lens/dist/clients/deadline-utils.js +0 -117
  73. package/extensions/dm-lens/dist/clients/debug-handles.js +0 -121
  74. package/extensions/dm-lens/dist/clients/debug-heap.js +0 -80
  75. package/extensions/dm-lens/dist/clients/deferred-lsp-work.js +0 -33
  76. package/extensions/dm-lens/dist/clients/degradation-ledger.js +0 -272
  77. package/extensions/dm-lens/dist/clients/demoted-finding-render.js +0 -61
  78. package/extensions/dm-lens/dist/clients/dependency-checker.js +0 -525
  79. package/extensions/dm-lens/dist/clients/deps/ast-grep-napi.js +0 -17
  80. package/extensions/dm-lens/dist/clients/deps/dm-host-deps.js +0 -10
  81. package/extensions/dm-lens/dist/clients/deps/dm-tui-compat.js +0 -58
  82. package/extensions/dm-lens/dist/clients/deps/js-yaml.js +0 -7
  83. package/extensions/dm-lens/dist/clients/deps/minimatch.js +0 -6
  84. package/extensions/dm-lens/dist/clients/deps/pidusage.js +0 -5
  85. package/extensions/dm-lens/dist/clients/deps/typebox.js +0 -5
  86. package/extensions/dm-lens/dist/clients/deps/vscode-jsonrpc.js +0 -8
  87. package/extensions/dm-lens/dist/clients/deps/web-tree-sitter.js +0 -11
  88. package/extensions/dm-lens/dist/clients/diagnostic-dispositions.js +0 -318
  89. package/extensions/dm-lens/dist/clients/diagnostic-line-freshness.js +0 -144
  90. package/extensions/dm-lens/dist/clients/diagnostic-logger.js +0 -60
  91. package/extensions/dm-lens/dist/clients/diagnostic-tracker.js +0 -89
  92. package/extensions/dm-lens/dist/clients/diagnostics-publish.js +0 -134
  93. package/extensions/dm-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -16
  94. package/extensions/dm-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -158
  95. package/extensions/dm-lens/dist/clients/dispatch/collect-later-tier.js +0 -25
  96. package/extensions/dm-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -58
  97. package/extensions/dm-lens/dist/clients/dispatch/dispatcher.js +0 -953
  98. package/extensions/dm-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  99. package/extensions/dm-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  100. package/extensions/dm-lens/dist/clients/dispatch/fact-runner.js +0 -18
  101. package/extensions/dm-lens/dist/clients/dispatch/fact-scheduler.js +0 -52
  102. package/extensions/dm-lens/dist/clients/dispatch/fact-store.js +0 -199
  103. package/extensions/dm-lens/dist/clients/dispatch/facts/comment-facts.js +0 -20
  104. package/extensions/dm-lens/dist/clients/dispatch/facts/file-content.js +0 -18
  105. package/extensions/dm-lens/dist/clients/dispatch/facts/function-facts.js +0 -281
  106. package/extensions/dm-lens/dist/clients/dispatch/facts/import-facts.js +0 -169
  107. package/extensions/dm-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -19
  108. package/extensions/dm-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -95
  109. package/extensions/dm-lens/dist/clients/dispatch/finding-policy.js +0 -159
  110. package/extensions/dm-lens/dist/clients/dispatch/indent-detect.js +0 -227
  111. package/extensions/dm-lens/dist/clients/dispatch/inline-suppressions.js +0 -52
  112. package/extensions/dm-lens/dist/clients/dispatch/integration.js +0 -1463
  113. package/extensions/dm-lens/dist/clients/dispatch/lazy.js +0 -8
  114. package/extensions/dm-lens/dist/clients/dispatch/pending-runner-findings.js +0 -75
  115. package/extensions/dm-lens/dist/clients/dispatch/plan.js +0 -278
  116. package/extensions/dm-lens/dist/clients/dispatch/priorities.js +0 -14
  117. package/extensions/dm-lens/dist/clients/dispatch/rule-id-normalize.js +0 -42
  118. package/extensions/dm-lens/dist/clients/dispatch/rule-ignores.js +0 -21
  119. package/extensions/dm-lens/dist/clients/dispatch/rule-policy.js +0 -73
  120. package/extensions/dm-lens/dist/clients/dispatch/rules/async-noise.js +0 -31
  121. package/extensions/dm-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  122. package/extensions/dm-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -35
  123. package/extensions/dm-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -29
  124. package/extensions/dm-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -29
  125. package/extensions/dm-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -31
  126. package/extensions/dm-lens/dist/clients/dispatch/rules/high-complexity.js +0 -56
  127. package/extensions/dm-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -45
  128. package/extensions/dm-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -31
  129. package/extensions/dm-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -45
  130. package/extensions/dm-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -46
  131. package/extensions/dm-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -38
  132. package/extensions/dm-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  133. package/extensions/dm-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -101
  134. package/extensions/dm-lens/dist/clients/dispatch/runner-context.js +0 -46
  135. package/extensions/dm-lens/dist/clients/dispatch/runners/actionlint.js +0 -98
  136. package/extensions/dm-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -690
  137. package/extensions/dm-lens/dist/clients/dispatch/runners/biome-check.js +0 -202
  138. package/extensions/dm-lens/dist/clients/dispatch/runners/cpp-check.js +0 -190
  139. package/extensions/dm-lens/dist/clients/dispatch/runners/credo.js +0 -66
  140. package/extensions/dm-lens/dist/clients/dispatch/runners/cue-vet.js +0 -167
  141. package/extensions/dm-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -151
  142. package/extensions/dm-lens/dist/clients/dispatch/runners/detekt.js +0 -157
  143. package/extensions/dm-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -157
  144. package/extensions/dm-lens/dist/clients/dispatch/runners/elixir-check.js +0 -153
  145. package/extensions/dm-lens/dist/clients/dispatch/runners/eslint.js +0 -99
  146. package/extensions/dm-lens/dist/clients/dispatch/runners/fact-rules.js +0 -20
  147. package/extensions/dm-lens/dist/clients/dispatch/runners/fish-indent.js +0 -65
  148. package/extensions/dm-lens/dist/clients/dispatch/runners/gleam-check.js +0 -107
  149. package/extensions/dm-lens/dist/clients/dispatch/runners/go-vet.js +0 -51
  150. package/extensions/dm-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -89
  151. package/extensions/dm-lens/dist/clients/dispatch/runners/hadolint.js +0 -83
  152. package/extensions/dm-lens/dist/clients/dispatch/runners/helm-lint.js +0 -144
  153. package/extensions/dm-lens/dist/clients/dispatch/runners/helm-render.js +0 -593
  154. package/extensions/dm-lens/dist/clients/dispatch/runners/htmlhint.js +0 -103
  155. package/extensions/dm-lens/dist/clients/dispatch/runners/index.js +0 -100
  156. package/extensions/dm-lens/dist/clients/dispatch/runners/javac.js +0 -82
  157. package/extensions/dm-lens/dist/clients/dispatch/runners/ktlint.js +0 -93
  158. package/extensions/dm-lens/dist/clients/dispatch/runners/lsp.js +0 -206
  159. package/extensions/dm-lens/dist/clients/dispatch/runners/markdownlint.js +0 -129
  160. package/extensions/dm-lens/dist/clients/dispatch/runners/mypy.js +0 -70
  161. package/extensions/dm-lens/dist/clients/dispatch/runners/oxlint.js +0 -305
  162. package/extensions/dm-lens/dist/clients/dispatch/runners/php-lint.js +0 -70
  163. package/extensions/dm-lens/dist/clients/dispatch/runners/phpstan.js +0 -90
  164. package/extensions/dm-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -56
  165. package/extensions/dm-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -307
  166. package/extensions/dm-lens/dist/clients/dispatch/runners/pyright.js +0 -118
  167. package/extensions/dm-lens/dist/clients/dispatch/runners/rubocop.js +0 -79
  168. package/extensions/dm-lens/dist/clients/dispatch/runners/ruff.js +0 -69
  169. package/extensions/dm-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -147
  170. package/extensions/dm-lens/dist/clients/dispatch/runners/shellcheck.js +0 -111
  171. package/extensions/dm-lens/dist/clients/dispatch/runners/shfmt.js +0 -81
  172. package/extensions/dm-lens/dist/clients/dispatch/runners/spellcheck.js +0 -74
  173. package/extensions/dm-lens/dist/clients/dispatch/runners/spotbugs.js +0 -208
  174. package/extensions/dm-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -136
  175. package/extensions/dm-lens/dist/clients/dispatch/runners/stylelint.js +0 -118
  176. package/extensions/dm-lens/dist/clients/dispatch/runners/swiftlint.js +0 -143
  177. package/extensions/dm-lens/dist/clients/dispatch/runners/taplo.js +0 -97
  178. package/extensions/dm-lens/dist/clients/dispatch/runners/terragrunt.js +0 -110
  179. package/extensions/dm-lens/dist/clients/dispatch/runners/tflint.js +0 -104
  180. package/extensions/dm-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -516
  181. package/extensions/dm-lens/dist/clients/dispatch/runners/trivy-config.js +0 -155
  182. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -285
  183. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -61
  184. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -61
  185. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -130
  186. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1067
  187. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -40
  188. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -117
  189. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -88
  190. package/extensions/dm-lens/dist/clients/dispatch/runners/utils.js +0 -8
  191. package/extensions/dm-lens/dist/clients/dispatch/runners/vale.js +0 -106
  192. package/extensions/dm-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -163
  193. package/extensions/dm-lens/dist/clients/dispatch/runners/yamllint.js +0 -83
  194. package/extensions/dm-lens/dist/clients/dispatch/runners/zig-check.js +0 -72
  195. package/extensions/dm-lens/dist/clients/dispatch/suppress-writer.js +0 -57
  196. package/extensions/dm-lens/dist/clients/dispatch/tool-profile.js +0 -30
  197. package/extensions/dm-lens/dist/clients/dispatch/types.js +0 -7
  198. package/extensions/dm-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  199. package/extensions/dm-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -31
  200. package/extensions/dm-lens/dist/clients/disposition-logger.js +0 -19
  201. package/extensions/dm-lens/dist/clients/disposition-publish.js +0 -88
  202. package/extensions/dm-lens/dist/clients/durable-store.js +0 -77
  203. package/extensions/dm-lens/dist/clients/effective-config.js +0 -160
  204. package/extensions/dm-lens/dist/clients/env-utils.js +0 -26
  205. package/extensions/dm-lens/dist/clients/error-class.js +0 -3
  206. package/extensions/dm-lens/dist/clients/eval-timestamp.js +0 -2
  207. package/extensions/dm-lens/dist/clients/event-loop-hold.js +0 -119
  208. package/extensions/dm-lens/dist/clients/event-loop-monitor.js +0 -84
  209. package/extensions/dm-lens/dist/clients/extension-log.js +0 -230
  210. package/extensions/dm-lens/dist/clients/extension-mode.js +0 -20
  211. package/extensions/dm-lens/dist/clients/feature-hints.js +0 -88
  212. package/extensions/dm-lens/dist/clients/file-kinds.js +0 -304
  213. package/extensions/dm-lens/dist/clients/file-role.js +0 -50
  214. package/extensions/dm-lens/dist/clients/file-time.js +0 -112
  215. package/extensions/dm-lens/dist/clients/file-utils.js +0 -630
  216. package/extensions/dm-lens/dist/clients/finding-delivery-gate.js +0 -129
  217. package/extensions/dm-lens/dist/clients/finding-identity.js +0 -23
  218. package/extensions/dm-lens/dist/clients/fix-worklog.js +0 -75
  219. package/extensions/dm-lens/dist/clients/format-events-publish.js +0 -230
  220. package/extensions/dm-lens/dist/clients/format-service.js +0 -154
  221. package/extensions/dm-lens/dist/clients/formatters-lazy.js +0 -8
  222. package/extensions/dm-lens/dist/clients/formatters.js +0 -1351
  223. package/extensions/dm-lens/dist/clients/freshness-cadence.js +0 -1
  224. package/extensions/dm-lens/dist/clients/freshness.js +0 -8
  225. package/extensions/dm-lens/dist/clients/generated-artifacts.js +0 -209
  226. package/extensions/dm-lens/dist/clients/generation-guard.js +0 -105
  227. package/extensions/dm-lens/dist/clients/git-guard.js +0 -770
  228. package/extensions/dm-lens/dist/clients/git-tracked-ignore.js +0 -120
  229. package/extensions/dm-lens/dist/clients/gitleaks-client.js +0 -282
  230. package/extensions/dm-lens/dist/clients/go-client.js +0 -47
  231. package/extensions/dm-lens/dist/clients/govulncheck-client.js +0 -411
  232. package/extensions/dm-lens/dist/clients/gradle-ktfmt-style.js +0 -177
  233. package/extensions/dm-lens/dist/clients/grammar-source.js +0 -224
  234. package/extensions/dm-lens/dist/clients/gzip-stage-write.js +0 -85
  235. package/extensions/dm-lens/dist/clients/hashline-anchor.js +0 -222
  236. package/extensions/dm-lens/dist/clients/hook-budgets.js +0 -16
  237. package/extensions/dm-lens/dist/clients/host-edit-normalize.js +0 -87
  238. package/extensions/dm-lens/dist/clients/host-ports.js +0 -35
  239. package/extensions/dm-lens/dist/clients/indent-retarget.js +0 -102
  240. package/extensions/dm-lens/dist/clients/inline-blocker-dispositions.js +0 -103
  241. package/extensions/dm-lens/dist/clients/install-diagnostics.js +0 -123
  242. package/extensions/dm-lens/dist/clients/installer/index.js +0 -3770
  243. package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -17
  244. package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh.js +0 -522
  245. package/extensions/dm-lens/dist/clients/instance-reaper-state.js +0 -3
  246. package/extensions/dm-lens/dist/clients/instance-reaper.js +0 -577
  247. package/extensions/dm-lens/dist/clients/instance-registry-lock.js +0 -131
  248. package/extensions/dm-lens/dist/clients/instance-registry.js +0 -452
  249. package/extensions/dm-lens/dist/clients/jscpd-client.js +0 -252
  250. package/extensions/dm-lens/dist/clients/json-cache-read.js +0 -25
  251. package/extensions/dm-lens/dist/clients/knip-client.js +0 -472
  252. package/extensions/dm-lens/dist/clients/language-policy.js +0 -242
  253. package/extensions/dm-lens/dist/clients/language-profile.js +0 -261
  254. package/extensions/dm-lens/dist/clients/language-registry.js +0 -424
  255. package/extensions/dm-lens/dist/clients/latency-logger.js +0 -192
  256. package/extensions/dm-lens/dist/clients/lazy-import.js +0 -14
  257. package/extensions/dm-lens/dist/clients/ledger-bounds.js +0 -21
  258. package/extensions/dm-lens/dist/clients/lens-config.js +0 -237
  259. package/extensions/dm-lens/dist/clients/lens-engine.js +0 -181
  260. package/extensions/dm-lens/dist/clients/lens-events.js +0 -105
  261. package/extensions/dm-lens/dist/clients/lens-flag-registry.js +0 -302
  262. package/extensions/dm-lens/dist/clients/lens-map.js +0 -980
  263. package/extensions/dm-lens/dist/clients/live-bus-emitter.js +0 -53
  264. package/extensions/dm-lens/dist/clients/log-cleanup.js +0 -138
  265. package/extensions/dm-lens/dist/clients/lsp/aggregation.js +0 -88
  266. package/extensions/dm-lens/dist/clients/lsp/cascade-tier.js +0 -203
  267. package/extensions/dm-lens/dist/clients/lsp/client.js +0 -2709
  268. package/extensions/dm-lens/dist/clients/lsp/config.js +0 -252
  269. package/extensions/dm-lens/dist/clients/lsp/content-limits.js +0 -21
  270. package/extensions/dm-lens/dist/clients/lsp/diagnostic-binding.js +0 -79
  271. package/extensions/dm-lens/dist/clients/lsp/document-drift.js +0 -312
  272. package/extensions/dm-lens/dist/clients/lsp/edits.js +0 -930
  273. package/extensions/dm-lens/dist/clients/lsp/index.js +0 -4448
  274. package/extensions/dm-lens/dist/clients/lsp/inferred-project.js +0 -121
  275. package/extensions/dm-lens/dist/clients/lsp/jvm-runtime.js +0 -194
  276. package/extensions/dm-lens/dist/clients/lsp/language.js +0 -23
  277. package/extensions/dm-lens/dist/clients/lsp/launch.js +0 -468
  278. package/extensions/dm-lens/dist/clients/lsp/lombok.js +0 -146
  279. package/extensions/dm-lens/dist/clients/lsp/path-utils.js +0 -1
  280. package/extensions/dm-lens/dist/clients/lsp/pending-aux-coverage.js +0 -90
  281. package/extensions/dm-lens/dist/clients/lsp/position-encoding.js +0 -28
  282. package/extensions/dm-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -42
  283. package/extensions/dm-lens/dist/clients/lsp/server.js +0 -2422
  284. package/extensions/dm-lens/dist/clients/lsp/session-roots.js +0 -52
  285. package/extensions/dm-lens/dist/clients/lsp/spawn-history.js +0 -15
  286. package/extensions/dm-lens/dist/clients/lsp/sync-kind.js +0 -30
  287. package/extensions/dm-lens/dist/clients/lsp/tsserver-sync.js +0 -156
  288. package/extensions/dm-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  289. package/extensions/dm-lens/dist/clients/lsp/wait-policy/classification.js +0 -36
  290. package/extensions/dm-lens/dist/clients/lsp/wait-policy/index.js +0 -3
  291. package/extensions/dm-lens/dist/clients/lsp/wait-policy/strategies.js +0 -175
  292. package/extensions/dm-lens/dist/clients/lsp/watch-queue.js +0 -40
  293. package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -267
  294. package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -7
  295. package/extensions/dm-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -93
  296. package/extensions/dm-lens/dist/clients/lsp-budget.js +0 -83
  297. package/extensions/dm-lens/dist/clients/lsp-document-symbols.js +0 -93
  298. package/extensions/dm-lens/dist/clients/lsp-lazy.js +0 -8
  299. package/extensions/dm-lens/dist/clients/lsp-mutation.js +0 -273
  300. package/extensions/dm-lens/dist/clients/lsp-status.js +0 -37
  301. package/extensions/dm-lens/dist/clients/map-with-concurrency.js +0 -20
  302. package/extensions/dm-lens/dist/clients/mcp/analyze.js +0 -251
  303. package/extensions/dm-lens/dist/clients/mcp/host-shim.js +0 -14
  304. package/extensions/dm-lens/dist/clients/mcp/ipc.js +0 -302
  305. package/extensions/dm-lens/dist/clients/mcp/review.js +0 -137
  306. package/extensions/dm-lens/dist/clients/mcp/session.js +0 -305
  307. package/extensions/dm-lens/dist/clients/memory-sampler.js +0 -107
  308. package/extensions/dm-lens/dist/clients/message-end-attribution.js +0 -18
  309. package/extensions/dm-lens/dist/clients/metrics-client.js +0 -80
  310. package/extensions/dm-lens/dist/clients/metrics-history.js +0 -255
  311. package/extensions/dm-lens/dist/clients/middle-man-analysis.js +0 -161
  312. package/extensions/dm-lens/dist/clients/model-provider.js +0 -31
  313. package/extensions/dm-lens/dist/clients/module-report-lsp.js +0 -140
  314. package/extensions/dm-lens/dist/clients/module-report.js +0 -1642
  315. package/extensions/dm-lens/dist/clients/mutating-tool.js +0 -377
  316. package/extensions/dm-lens/dist/clients/mutation-attribution.js +0 -168
  317. package/extensions/dm-lens/dist/clients/mutation-bridge.js +0 -102
  318. package/extensions/dm-lens/dist/clients/ndjson-logger.js +0 -471
  319. package/extensions/dm-lens/dist/clients/observed-mutation-sources.js +0 -43
  320. package/extensions/dm-lens/dist/clients/observed-mutation.js +0 -709
  321. package/extensions/dm-lens/dist/clients/oldtext-autopatch.js +0 -100
  322. package/extensions/dm-lens/dist/clients/opaque-mutation-scan.js +0 -313
  323. package/extensions/dm-lens/dist/clients/opengrep-client.js +0 -216
  324. package/extensions/dm-lens/dist/clients/opengrep-config.js +0 -52
  325. package/extensions/dm-lens/dist/clients/package-manager.js +0 -303
  326. package/extensions/dm-lens/dist/clients/package-root.js +0 -25
  327. package/extensions/dm-lens/dist/clients/partial-edit-apply.js +0 -337
  328. package/extensions/dm-lens/dist/clients/path-attribution-telemetry.js +0 -31
  329. package/extensions/dm-lens/dist/clients/path-keyed-map.js +0 -57
  330. package/extensions/dm-lens/dist/clients/path-utils.js +0 -476
  331. package/extensions/dm-lens/dist/clients/performance-report.js +0 -291
  332. package/extensions/dm-lens/dist/clients/persist-debounce.js +0 -37
  333. package/extensions/dm-lens/dist/clients/persistent-reverify.js +0 -165
  334. package/extensions/dm-lens/dist/clients/php-cs-fixer-config.js +0 -15
  335. package/extensions/dm-lens/dist/clients/pipeline.js +0 -1091
  336. package/extensions/dm-lens/dist/clients/post-autofix-notice.js +0 -19
  337. package/extensions/dm-lens/dist/clients/print-mode.js +0 -3
  338. package/extensions/dm-lens/dist/clients/probe-home-state.js +0 -62
  339. package/extensions/dm-lens/dist/clients/process-bridge.js +0 -20
  340. package/extensions/dm-lens/dist/clients/process-singletons.js +0 -69
  341. package/extensions/dm-lens/dist/clients/process-snapshot.js +0 -22
  342. package/extensions/dm-lens/dist/clients/project-changes.js +0 -112
  343. package/extensions/dm-lens/dist/clients/project-conventions.js +0 -152
  344. package/extensions/dm-lens/dist/clients/project-diagnostics/cache.js +0 -91
  345. package/extensions/dm-lens/dist/clients/project-diagnostics/extractors.js +0 -53
  346. package/extensions/dm-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -408
  347. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -64
  348. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -38
  349. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -21
  350. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -23
  351. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -28
  352. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -45
  353. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -34
  354. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -32
  355. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -79
  356. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -41
  357. package/extensions/dm-lens/dist/clients/project-diagnostics/scanner.js +0 -349
  358. package/extensions/dm-lens/dist/clients/project-diagnostics/types.js +0 -1
  359. package/extensions/dm-lens/dist/clients/project-lens-config.js +0 -437
  360. package/extensions/dm-lens/dist/clients/project-report.js +0 -527
  361. package/extensions/dm-lens/dist/clients/project-scale.js +0 -60
  362. package/extensions/dm-lens/dist/clients/project-scan-policy.js +0 -4
  363. package/extensions/dm-lens/dist/clients/project-snapshot-fingerprint.js +0 -39
  364. package/extensions/dm-lens/dist/clients/project-snapshot-persist-worker.js +0 -14
  365. package/extensions/dm-lens/dist/clients/project-snapshot.js +0 -1178
  366. package/extensions/dm-lens/dist/clients/project-trust.js +0 -104
  367. package/extensions/dm-lens/dist/clients/python-environment.js +0 -120
  368. package/extensions/dm-lens/dist/clients/python-provenance.js +0 -551
  369. package/extensions/dm-lens/dist/clients/quiet-window-config.js +0 -16
  370. package/extensions/dm-lens/dist/clients/quiet-window.js +0 -126
  371. package/extensions/dm-lens/dist/clients/read-bridge.js +0 -46
  372. package/extensions/dm-lens/dist/clients/read-expansion.js +0 -218
  373. package/extensions/dm-lens/dist/clients/read-guard-logger.js +0 -178
  374. package/extensions/dm-lens/dist/clients/read-guard-tool-lines.js +0 -1042
  375. package/extensions/dm-lens/dist/clients/read-guard.js +0 -1096
  376. package/extensions/dm-lens/dist/clients/recent-touches.js +0 -126
  377. package/extensions/dm-lens/dist/clients/redact/secrets.js +0 -386
  378. package/extensions/dm-lens/dist/clients/resource-sampler.js +0 -344
  379. package/extensions/dm-lens/dist/clients/reverse-deps.js +0 -227
  380. package/extensions/dm-lens/dist/clients/review-graph/builder.js +0 -3635
  381. package/extensions/dm-lens/dist/clients/review-graph/format.js +0 -30
  382. package/extensions/dm-lens/dist/clients/review-graph/git-identity.js +0 -115
  383. package/extensions/dm-lens/dist/clients/review-graph/import-resolvers.js +0 -325
  384. package/extensions/dm-lens/dist/clients/review-graph/persist-worker.js +0 -8
  385. package/extensions/dm-lens/dist/clients/review-graph/query.js +0 -195
  386. package/extensions/dm-lens/dist/clients/review-graph/revision-drift.js +0 -3
  387. package/extensions/dm-lens/dist/clients/review-graph/service.js +0 -60
  388. package/extensions/dm-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -54
  389. package/extensions/dm-lens/dist/clients/review-graph/symbol-id.js +0 -29
  390. package/extensions/dm-lens/dist/clients/review-graph/tsconfig-paths.js +0 -253
  391. package/extensions/dm-lens/dist/clients/review-graph/types.js +0 -1
  392. package/extensions/dm-lens/dist/clients/review-graph/workspace-modules.js +0 -374
  393. package/extensions/dm-lens/dist/clients/review-graph-logger.js +0 -51
  394. package/extensions/dm-lens/dist/clients/ruff-client.js +0 -109
  395. package/extensions/dm-lens/dist/clients/rules-scanner.js +0 -113
  396. package/extensions/dm-lens/dist/clients/run-duration.js +0 -9
  397. package/extensions/dm-lens/dist/clients/runtime-agent-end.js +0 -608
  398. package/extensions/dm-lens/dist/clients/runtime-config.js +0 -39
  399. package/extensions/dm-lens/dist/clients/runtime-context.js +0 -155
  400. package/extensions/dm-lens/dist/clients/runtime-coordinator.js +0 -817
  401. package/extensions/dm-lens/dist/clients/runtime-session.js +0 -1625
  402. package/extensions/dm-lens/dist/clients/runtime-tool-call.js +0 -868
  403. package/extensions/dm-lens/dist/clients/runtime-tool-result.js +0 -1317
  404. package/extensions/dm-lens/dist/clients/runtime-turn.js +0 -2333
  405. package/extensions/dm-lens/dist/clients/rust-client.js +0 -48
  406. package/extensions/dm-lens/dist/clients/safe-spawn.js +0 -1267
  407. package/extensions/dm-lens/dist/clients/sanitize.js +0 -174
  408. package/extensions/dm-lens/dist/clients/scan-utils.js +0 -7
  409. package/extensions/dm-lens/dist/clients/scratch-tree-policy.js +0 -57
  410. package/extensions/dm-lens/dist/clients/search-read-registration.js +0 -59
  411. package/extensions/dm-lens/dist/clients/secret-findings.js +0 -69
  412. package/extensions/dm-lens/dist/clients/security-scan-client.js +0 -208
  413. package/extensions/dm-lens/dist/clients/session-event-guard.js +0 -111
  414. package/extensions/dm-lens/dist/clients/session-lifecycle.js +0 -173
  415. package/extensions/dm-lens/dist/clients/session-start-observability.js +0 -47
  416. package/extensions/dm-lens/dist/clients/session-state-store.js +0 -63
  417. package/extensions/dm-lens/dist/clients/session-summary.js +0 -8
  418. package/extensions/dm-lens/dist/clients/sessionstart-logger.js +0 -22
  419. package/extensions/dm-lens/dist/clients/sg-runner.js +0 -627
  420. package/extensions/dm-lens/dist/clients/sgconfig.js +0 -280
  421. package/extensions/dm-lens/dist/clients/shared-checkout-guard.js +0 -177
  422. package/extensions/dm-lens/dist/clients/single-flight.js +0 -52
  423. package/extensions/dm-lens/dist/clients/situational-tool-telemetry.js +0 -85
  424. package/extensions/dm-lens/dist/clients/skills-resolver.js +0 -38
  425. package/extensions/dm-lens/dist/clients/slow-fs.js +0 -77
  426. package/extensions/dm-lens/dist/clients/smells-rollup.js +0 -118
  427. package/extensions/dm-lens/dist/clients/source-filter.js +0 -320
  428. package/extensions/dm-lens/dist/clients/source-walker.js +0 -96
  429. package/extensions/dm-lens/dist/clients/spawn-output-cap.js +0 -35
  430. package/extensions/dm-lens/dist/clients/spawn-timeout-cooldown.js +0 -51
  431. package/extensions/dm-lens/dist/clients/stale-marker.js +0 -1
  432. package/extensions/dm-lens/dist/clients/startup-scan.js +0 -222
  433. package/extensions/dm-lens/dist/clients/startup-timing.js +0 -67
  434. package/extensions/dm-lens/dist/clients/string-utils.js +0 -6
  435. package/extensions/dm-lens/dist/clients/subagent-mode.js +0 -40
  436. package/extensions/dm-lens/dist/clients/symbol-containment.js +0 -16
  437. package/extensions/dm-lens/dist/clients/symbol-types.js +0 -1
  438. package/extensions/dm-lens/dist/clients/test-runner-client.js +0 -1504
  439. package/extensions/dm-lens/dist/clients/test-runner-delivery.js +0 -260
  440. package/extensions/dm-lens/dist/clients/todo-scanner.js +0 -164
  441. package/extensions/dm-lens/dist/clients/tool-agreement.js +0 -377
  442. package/extensions/dm-lens/dist/clients/tool-config.js +0 -218
  443. package/extensions/dm-lens/dist/clients/tool-cwd.js +0 -235
  444. package/extensions/dm-lens/dist/clients/tool-definition.js +0 -22
  445. package/extensions/dm-lens/dist/clients/tool-event.js +0 -18
  446. package/extensions/dm-lens/dist/clients/tool-policy.js +0 -2393
  447. package/extensions/dm-lens/dist/clients/tool-probe.js +0 -5
  448. package/extensions/dm-lens/dist/clients/tool-render.js +0 -79
  449. package/extensions/dm-lens/dist/clients/tool-set-policy.js +0 -70
  450. package/extensions/dm-lens/dist/clients/tree-sitter-cache.js +0 -206
  451. package/extensions/dm-lens/dist/clients/tree-sitter-client.js +0 -2787
  452. package/extensions/dm-lens/dist/clients/tree-sitter-logger.js +0 -77
  453. package/extensions/dm-lens/dist/clients/tree-sitter-navigator.js +0 -198
  454. package/extensions/dm-lens/dist/clients/tree-sitter-query-loader.js +0 -318
  455. package/extensions/dm-lens/dist/clients/tree-sitter-shared.js +0 -87
  456. package/extensions/dm-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -957
  457. package/extensions/dm-lens/dist/clients/trivy-client.js +0 -305
  458. package/extensions/dm-lens/dist/clients/tui-fit.js +0 -26
  459. package/extensions/dm-lens/dist/clients/turn-context.js +0 -48
  460. package/extensions/dm-lens/dist/clients/turn-end/lane.js +0 -1
  461. package/extensions/dm-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -54
  462. package/extensions/dm-lens/dist/clients/turn-end/lanes/secrets.js +0 -131
  463. package/extensions/dm-lens/dist/clients/turn-summary-render.js +0 -56
  464. package/extensions/dm-lens/dist/clients/turn-summary.js +0 -84
  465. package/extensions/dm-lens/dist/clients/typos-config.js +0 -9
  466. package/extensions/dm-lens/dist/clients/user-notify.js +0 -15
  467. package/extensions/dm-lens/dist/clients/vanished-instance-marker.js +0 -18
  468. package/extensions/dm-lens/dist/clients/warm-attach.js +0 -210
  469. package/extensions/dm-lens/dist/clients/widget-state.js +0 -963
  470. package/extensions/dm-lens/dist/clients/word-index-logger.js +0 -21
  471. package/extensions/dm-lens/dist/clients/word-index-store.js +0 -222
  472. package/extensions/dm-lens/dist/clients/word-index.js +0 -1426
  473. package/extensions/dm-lens/dist/clients/workspace-topology.js +0 -261
  474. package/extensions/dm-lens/dist/clients/write-ordering-guard.js +0 -24
  475. package/extensions/dm-lens/dist/clients/zizmor-config.js +0 -160
  476. package/extensions/dm-lens/dist/i18n.js +0 -67
  477. package/extensions/dm-lens/dist/index.js +0 -1782
  478. package/extensions/dm-lens/dist/mcp/analyze-cli.js +0 -178
  479. package/extensions/dm-lens/dist/mcp/build-staleness.js +0 -55
  480. package/extensions/dm-lens/dist/mcp/cli.js +0 -78
  481. package/extensions/dm-lens/dist/mcp/server.js +0 -1208
  482. package/extensions/dm-lens/dist/mcp/worker.js +0 -33
  483. package/extensions/dm-lens/dist/scripts/lib/process-scan.mjs +0 -256
  484. package/extensions/dm-lens/dist/scripts/lib/skills-predicate.mjs +0 -53
  485. package/extensions/dm-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -45
  486. package/extensions/dm-lens/dist/tools/activate-tools.js +0 -61
  487. package/extensions/dm-lens/dist/tools/ast-grep-outline.js +0 -127
  488. package/extensions/dm-lens/dist/tools/ast-grep-replace.js +0 -226
  489. package/extensions/dm-lens/dist/tools/ast-grep-search.js +0 -687
  490. package/extensions/dm-lens/dist/tools/effective-config.js +0 -59
  491. package/extensions/dm-lens/dist/tools/lens-diagnostic-mark.js +0 -240
  492. package/extensions/dm-lens/dist/tools/lens-diagnostics.js +0 -1484
  493. package/extensions/dm-lens/dist/tools/lsp-diagnostics.js +0 -1088
  494. package/extensions/dm-lens/dist/tools/lsp-navigation.js +0 -1194
  495. package/extensions/dm-lens/dist/tools/lsp-structured-output.js +0 -277
  496. package/extensions/dm-lens/dist/tools/module-report.js +0 -332
  497. package/extensions/dm-lens/dist/tools/project-report.js +0 -72
  498. package/extensions/dm-lens/dist/tools/render-compact.js +0 -272
  499. package/extensions/dm-lens/dist/tools/scan-progress.js +0 -31
  500. package/extensions/dm-lens/dist/tools/shared.js +0 -26
  501. package/extensions/dm-lens/dist/tools/symbol-search.js +0 -133
  502. package/extensions/dm-mcp-adapter/dist/abort.js +0 -35
  503. package/extensions/dm-mcp-adapter/dist/agent-dir.js +0 -45
  504. package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +0 -392
  505. package/extensions/dm-mcp-adapter/dist/agent-plugin-provenance.js +0 -26
  506. package/extensions/dm-mcp-adapter/dist/bearer-command-resolver.js +0 -182
  507. package/extensions/dm-mcp-adapter/dist/claude-plugin-loader.js +0 -350
  508. package/extensions/dm-mcp-adapter/dist/config.js +0 -1310
  509. package/extensions/dm-mcp-adapter/dist/consent-manager.js +0 -84
  510. package/extensions/dm-mcp-adapter/dist/elicitation-handler.js +0 -308
  511. package/extensions/dm-mcp-adapter/dist/errors.js +0 -225
  512. package/extensions/dm-mcp-adapter/dist/http-ca.js +0 -71
  513. package/extensions/dm-mcp-adapter/dist/jev-client.js +0 -359
  514. package/extensions/dm-mcp-adapter/dist/jev-contracts.js +0 -1
  515. package/extensions/dm-mcp-adapter/dist/jev-key-store.js +0 -176
  516. package/extensions/dm-mcp-adapter/dist/json-schema-validator.js +0 -70
  517. package/extensions/dm-mcp-adapter/dist/lifecycle.js +0 -387
  518. package/extensions/dm-mcp-adapter/dist/logger.js +0 -115
  519. package/extensions/dm-mcp-adapter/dist/mcp-auth-fetch.js +0 -86
  520. package/extensions/dm-mcp-adapter/dist/mcp-auth-flow.js +0 -949
  521. package/extensions/dm-mcp-adapter/dist/mcp-auth.js +0 -997
  522. package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +0 -182
  523. package/extensions/dm-mcp-adapter/dist/mcp-callback-server.js +0 -381
  524. package/extensions/dm-mcp-adapter/dist/mcp-oauth-provider.js +0 -524
  525. package/extensions/dm-mcp-adapter/dist/mcp-probe.js +0 -151
  526. package/extensions/dm-mcp-adapter/dist/mcp-tasks.js +0 -300
  527. package/extensions/dm-mcp-adapter/dist/mcp-trace.js +0 -215
  528. package/extensions/dm-mcp-adapter/dist/metadata-cache.js +0 -267
  529. package/extensions/dm-mcp-adapter/dist/npx-resolver.js +0 -465
  530. package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +0 -135
  531. package/extensions/dm-mcp-adapter/dist/request-headers-command.js +0 -294
  532. package/extensions/dm-mcp-adapter/dist/resource-tools.js +0 -7
  533. package/extensions/dm-mcp-adapter/dist/runtime-owner.js +0 -94
  534. package/extensions/dm-mcp-adapter/dist/sampling-handler.js +0 -213
  535. package/extensions/dm-mcp-adapter/dist/secure-keyring.js +0 -191
  536. package/extensions/dm-mcp-adapter/dist/server-manager.js +0 -1514
  537. package/extensions/dm-mcp-adapter/dist/session-approvals.js +0 -127
  538. package/extensions/dm-mcp-adapter/dist/session-recovery.js +0 -82
  539. package/extensions/dm-mcp-adapter/dist/state.js +0 -1
  540. package/extensions/dm-mcp-adapter/dist/types.js +0 -286
  541. package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +0 -36
  542. package/extensions/dm-mcp-adapter/dist/ui-resource-handler.js +0 -204
  543. package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +0 -58
  544. package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +0 -26
  545. package/extensions/dm-mcp-adapter/dist/unix-socket-transport.js +0 -83
  546. package/extensions/dm-mcp-adapter/dist/utils.js +0 -382
@@ -1,2787 +0,0 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- import * as crypto from "node:crypto";
3
- import * as fs from "node:fs";
4
- import { createRequire } from "node:module";
5
- import * as path from "node:path";
6
- import { getDegradationLedgerGeneration, incrementDegradationCount, recordDegradation, recordDegradationOnce } from "./degradation-ledger.js";
7
- import { BoundedFifoMap } from "./bounded-cache.js";
8
- import { loadWebTreeSitter } from "./deps/web-tree-sitter.js";
9
- import { transientRetryDelayMs } from "./dispatch/runners/utils/availability-policy.js";
10
- import { getProjectIgnoreMatcher, isExcludedDirName } from "./file-utils.js";
11
- import { downloadGrammarDetailed, fileHasWasmMagic, grammarBlockReason, grammarFileSha256, isVendoredGrammar, LANGUAGE_TO_GRAMMAR, pinnedGrammarHash, vendoredGrammarRefusal, vendoredGrammarsDir } from "./grammar-source.js";
12
- import { getPackageRoot, resolvePackagePath } from "./package-root.js";
13
- import { resolveWebTreeSitterPackageDir } from "../scripts/lib/web-tree-sitter-dir.mjs";
14
- import { assertInstallAllowed, getProjectTrustGeneration } from "./project-trust.js";
15
- import { escapeRegExp } from "./string-utils.js";
16
- import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
17
- import { notifyUserDegradation } from "./user-notify.js";
18
- const _require = createRequire(import.meta.url);
19
- import { createTreeCacheCounters, deriveScanTreeCacheCapacity, TreeCache } from "./tree-sitter-cache.js";
20
- import { TreeSitterNavigator } from "./tree-sitter-navigator.js";
21
- import { isProvenSqlAlchemySessionReceiver, isSafePsycopgIdentifierComposition, isSqlAlchemyEntityQueryArgument, isSqlAlchemyStatementArgument, PYTHON_SQLALCHEMY_RECEIVER_NAMES, PYTHON_SQLALCHEMY_STATEMENT_BUILDERS } from "./python-provenance.js";
22
- import { TreeSitterQueryLoader } from "./tree-sitter-query-loader.js";
23
- const TREE_SITTER_MAX_SCAN_FILES = 20000;
24
- const QUERY_BATCH_MAX_LOAD_FAILURES = 3;
25
- const NO_NESTED_ANCHOR_VISIT_CAP = 1e4;
26
- const PYTHON_SQL_SINK_METHODS = new Set([
27
- "execute",
28
- "executemany",
29
- "query",
30
- "raw"
31
- ]);
32
- const TYPESCRIPT_SQL_KNOWN_PACKAGES = new Set([
33
- "pg",
34
- "mysql2",
35
- "better-sqlite3",
36
- "knex",
37
- "@prisma/client"
38
- ]);
39
- const TYPESCRIPT_SQL_IMPORTS = new WeakMap;
40
- const NOT_PARSED = { parsed: false };
41
- function createParserCounters() {
42
- return {
43
- parserInvocations: 0,
44
- parserDurationMs: 0,
45
- parserFailures: 0
46
- };
47
- }
48
- function grammarWriteBlockedReason(dir) {
49
- let probe = dir;
50
- while (!fs.existsSync(probe)) {
51
- const parent = path.dirname(probe);
52
- if (parent === probe)
53
- return "ENOENT";
54
- probe = parent;
55
- }
56
- try {
57
- fs.accessSync(probe, fs.constants.W_OK);
58
- return;
59
- } catch (err) {
60
- return err?.code ?? "EACCES";
61
- }
62
- }
63
- function grammarFileStamp(filePath) {
64
- try {
65
- const stat = fs.statSync(filePath);
66
- return stat.isFile() ? `${stat.size}:${stat.mtimeMs}` : undefined;
67
- } catch {
68
- return;
69
- }
70
- }
71
- function isTreeSitterWasmAbortError(error) {
72
- const message = error instanceof Error ? error.message : String(error);
73
- return message.includes("Aborted") || message.includes("abort()");
74
- }
75
- const RESOLUTION_ERROR_CODES = new Set([
76
- "ERR_MODULE_NOT_FOUND",
77
- "MODULE_NOT_FOUND",
78
- "ERR_INVALID_MODULE_SPECIFIER",
79
- "ERR_UNSUPPORTED_DIR_IMPORT",
80
- "ENOENT",
81
- "EMFILE",
82
- "EBUSY",
83
- "EAGAIN",
84
- "EPERM",
85
- "ETXTBSY"
86
- ]);
87
- function collectErrorCodes(err) {
88
- const codes = [];
89
- let current = err;
90
- const seen = new Set;
91
- while (current instanceof Error && !seen.has(current)) {
92
- seen.add(current);
93
- const code = current.code;
94
- if (typeof code === "string")
95
- codes.push(code);
96
- current = current.cause;
97
- }
98
- return codes;
99
- }
100
- function classifyWebTreeSitterLoadFailure(err) {
101
- const codes = collectErrorCodes(err);
102
- return codes.some((code) => RESOLUTION_ERROR_CODES.has(code)) ? "resolution" : "evaluation";
103
- }
104
-
105
- export class TreeSitterClient {
106
- initialized = false;
107
- initPromise = null;
108
- webTreeSitterLoadFailed = false;
109
- languages = new Map;
110
- parsers = new Map;
111
- treeCache;
112
- navigator = new TreeSitterNavigator;
113
- grammarsDir;
114
- grammarEnsurePromises = new Map;
115
- grammarRetryAtMs = new Map;
116
- verifiedGrammarPaths = new Map;
117
- decodeFailedGrammarPaths = new Map;
118
- verifiedGrammarVersionAt = new Map;
119
- staleGrammarVersionAt = new Map;
120
- staleReportedGrammarPaths = new Set;
121
- poisonedGrammarPaths = new Set;
122
- grammarFailureAttempts = new Map;
123
- grammarLastNotifiedDelayMs = new Map;
124
- grammarNotificationsLedgerGen = getDegradationLedgerGeneration();
125
- trustBlockedGrammarNotifications = new Set;
126
- trustNotificationsGeneration = getProjectTrustGeneration();
127
- ParserClass = null;
128
- LanguageLoader = null;
129
- static QUERY_CACHE_MAX_ENTRIES = 256;
130
- static QUERY_BATCH_CACHE_MAX_ENTRIES = 256;
131
- queryCache = new BoundedFifoMap(TreeSitterClient.QUERY_CACHE_MAX_ENTRIES);
132
- queryBatchCache = new BoundedFifoMap(TreeSitterClient.QUERY_BATCH_CACHE_MAX_ENTRIES);
133
- queryCacheCap() {
134
- const value = Number.parseInt(process.env.DM_LENS_TREE_SITTER_QUERY_CACHE_CAP ?? "", 10);
135
- return Number.isSafeInteger(value) && value > 0 ? value : TreeSitterClient.QUERY_CACHE_MAX_ENTRIES;
136
- }
137
- queryBatchCacheCap() {
138
- const value = Number.parseInt(process.env.DM_LENS_TREE_SITTER_QUERY_BATCH_CACHE_CAP ?? "", 10);
139
- return Number.isSafeInteger(value) && value > 0 ? value : TreeSitterClient.QUERY_BATCH_CACHE_MAX_ENTRIES;
140
- }
141
- cacheQuery(key, value) {
142
- this.queryCache.delete(key);
143
- const evicted = this.queryCache.setMaxEntries(this.queryCacheCap());
144
- evicted.push(...this.queryCache.set(key, value));
145
- for (const [, dropped] of evicted)
146
- dropped?.query?.delete?.();
147
- }
148
- cacheQueryBatch(key, value) {
149
- this.queryBatchCache.delete(key);
150
- const evicted = this.queryBatchCache.setMaxEntries(this.queryBatchCacheCap());
151
- evicted.push(...this.queryBatchCache.set(key, value));
152
- for (const [, dropped] of evicted)
153
- dropped?.query?.delete?.();
154
- }
155
- queryBatchLoadFailures = new Map;
156
- queryLoader = new TreeSitterQueryLoader;
157
- verbose;
158
- parserCounters = createParserCounters();
159
- parseCacheMeasurement = new AsyncLocalStorage;
160
- activeMeasurements = 0;
161
- onWasmAbort;
162
- wasmAborted = false;
163
- grammarDirResolutionDeps;
164
- constructor(verbose = false, onWasmAbort, grammarDirResolutionDeps) {
165
- this.grammarDirResolutionDeps = grammarDirResolutionDeps ?? {
166
- resolveAsset: (asset) => this.resolveWebTreeSitterAsset(asset),
167
- resolvePackage: (specifier) => _require.resolve(specifier),
168
- packageRoot: () => getPackageRoot(import.meta.url),
169
- cwd: () => process.cwd()
170
- };
171
- this.grammarsDir = this.findGrammarsDir();
172
- this.verbose = verbose;
173
- this.onWasmAbort = onWasmAbort;
174
- this.treeCache = new TreeCache(50, verbose, 4096, (key, amount) => {
175
- const measurement = this.parseCacheMeasurement.getStore();
176
- if (measurement)
177
- measurement[key] += amount;
178
- }, (error) => this.reportWasmAbort(error));
179
- }
180
- recordParserCounter(key, amount = 1) {
181
- this.parserCounters[key] += amount;
182
- const measurement = this.parseCacheMeasurement.getStore();
183
- if (measurement)
184
- measurement[key] += amount;
185
- }
186
- reportWasmAbort(error) {
187
- if (!isTreeSitterWasmAbortError(error))
188
- return false;
189
- if (!this.wasmAborted) {
190
- this.wasmAborted = true;
191
- recordDegradation({
192
- kind: "wasm-abort",
193
- subject: "web-tree-sitter",
194
- reason: error instanceof Error ? error.message : String(error)
195
- });
196
- this.onWasmAbort?.();
197
- }
198
- return true;
199
- }
200
- getRuntimeStats() {
201
- return {
202
- languagesLoaded: this.languages.size,
203
- parsersLoaded: this.parsers.size,
204
- queryCacheSize: this.queryCache.size,
205
- queryBatchCacheSize: this.queryBatchCache.size
206
- };
207
- }
208
- getParseCacheStats() {
209
- return {
210
- ...this.treeCache.getStats(),
211
- ...this.parserCounters
212
- };
213
- }
214
- ensureTreeCacheCapacity(fileCount) {
215
- this.treeCache.setMaxSize(deriveScanTreeCacheCapacity(fileCount, this.treeCache.getMaxSize()));
216
- }
217
- async withParseCacheMeasurement(work, onComplete) {
218
- const measurement = {
219
- ...createTreeCacheCounters(),
220
- ...createParserCounters()
221
- };
222
- this.activeMeasurements++;
223
- try {
224
- return await this.parseCacheMeasurement.run(measurement, async () => {
225
- try {
226
- return await work();
227
- } finally {
228
- const cacheStats = this.treeCache.getStats();
229
- try {
230
- onComplete({
231
- ...cacheStats,
232
- ...measurement,
233
- misses: measurement.lookups - measurement.hits
234
- });
235
- } catch {}
236
- }
237
- });
238
- } finally {
239
- if (--this.activeMeasurements === 0)
240
- this.parseCacheMeasurement.disable();
241
- }
242
- }
243
- dbg(msg) {
244
- if (this.verbose) {
245
- logTreeSitterDiagnostic({
246
- subsystem: "tree-sitter-client",
247
- level: "debug",
248
- message: msg
249
- });
250
- }
251
- }
252
- webTreeSitterPackageDir() {
253
- return resolveWebTreeSitterPackageDir({
254
- resolve: (specifier) => this.grammarDirResolutionDeps.resolvePackage(specifier),
255
- packageRoot: () => this.grammarDirResolutionDeps.packageRoot(),
256
- cwd: () => this.grammarDirResolutionDeps.cwd()
257
- });
258
- }
259
- resolveWebTreeSitterAsset(asset) {
260
- try {
261
- const resolved = this.grammarDirResolutionDeps.resolvePackage(`web-tree-sitter/${asset}`);
262
- if (fs.existsSync(resolved))
263
- return resolved;
264
- } catch {}
265
- const pkgDir = this.webTreeSitterPackageDir();
266
- if (pkgDir) {
267
- const candidate = path.join(pkgDir, asset);
268
- if (fs.existsSync(candidate))
269
- return candidate;
270
- }
271
- return;
272
- }
273
- _bundledGrammarsDir;
274
- bundledGrammarsDir() {
275
- if (this._bundledGrammarsDir)
276
- return this._bundledGrammarsDir;
277
- try {
278
- const dir = resolvePackagePath(import.meta.url, "grammars");
279
- if (fs.existsSync(dir))
280
- this._bundledGrammarsDir = dir;
281
- return this._bundledGrammarsDir;
282
- } catch {
283
- return;
284
- }
285
- }
286
- _vendoredGrammarsDir;
287
- vendoredGrammarsDir() {
288
- if (this._vendoredGrammarsDir)
289
- return this._vendoredGrammarsDir;
290
- try {
291
- const dir = vendoredGrammarsDir();
292
- if (fs.existsSync(dir))
293
- this._vendoredGrammarsDir = dir;
294
- return this._vendoredGrammarsDir;
295
- } catch {
296
- return;
297
- }
298
- }
299
- grammarSourceDirs() {
300
- const dirs = [];
301
- const push = (d) => {
302
- if (d && !dirs.includes(d))
303
- dirs.push(d);
304
- };
305
- push(this.vendoredGrammarsDir());
306
- push(this.bundledGrammarsDir());
307
- push(this.grammarsDir || undefined);
308
- push(this.resolveWebTreeSitterAsset("grammars"));
309
- return dirs;
310
- }
311
- resolveGrammarFile(grammarFile) {
312
- for (const dir of this.grammarSourceDirs()) {
313
- const candidate = path.join(dir, grammarFile);
314
- const stamp = grammarFileStamp(candidate);
315
- if (!stamp)
316
- continue;
317
- if (this.decodeFailedGrammarPaths.get(candidate) === stamp)
318
- continue;
319
- if (this.verifiedGrammarPaths.get(candidate) === stamp) {
320
- if (this.isGrammarVersionCurrent(candidate, grammarFile, stamp)) {
321
- return candidate;
322
- }
323
- continue;
324
- }
325
- if (fileHasWasmMagic(candidate)) {
326
- this.verifiedGrammarPaths.set(candidate, stamp);
327
- if (this.isGrammarVersionCurrent(candidate, grammarFile, stamp)) {
328
- return candidate;
329
- }
330
- continue;
331
- }
332
- this.verifiedGrammarPaths.delete(candidate);
333
- this.reportPoisonedGrammarFile(candidate, grammarFile);
334
- }
335
- return;
336
- }
337
- refreshGrammarSessionLatches() {
338
- const ledgerGen = getDegradationLedgerGeneration();
339
- if (ledgerGen === this.grammarNotificationsLedgerGen)
340
- return;
341
- this.grammarNotificationsLedgerGen = ledgerGen;
342
- this.grammarLastNotifiedDelayMs.clear();
343
- this.poisonedGrammarPaths.clear();
344
- this.staleReportedGrammarPaths.clear();
345
- }
346
- reportPoisonedGrammarFile(candidate, grammarFile) {
347
- this.refreshGrammarSessionLatches();
348
- if (this.poisonedGrammarPaths.has(candidate))
349
- return;
350
- this.poisonedGrammarPaths.add(candidate);
351
- logTreeSitterDiagnostic({
352
- subsystem: "tree-sitter-client",
353
- level: "warn",
354
- message: `ignoring ${candidate}: the file is not a wasm module (missing the \\0asm preamble) — ` + `most likely a captive-portal or proxy page written by an earlier download (#1548). ` + `dm-lens will treat the grammar as missing and try to fetch it again.`,
355
- metadata: { grammarFile, path: candidate, outcome: "not-wasm" }
356
- });
357
- incrementDegradationCount({
358
- kind: "grammar-blocked",
359
- subject: grammarFile,
360
- reason: "on-disk grammar file is not a wasm module — ignored, re-fetching"
361
- });
362
- }
363
- isGrammarVersionCurrent(candidate, grammarFile, stamp) {
364
- if (isVendoredGrammar(grammarFile))
365
- return true;
366
- if (this.staleGrammarVersionAt.get(candidate) === stamp) {
367
- this.reportStaleGrammarVersion(candidate, grammarFile);
368
- return false;
369
- }
370
- if (this.verifiedGrammarVersionAt.get(candidate) === stamp)
371
- return true;
372
- const pinnedHash = pinnedGrammarHash(grammarFile);
373
- if (!pinnedHash) {
374
- this.verifiedGrammarVersionAt.set(candidate, stamp);
375
- return true;
376
- }
377
- const actualHash = grammarFileSha256(candidate);
378
- if (actualHash === pinnedHash) {
379
- this.verifiedGrammarVersionAt.set(candidate, stamp);
380
- return true;
381
- }
382
- this.verifiedGrammarVersionAt.delete(candidate);
383
- this.staleGrammarVersionAt.set(candidate, stamp);
384
- this.reportStaleGrammarVersion(candidate, grammarFile);
385
- return false;
386
- }
387
- reportStaleGrammarVersion(candidate, grammarFile) {
388
- this.refreshGrammarSessionLatches();
389
- if (this.staleReportedGrammarPaths.has(candidate))
390
- return;
391
- this.staleReportedGrammarPaths.add(candidate);
392
- logTreeSitterDiagnostic({
393
- subsystem: "tree-sitter-client",
394
- level: "warn",
395
- message: `ignoring ${candidate}: its sha256 no longer matches the pinned grammar ` + `manifest — the pinned tree-sitter-wasms version (or a SOURCE_OVERRIDES entry) ` + `moved since this file was downloaded. dm-lens will treat the grammar as ` + `missing and re-fetch the current build (#1760).`,
396
- metadata: { grammarFile, path: candidate, outcome: "stale-version" }
397
- });
398
- incrementDegradationCount({
399
- kind: "grammar-blocked",
400
- subject: grammarFile,
401
- reason: "cached grammar no longer matches the pinned manifest hash — ignored, re-fetching"
402
- });
403
- }
404
- findGrammarsDir(deps = this.grammarDirResolutionDeps) {
405
- const grammarsDir = deps.resolveAsset("grammars");
406
- if (grammarsDir && fs.existsSync(path.join(grammarsDir, "tree-sitter-typescript.wasm"))) {
407
- return grammarsDir;
408
- }
409
- try {
410
- const wasmsOut = path.join(path.dirname(deps.resolvePackage("tree-sitter-wasms/package.json")), "out");
411
- if (fs.existsSync(wasmsOut))
412
- return wasmsOut;
413
- } catch {}
414
- const cwdWasms = path.join(deps.cwd(), "node_modules", "tree-sitter-wasms", "out");
415
- if (fs.existsSync(cwdWasms))
416
- return cwdWasms;
417
- return "";
418
- }
419
- grammarsWriteDir() {
420
- const pkgDir = this.webTreeSitterPackageDir();
421
- return pkgDir ? path.join(pkgDir, "grammars") : undefined;
422
- }
423
- async ensureGrammar(grammarFile) {
424
- if (this.resolveGrammarFile(grammarFile)) {
425
- return true;
426
- }
427
- if (!assertInstallAllowed(`tree-sitter grammar fetch: ${grammarFile}`)) {
428
- const unavailable = `tree-sitter grammar '${grammarFile}' is unavailable because the project is not trusted; ` + `runtime grammar downloads are disabled until trust is granted.`;
429
- logTreeSitterDiagnostic({
430
- subsystem: "tree-sitter-client",
431
- message: unavailable,
432
- metadata: { grammarFile, outcome: "trust-gated" }
433
- });
434
- recordDegradation({
435
- kind: "grammar-blocked",
436
- subject: grammarFile,
437
- reason: "runtime grammar download blocked because project is untrusted"
438
- });
439
- const generation = getProjectTrustGeneration();
440
- if (generation !== this.trustNotificationsGeneration) {
441
- this.trustNotificationsGeneration = generation;
442
- this.trustBlockedGrammarNotifications.clear();
443
- }
444
- if (!this.trustBlockedGrammarNotifications.has(grammarFile)) {
445
- this.trustBlockedGrammarNotifications.add(grammarFile);
446
- notifyUserDegradation(`dm-lens: ${unavailable}`);
447
- }
448
- return false;
449
- }
450
- const inflight = this.grammarEnsurePromises.get(grammarFile);
451
- if (inflight)
452
- return inflight;
453
- const retryAt = this.grammarRetryAtMs.get(grammarFile);
454
- if (retryAt !== undefined && Date.now() < retryAt) {
455
- return false;
456
- }
457
- const task = (async () => {
458
- try {
459
- return await this.fetchGrammar(grammarFile);
460
- } catch (err) {
461
- const message = err instanceof Error ? err.message : String(err);
462
- this.recordGrammarFailure(grammarFile, `The runtime grammar fetch threw an unexpected error (${message}).`, true, "download-failed");
463
- return false;
464
- }
465
- })();
466
- this.grammarEnsurePromises.set(grammarFile, task);
467
- task.finally(() => {
468
- if (this.grammarEnsurePromises.get(grammarFile) === task) {
469
- this.grammarEnsurePromises.delete(grammarFile);
470
- }
471
- }).catch(() => {});
472
- return task;
473
- }
474
- async fetchGrammar(grammarFile) {
475
- if (isVendoredGrammar(grammarFile)) {
476
- const { reason } = vendoredGrammarRefusal(grammarFile);
477
- this.recordGrammarFailure(grammarFile, reason ?? `${grammarFile} is missing from vendor/grammars/.`, false);
478
- return false;
479
- }
480
- const dir = this.grammarsDir && fs.existsSync(this.grammarsDir) ? this.grammarsDir : this.grammarsWriteDir();
481
- if (!dir) {
482
- this.recordGrammarFailure(grammarFile, "No grammars directory could be resolved for the runtime fetch: the web-tree-sitter package is not locatable from this runtime, from dm-lens's own package root, or from the working directory.", true, "write-dir-unresolvable");
483
- return false;
484
- }
485
- const writeBlock = grammarWriteBlockedReason(dir);
486
- if (writeBlock) {
487
- this.recordGrammarFailure(grammarFile, `The grammars directory ${dir} cannot be written to (${writeBlock}), so the runtime fetch has nowhere to land.`, true, "write-dir-unwritable");
488
- return false;
489
- }
490
- const { ok, retryable, reason } = await downloadGrammarDetailed(dir, grammarFile);
491
- if (ok) {
492
- if (!this.grammarsDir)
493
- this.grammarsDir = dir;
494
- this.grammarRetryAtMs.delete(grammarFile);
495
- this.grammarFailureAttempts.delete(grammarFile);
496
- this.grammarLastNotifiedDelayMs.delete(grammarFile);
497
- logTreeSitterDiagnostic({
498
- subsystem: "tree-sitter-client",
499
- level: "warn",
500
- message: `fetched missing tree-sitter grammar ${grammarFile} at runtime (install scripts were skipped by the package manager)`,
501
- metadata: { grammarFile, outcome: "fetched" }
502
- });
503
- } else {
504
- this.recordGrammarFailure(grammarFile, reason ?? "The package manager skipped install scripts and the runtime download failed.", retryable, "download-failed");
505
- }
506
- return ok;
507
- }
508
- recordGrammarFailure(grammarFile, detail, retryable, cause) {
509
- const attempts = (this.grammarFailureAttempts.get(grammarFile) ?? 0) + 1;
510
- this.grammarFailureAttempts.set(grammarFile, attempts);
511
- const retryDelayMs = retryable ? transientRetryDelayMs(attempts, "probe-timeout") : undefined;
512
- this.grammarRetryAtMs.set(grammarFile, retryDelayMs === undefined ? Number.POSITIVE_INFINITY : Date.now() + retryDelayMs);
513
- const remedy = cause === "write-dir-unresolvable" ? `This is a packaging/module-resolution fault, not a download problem: reinstall dm-lens so its ` + `web-tree-sitter dependency is present, or drop the wasm into dm-lens's own grammars/ directory.` : cause === "write-dir-unwritable" ? `This is a filesystem-permission fault, not a download problem: make that directory writable ` + `for the user dm-lens runs as, or reinstall dm-lens as that user.` : retryable ? `if the problem persists, allow the package manager's build scripts ` + `(pnpm approve-builds / bun trustedDependencies) or restore network access.` : `Fix: reinstall with a manager that runs postinstall, allow its build scripts ` + `(pnpm approve-builds / bun trustedDependencies), or restore network access.`;
514
- const unavailable = `tree-sitter grammar '${grammarFile}' is unavailable — symbol search, ` + `module reports and structural rules for this language will be degraded. ` + `${detail} ` + (retryable ? `dm-lens will retry automatically in ${Math.round(retryDelayMs / 1000)}s; ` : `The grammar source reports it does not exist (not a network problem), so this will not ` + `resolve on retry. `) + remedy;
515
- logTreeSitterDiagnostic({
516
- subsystem: "tree-sitter-client",
517
- message: unavailable,
518
- metadata: {
519
- grammarFile,
520
- outcome: "unavailable",
521
- retryable,
522
- retryDelayMs,
523
- attempts,
524
- ...cause ? { cause } : {}
525
- }
526
- });
527
- incrementDegradationCount({
528
- kind: "grammar-blocked",
529
- subject: grammarFile,
530
- reason: retryable ? `runtime grammar download failed — retryable (${detail.trim()})` : `runtime grammar download failed — durable (${detail.trim()})`
531
- });
532
- this.refreshGrammarSessionLatches();
533
- const notifyKey = retryDelayMs ?? -1;
534
- if (this.grammarLastNotifiedDelayMs.get(grammarFile) !== notifyKey) {
535
- this.grammarLastNotifiedDelayMs.set(grammarFile, notifyKey);
536
- notifyUserDegradation(`dm-lens: ${unavailable}`);
537
- }
538
- }
539
- recordGrammarLoadFailure(grammarPath, grammarFile, err) {
540
- const stamp = grammarFileStamp(grammarPath);
541
- this.verifiedGrammarPaths.delete(grammarPath);
542
- if (stamp)
543
- this.decodeFailedGrammarPaths.set(grammarPath, stamp);
544
- const message = err instanceof Error ? err.message : String(err);
545
- logTreeSitterDiagnostic({
546
- subsystem: "tree-sitter-client",
547
- level: "warn",
548
- message: `Language.load failed for ${grammarPath} even though the wasm preamble check ` + `passed (${message}) — most likely a truncated download (#1564). Treating the ` + `grammar as missing so the next demand re-fetches it.`,
549
- metadata: { grammarFile, path: grammarPath, outcome: "load-failed" }
550
- });
551
- this.recordGrammarFailure(grammarFile, `Language.load failed on a resolved grammar file (${message}).`, true);
552
- }
553
- async init() {
554
- if (this.wasmAborted)
555
- return false;
556
- if (this.initialized)
557
- return true;
558
- if (this.webTreeSitterLoadFailed)
559
- return false;
560
- if (this.initPromise)
561
- return this.initPromise;
562
- this.initPromise = (async () => {
563
- try {
564
- let mod;
565
- try {
566
- mod = await loadWebTreeSitter();
567
- } catch (err) {
568
- const classification = classifyWebTreeSitterLoadFailure(err);
569
- if (classification === "evaluation") {
570
- this.webTreeSitterLoadFailed = true;
571
- }
572
- recordDegradationOnce({
573
- kind: "web-tree-sitter-load-failed",
574
- subject: "web-tree-sitter",
575
- reason: (err instanceof Error ? err.message : String(err)) + (classification === "resolution" ? " (resolution failure — retryable)" : " (evaluation failure — latched for the session)")
576
- });
577
- throw err;
578
- }
579
- const anyMod = mod;
580
- const ParserClass = anyMod.Parser || anyMod.default || anyMod;
581
- if (!ParserClass || typeof ParserClass.init !== "function") {
582
- this.dbg("Parser class not found or missing init method");
583
- return false;
584
- }
585
- this.ParserClass = ParserClass;
586
- this.LanguageLoader = mod.Language;
587
- const wasmPath = this.resolveWebTreeSitterAsset("tree-sitter.wasm");
588
- if (!wasmPath) {
589
- this.dbg("Could not resolve tree-sitter.wasm");
590
- return false;
591
- }
592
- const wasmDir = path.dirname(wasmPath);
593
- this.dbg(`Looking for WASM at: ${wasmPath}, exists: ${fs.existsSync(wasmPath)}`);
594
- await ParserClass.init({
595
- locateFile: (scriptName) => {
596
- const fullPath = path.join(wasmDir, scriptName);
597
- this.dbg(`locateFile: ${scriptName} -> ${fullPath}`);
598
- return fullPath;
599
- }
600
- });
601
- this.initialized = true;
602
- return true;
603
- } catch (err) {
604
- this.reportWasmAbort(err);
605
- this.dbg(`Init error: ${err}`);
606
- return false;
607
- } finally {
608
- this.initPromise = null;
609
- }
610
- })();
611
- return this.initPromise;
612
- }
613
- resetLoadStateForSession() {
614
- this.webTreeSitterLoadFailed = false;
615
- }
616
- async loadLanguage(languageId) {
617
- if (this.wasmAborted)
618
- return null;
619
- this.dbg(`Loading language: ${languageId}`);
620
- if (this.languages.has(languageId)) {
621
- this.dbg(`Language ${languageId} already loaded`);
622
- return this.languages.get(languageId);
623
- }
624
- if (!this.ParserClass) {
625
- this.dbg(`ParserClass not initialized`);
626
- return null;
627
- }
628
- const grammarFile = LANGUAGE_TO_GRAMMAR[languageId];
629
- if (!grammarFile) {
630
- this.dbg(`No grammar file for ${languageId}`);
631
- return null;
632
- }
633
- const blockReason = grammarBlockReason(grammarFile);
634
- if (blockReason) {
635
- this.dbg(`Grammar ${grammarFile} blocked on this runtime — ${blockReason}`);
636
- recordDegradation({
637
- kind: "grammar-blocked",
638
- subject: grammarFile,
639
- reason: blockReason
640
- });
641
- return null;
642
- }
643
- let grammarPath = this.resolveGrammarFile(grammarFile);
644
- if (!grammarPath) {
645
- if (await this.ensureGrammar(grammarFile)) {
646
- grammarPath = this.resolveGrammarFile(grammarFile);
647
- }
648
- }
649
- this.dbg(`Grammar path: ${grammarPath}, exists: ${grammarPath && fs.existsSync(grammarPath)}`);
650
- if (!grammarPath || !fs.existsSync(grammarPath)) {
651
- this.dbg(`Grammar file not found: ${grammarPath}`);
652
- return null;
653
- }
654
- try {
655
- if (!this.LanguageLoader?.load) {
656
- this.dbg(`LanguageLoader.load not available`);
657
- return null;
658
- }
659
- this.dbg(`Calling Language.load...`);
660
- const language = await this.LanguageLoader.load(grammarPath);
661
- this.dbg(`Language loaded: ${language?.name || "unknown"}`);
662
- if (language) {
663
- this.languages.set(languageId, language);
664
- }
665
- return language;
666
- } catch (err) {
667
- if (!this.reportWasmAbort(err)) {
668
- this.recordGrammarLoadFailure(grammarPath, grammarFile, err);
669
- }
670
- this.dbg(`Language load error: ${err}`);
671
- return null;
672
- }
673
- }
674
- async getParser(languageId) {
675
- if (this.wasmAborted)
676
- return null;
677
- if (this.parsers.has(languageId)) {
678
- return this.parsers.get(languageId);
679
- }
680
- const language = await this.loadLanguage(languageId);
681
- if (!language || !this.ParserClass)
682
- return null;
683
- const parser = new this.ParserClass;
684
- parser.setLanguage(language);
685
- this.parsers.set(languageId, parser);
686
- return parser;
687
- }
688
- async parseFile(filePath, languageId, contentOverride) {
689
- const outcome = await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => tree);
690
- return outcome.parsed ? outcome.value : null;
691
- }
692
- async withParsedTree(filePath, languageId, contentOverride, consume) {
693
- return this.parseFileAndUse(filePath, languageId, contentOverride, consume);
694
- }
695
- async parseFileAndUse(filePath, languageId, contentOverride, consume) {
696
- this.dbg(`Parsing ${filePath} with language ${languageId}`);
697
- const parser = await this.getParser(languageId);
698
- if (!parser) {
699
- this.dbg(`Failed to get parser for ${languageId}`);
700
- return NOT_PARSED;
701
- }
702
- let tree;
703
- try {
704
- const content = contentOverride ?? fs.readFileSync(filePath, "utf-8");
705
- this.dbg(`File content length: ${content.length}`);
706
- const cachedTree = this.treeCache.get(filePath, content, languageId);
707
- if (cachedTree) {
708
- this.dbg(`Using cached tree for ${filePath}`);
709
- tree = cachedTree;
710
- } else {
711
- const parseStartedAt = performance.now();
712
- this.recordParserCounter("parserInvocations");
713
- try {
714
- tree = parser.parse(content);
715
- } catch (err) {
716
- this.recordParserCounter("parserFailures");
717
- throw err;
718
- } finally {
719
- this.recordParserCounter("parserDurationMs", performance.now() - parseStartedAt);
720
- }
721
- this.dbg(`Parsed, root node type: ${tree.rootNode.type}`);
722
- this.treeCache.set(filePath, content, languageId, tree);
723
- }
724
- } catch (err) {
725
- this.reportWasmAbort(err);
726
- this.dbg(`Parse error: ${err}`);
727
- return NOT_PARSED;
728
- }
729
- try {
730
- return { parsed: true, value: consume(tree) };
731
- } catch (error) {
732
- this.reportWasmAbort(error);
733
- throw error;
734
- }
735
- }
736
- extractInjections(filePath, content) {
737
- const injections = [];
738
- const sqlPattern = /\b(sql|query|execute)\s*`([^`]+)`/gi;
739
- let match;
740
- while ((match = sqlPattern.exec(content)) !== null) {
741
- const lines = content.slice(0, match.index).split(`
742
- `);
743
- injections.push({
744
- type: "sql",
745
- content: match[2],
746
- line: lines.length,
747
- column: lines[lines.length - 1].length
748
- });
749
- }
750
- const cssPattern = /\b(styled(?:\.\w+)?|css)\s*`([^`]+)`/gi;
751
- while ((match = cssPattern.exec(content)) !== null) {
752
- const lines = content.slice(0, match.index).split(`
753
- `);
754
- injections.push({
755
- type: "css",
756
- content: match[2],
757
- line: lines.length,
758
- column: lines[lines.length - 1].length
759
- });
760
- }
761
- const regexPattern = /new\s+RegExp\s*\(\s*`([^`]+)`/gi;
762
- while ((match = regexPattern.exec(content)) !== null) {
763
- const lines = content.slice(0, match.index).split(`
764
- `);
765
- injections.push({
766
- type: "regex",
767
- content: match[1],
768
- line: lines.length,
769
- column: lines[lines.length - 1].length
770
- });
771
- }
772
- this.dbg(`Found ${injections.length} injections in ${filePath}`);
773
- return injections;
774
- }
775
- isAvailable() {
776
- if (this.wasmAborted)
777
- return false;
778
- if (this.resolveGrammarFile("tree-sitter-typescript.wasm"))
779
- return true;
780
- const dir = this.findGrammarsDir();
781
- this.grammarsDir = dir;
782
- return !!dir && fs.existsSync(dir);
783
- }
784
- async isLanguageSupported(languageId) {
785
- if (this.wasmAborted)
786
- return false;
787
- if (!this.initialized)
788
- await this.init();
789
- const language = await this.loadLanguage(languageId);
790
- return language !== null;
791
- }
792
- getLanguage(languageId) {
793
- if (this.wasmAborted)
794
- return null;
795
- return this.languages.get(languageId) || null;
796
- }
797
- async structuralSearch(pattern, languageId, rootDir, options = {}) {
798
- if (!this.initialized) {
799
- const ok = await this.init();
800
- if (!ok)
801
- return [];
802
- }
803
- try {
804
- await this.queryLoader.loadQueries(rootDir);
805
- } catch (err) {
806
- this.dbg(`Failed to load queries for ${rootDir}: ${err}`);
807
- }
808
- this.dbg(`Compiling pattern: ${pattern.slice(0, 50)}...`);
809
- const compiled = await this.compileQuery(pattern, languageId);
810
- if (!compiled) {
811
- this.dbg(`Pattern compilation failed`);
812
- return [];
813
- }
814
- this.dbg(`Pattern compiled, metavars: ${compiled.metavars.join(", ")}`);
815
- const files = this.collectFiles(rootDir, languageId, options.fileFilter);
816
- this.dbg(`Scanning ${files.length} files...`);
817
- const matches = [];
818
- const maxResults = options.maxResults ?? 50;
819
- for (const file of files) {
820
- if (matches.length >= maxResults)
821
- break;
822
- const fileMatches = await this.searchFileWithQuery(file, compiled.query, compiled.metavars, languageId, pattern, compiled.postFilter, compiled.postFilterParams);
823
- matches.push(...fileMatches);
824
- }
825
- return matches.slice(0, maxResults);
826
- }
827
- async runQueryOnFile(queryDef, filePath, languageId, options = {}, contentOverride) {
828
- if (!this.initialized) {
829
- const ok = await this.init();
830
- if (!ok)
831
- return [];
832
- }
833
- const compiled = await this.compileRawQuery(queryDef.id, queryDef.query, queryDef.metavars, languageId, queryDef.post_filter, queryDef.post_filter_params);
834
- if (!compiled)
835
- return [];
836
- const matches = await this.searchFileWithQuery(filePath, compiled.query, compiled.metavars, languageId, queryDef.id, compiled.postFilter, compiled.postFilterParams, contentOverride);
837
- const maxResults = options.maxResults ?? 50;
838
- return matches.slice(0, maxResults);
839
- }
840
- async runQueriesOnFile(queryDefs, filePath, languageId, options = {}, contentOverride) {
841
- if (queryDefs.length === 0)
842
- return [];
843
- if (!this.initialized) {
844
- const ok = await this.init();
845
- if (!ok)
846
- return [];
847
- }
848
- const maxResults = options.maxResults ?? 50;
849
- const batch = await this.compileQueryBatch(queryDefs, languageId);
850
- if (!batch) {
851
- const results = [];
852
- for (const queryDef of queryDefs) {
853
- const matches = await this.runQueryOnFile(queryDef, filePath, languageId, options, contentOverride);
854
- for (const match of matches)
855
- results.push({ queryDef, match });
856
- }
857
- return results;
858
- }
859
- const perQuery = new Map;
860
- await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => {
861
- try {
862
- const rootNode = tree.rootNode;
863
- for (const match of batch.query.matches(rootNode)) {
864
- const owner = batch.ownerOfPattern[match.patternIndex];
865
- if (owner === undefined)
866
- continue;
867
- const bucket = perQuery.get(owner) ?? [];
868
- if (bucket.length >= maxResults)
869
- continue;
870
- const entry = batch.entries[owner];
871
- const captures = {};
872
- for (const capture of match.captures) {
873
- if (entry.metavars.includes(capture.name)) {
874
- captures[capture.name] = capture.node;
875
- }
876
- }
877
- if (!this.evaluatePredicates(batch.query, match))
878
- continue;
879
- if (entry.postFilter && !this.applyPostFilter(entry.postFilter, entry.postFilterParams, captures, rootNode)) {
880
- continue;
881
- }
882
- if (match.captures.length === 0)
883
- continue;
884
- const firstNode = match.captures[0].node;
885
- const textCaptures = {};
886
- for (const [name, node] of Object.entries(captures)) {
887
- textCaptures[name] = node.text;
888
- }
889
- bucket.push({
890
- file: filePath,
891
- line: firstNode.startPosition.row + 1,
892
- column: firstNode.startPosition.column + 1,
893
- matchedText: firstNode.text,
894
- nodeType: firstNode.type,
895
- captures: textCaptures
896
- });
897
- perQuery.set(owner, bucket);
898
- }
899
- } catch (err) {
900
- this.reportWasmAbort(err);
901
- this.dbg(`Batched query matching error: ${err}`);
902
- }
903
- });
904
- const results = [];
905
- for (let i = 0;i < batch.entries.length; i++) {
906
- for (const match of perQuery.get(i) ?? []) {
907
- results.push({ queryDef: batch.entries[i].queryDef, match });
908
- }
909
- }
910
- return results;
911
- }
912
- async compileQueryBatch(queryDefs, languageId) {
913
- const identity = crypto.createHash("sha256").update(JSON.stringify(queryDefs)).digest("hex");
914
- const cacheKey = this.getQueryCacheKey(`batch:${identity}`, languageId);
915
- const cached = this.queryBatchCache.get(cacheKey);
916
- if (cached !== undefined) {
917
- this.queryBatchCache.delete(cacheKey);
918
- this.queryBatchCache.set(cacheKey, cached);
919
- return cached;
920
- }
921
- const language = await this.loadLanguage(languageId);
922
- if (!language) {
923
- const failures = (this.queryBatchLoadFailures.get(cacheKey) ?? 0) + 1;
924
- if (failures >= QUERY_BATCH_MAX_LOAD_FAILURES) {
925
- this.dbg(`Batch: grammar for ${languageId} failed to load ${failures} times — caching miss`);
926
- this.queryBatchLoadFailures.delete(cacheKey);
927
- this.cacheQueryBatch(cacheKey, null);
928
- } else {
929
- this.queryBatchLoadFailures.set(cacheKey, failures);
930
- }
931
- return null;
932
- }
933
- this.queryBatchLoadFailures.delete(cacheKey);
934
- const build = async () => {
935
- const Query = (await loadWebTreeSitter()).Query;
936
- const entries = [];
937
- const sources = [];
938
- const ownerOfPattern = [];
939
- for (const queryDef of queryDefs) {
940
- let patternCount;
941
- try {
942
- const probe = new Query(language, queryDef.query);
943
- patternCount = probe.patternCount();
944
- probe.delete?.();
945
- } catch (err) {
946
- if (this.reportWasmAbort(err))
947
- return null;
948
- this.dbg(`Batch: skipping ${queryDef.id} for ${languageId}: ${err}`);
949
- this.reportQueryCompileFailure(queryDef.id, languageId, err);
950
- continue;
951
- }
952
- const owner = entries.length;
953
- entries.push({
954
- queryDef,
955
- metavars: queryDef.metavars ?? [],
956
- postFilter: queryDef.post_filter,
957
- postFilterParams: queryDef.post_filter_params
958
- });
959
- sources.push(queryDef.query);
960
- for (let i = 0;i < patternCount; i++)
961
- ownerOfPattern.push(owner);
962
- }
963
- if (entries.length === 0)
964
- return null;
965
- try {
966
- const query = new Query(language, sources.join(`
967
- `));
968
- if (query.patternCount() !== ownerOfPattern.length) {
969
- this.dbg(`Batch pattern count mismatch for ${languageId} (${query.patternCount()} vs ${ownerOfPattern.length}) — falling back`);
970
- return null;
971
- }
972
- return { query, entries, ownerOfPattern };
973
- } catch (err) {
974
- if (this.reportWasmAbort(err))
975
- return null;
976
- this.dbg(`Batch compile failed for ${languageId}: ${err}`);
977
- return null;
978
- }
979
- };
980
- const batch = await build();
981
- this.cacheQueryBatch(cacheKey, batch);
982
- return batch;
983
- }
984
- patternToQuery(pattern, languageId) {
985
- const loadedQuery = this.queryLoader.findMatchingQuery(pattern, languageId);
986
- if (loadedQuery) {
987
- this.dbg(`Using loaded query: ${loadedQuery.id}`);
988
- return {
989
- query: loadedQuery.query,
990
- metavars: loadedQuery.metavars,
991
- postFilter: loadedQuery.post_filter,
992
- postFilterParams: loadedQuery.post_filter_params,
993
- queryDef: loadedQuery
994
- };
995
- }
996
- return this.getInlinePattern(pattern);
997
- }
998
- getInlinePattern(pattern) {
999
- if (pattern.includes("async function") && pattern.includes("$NAME")) {
1000
- return {
1001
- query: `(function_declaration
1002
- "async"
1003
- name: (identifier) @NAME
1004
- parameters: (formal_parameters) @PARAMS
1005
- body: (statement_block) @BODY)`,
1006
- metavars: ["NAME", "PARAMS", "BODY"]
1007
- };
1008
- }
1009
- if (pattern.includes("console")) {
1010
- return {
1011
- query: `(call_expression
1012
- function: (member_expression
1013
- object: (identifier) @OBJ (#eq? @OBJ "console")
1014
- property: (property_identifier) @METHOD)
1015
- arguments: (arguments) @ARGS)`,
1016
- metavars: ["OBJ", "METHOD", "ARGS"]
1017
- };
1018
- }
1019
- if (pattern.includes("function $NAME") && pattern.includes("PARAMS")) {
1020
- return {
1021
- query: `(function_declaration
1022
- name: (identifier) @NAME
1023
- parameters: (formal_parameters) @PARAMS
1024
- body: (statement_block) @BODY)`,
1025
- metavars: ["NAME", "PARAMS", "BODY"],
1026
- postFilter: "count_params",
1027
- postFilterParams: { min_params: 6 }
1028
- };
1029
- }
1030
- if (pattern.includes(".then") && pattern.includes(".catch")) {
1031
- return {
1032
- query: `(call_expression
1033
- function: (member_expression
1034
- object: (call_expression
1035
- function: (member_expression
1036
- object: (call_expression
1037
- function: (member_expression
1038
- property: (property_identifier) @M1)
1039
- arguments: (arguments))
1040
- property: (property_identifier) @M2)
1041
- arguments: (arguments))
1042
- property: (property_identifier) @M3)
1043
- arguments: (arguments))
1044
- (#match? @M1 "^(then|catch)$")
1045
- (#match? @M2 "^(then|catch)$")
1046
- (#match? @M3 "^(then|catch)$")`,
1047
- metavars: ["M1", "M2", "M3"]
1048
- };
1049
- }
1050
- const simpleMatch = pattern.match(/\$([A-Z_][A-Z0-9_]*)/);
1051
- if (simpleMatch) {
1052
- const name = simpleMatch[1];
1053
- return {
1054
- query: `(identifier) @${name}`,
1055
- metavars: [name]
1056
- };
1057
- }
1058
- return { query: "", metavars: [] };
1059
- }
1060
- getQueryCacheKey(pattern, languageId) {
1061
- return `${languageId}:${pattern}`;
1062
- }
1063
- async compileQuery(pattern, languageId) {
1064
- const cacheKey = this.getQueryCacheKey(pattern, languageId);
1065
- if (this.queryCache.has(cacheKey)) {
1066
- this.dbg(`Query cache hit: ${cacheKey}`);
1067
- const cached = this.queryCache.get(cacheKey);
1068
- this.queryCache.delete(cacheKey);
1069
- this.queryCache.set(cacheKey, cached);
1070
- return cached;
1071
- }
1072
- const language = await this.loadLanguage(languageId);
1073
- if (!language) {
1074
- this.dbg(`Could not load language ${languageId}`);
1075
- return null;
1076
- }
1077
- const { query: queryStr, metavars, postFilter, postFilterParams } = this.patternToQuery(pattern, languageId);
1078
- this.dbg(`Query string: ${queryStr.slice(0, 100)}...`);
1079
- try {
1080
- const Query = (await loadWebTreeSitter()).Query;
1081
- const query = new Query(language, queryStr);
1082
- this.dbg(`Query compiled with ${query.patternCount()} patterns`);
1083
- const result = { query, metavars, postFilter, postFilterParams };
1084
- this.cacheQuery(cacheKey, result);
1085
- return result;
1086
- } catch (err) {
1087
- this.reportWasmAbort(err);
1088
- this.dbg(`Query compilation failed: ${err}`);
1089
- return null;
1090
- }
1091
- }
1092
- async compileRawQuery(queryId, queryStr, metavars, languageId, postFilter, postFilterParams) {
1093
- const cacheKey = this.getQueryCacheKey(`raw:${queryId}:${queryStr}`, languageId);
1094
- if (this.queryCache.has(cacheKey)) {
1095
- const cached = this.queryCache.get(cacheKey);
1096
- this.queryCache.delete(cacheKey);
1097
- this.queryCache.set(cacheKey, cached);
1098
- return cached;
1099
- }
1100
- const language = await this.loadLanguage(languageId);
1101
- if (!language)
1102
- return null;
1103
- try {
1104
- const Query = (await loadWebTreeSitter()).Query;
1105
- const query = new Query(language, queryStr);
1106
- const result = { query, metavars, postFilter, postFilterParams };
1107
- this.cacheQuery(cacheKey, result);
1108
- return result;
1109
- } catch (err) {
1110
- this.reportWasmAbort(err);
1111
- this.dbg(`Raw query compilation failed (${queryId}): ${err}`);
1112
- this.reportQueryCompileFailure(queryId, languageId, err);
1113
- return null;
1114
- }
1115
- }
1116
- reportedCompileFailures = new Set;
1117
- reportQueryCompileFailure(ruleId, languageId, err) {
1118
- const key = `${ruleId}:${languageId}`;
1119
- if (this.reportedCompileFailures.has(key))
1120
- return;
1121
- this.reportedCompileFailures.add(key);
1122
- logTreeSitterDiagnostic({
1123
- subsystem: "tree-sitter-client",
1124
- languageId,
1125
- message: `tree-sitter rule '${ruleId}' failed to compile against '${languageId}' — ` + `matches for this rule are silently dropped rather than reported. ` + `Fix the query in the rule definition to re-enable it. (${err})`,
1126
- metadata: { ruleId }
1127
- });
1128
- }
1129
- hasChildToken(node, token) {
1130
- return node.children?.some((child) => child.type === token || child.text === token);
1131
- }
1132
- bindingNames(node) {
1133
- const names = new Set;
1134
- if (!node)
1135
- return names;
1136
- const stack = [node];
1137
- while (stack.length > 0) {
1138
- const current = stack.pop();
1139
- if (current.type === "identifier")
1140
- names.add(current.text);
1141
- else if (current.type === "shorthand_property_identifier_pattern") {
1142
- names.add(current.text);
1143
- }
1144
- if (current.type === "property_identifier" || current.type === "type_identifier" || current.type === "computed_property_name")
1145
- continue;
1146
- if (current.type === "assignment_pattern" || current.type === "object_assignment_pattern") {
1147
- const left = current.childForFieldName?.("left");
1148
- if (left)
1149
- stack.push(left);
1150
- continue;
1151
- }
1152
- stack.push(...current.children ?? []);
1153
- }
1154
- return names;
1155
- }
1156
- containsYieldInFunctionBody(node, root = node) {
1157
- for (const child of node.children ?? []) {
1158
- if (child.type === "yield")
1159
- return true;
1160
- if (child !== root && ["function_definition", "class_definition", "lambda"].includes(child.type)) {
1161
- continue;
1162
- }
1163
- if (this.containsYieldInFunctionBody(child, root))
1164
- return true;
1165
- }
1166
- return false;
1167
- }
1168
- isLikelySqlAlchemyReceiver(text) {
1169
- const tail = text.split(".").pop() ?? text;
1170
- return PYTHON_SQLALCHEMY_RECEIVER_NAMES.has(tail.toLowerCase());
1171
- }
1172
- switchCaseBodyStatements(caseNode) {
1173
- const named = (caseNode.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
1174
- return named.slice(1);
1175
- }
1176
- hasFallthroughMarker(caseNode) {
1177
- const comments = [];
1178
- const body = this.switchCaseBodyStatements(caseNode);
1179
- const last = body[body.length - 1];
1180
- const lastEnd = last?.endIndex ?? caseNode.startIndex;
1181
- for (const c of caseNode.children ?? []) {
1182
- if (c.type.includes("comment") && c.startIndex >= lastEnd) {
1183
- comments.push(c);
1184
- }
1185
- }
1186
- const siblings = (caseNode.parent?.children ?? []).filter((c) => c.isNamed);
1187
- const index = siblings.findIndex((c) => c.startIndex === caseNode.startIndex);
1188
- const next = index >= 0 ? siblings[index + 1] : undefined;
1189
- if (next?.type.includes("comment"))
1190
- comments.push(next);
1191
- if (last) {
1192
- const NESTED_FN = new Set([
1193
- "function_declaration",
1194
- "function_expression",
1195
- "arrow_function",
1196
- "method_definition",
1197
- "generator_function",
1198
- "generator_function_declaration",
1199
- "class_declaration"
1200
- ]);
1201
- const stack = [last];
1202
- for (let visited = 0;stack.length > 0 && visited < 500; visited++) {
1203
- const node = stack.pop();
1204
- if (!node)
1205
- break;
1206
- if (node.type.includes("comment"))
1207
- comments.push(node);
1208
- if (!NESTED_FN.has(node.type)) {
1209
- stack.push(...node.children ?? []);
1210
- }
1211
- }
1212
- }
1213
- return comments.some((c) => /falls?\s?through/i.test(c.text));
1214
- }
1215
- statementTerminates(node, depth = 0) {
1216
- if (depth > 20)
1217
- return false;
1218
- const t = node.type;
1219
- const TERMINATORS = new Set([
1220
- "break_statement",
1221
- "return_statement",
1222
- "throw_statement",
1223
- "continue_statement"
1224
- ]);
1225
- if (TERMINATORS.has(t))
1226
- return true;
1227
- if (t === "statement_block" || t === "else_clause" || t === "catch_clause" || t === "finally_clause") {
1228
- const inner = (node.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
1229
- const last = inner[inner.length - 1];
1230
- if (!last)
1231
- return false;
1232
- return this.statementTerminates(last, depth + 1);
1233
- }
1234
- if (t === "try_statement") {
1235
- const tryBody = (node.children ?? []).find((c) => c.type === "statement_block");
1236
- const tryTerminates = tryBody ? this.statementTerminates(tryBody, depth + 1) : false;
1237
- const catches = (node.children ?? []).filter((c) => c.type === "catch_clause");
1238
- const fin = (node.children ?? []).find((c) => c.type === "finally_clause");
1239
- if (tryTerminates) {
1240
- return catches.every((c) => this.statementTerminates(c, depth + 1));
1241
- }
1242
- return !!fin && this.statementTerminates(fin, depth + 1);
1243
- }
1244
- if (t === "if_statement") {
1245
- const named = (node.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
1246
- const consequence = named[1];
1247
- const alternative = named[2];
1248
- if (!consequence || !alternative)
1249
- return false;
1250
- return this.statementTerminates(consequence, depth + 1) && this.statementTerminates(alternative, depth + 1);
1251
- }
1252
- return false;
1253
- }
1254
- nextSiblingIsSwitchLabel(caseNode) {
1255
- const siblings = (caseNode.parent?.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
1256
- const index = siblings.findIndex((c) => c.startIndex === caseNode.startIndex);
1257
- const next = index >= 0 ? siblings[index + 1] : undefined;
1258
- return next?.type === "switch_case" || next?.type === "switch_default";
1259
- }
1260
- bodyHasLoopExit(node, insideNestedLoop) {
1261
- const NESTS_BREAK = new Set([
1262
- "for_statement",
1263
- "for_in_statement",
1264
- "while_statement",
1265
- "do_statement",
1266
- "switch_statement"
1267
- ]);
1268
- const NESTED_FN = new Set([
1269
- "function_declaration",
1270
- "function_expression",
1271
- "arrow_function",
1272
- "method_definition",
1273
- "generator_function",
1274
- "generator_function_declaration",
1275
- "class_declaration"
1276
- ]);
1277
- for (const child of node.children ?? []) {
1278
- const t = child.type;
1279
- if (NESTED_FN.has(t))
1280
- continue;
1281
- if (t === "return_statement" || t === "throw_statement")
1282
- return true;
1283
- if (t === "break_statement") {
1284
- const labeled = (child.children ?? []).some((c) => c.isNamed);
1285
- if (!insideNestedLoop || labeled)
1286
- return true;
1287
- }
1288
- if (this.bodyHasLoopExit(child, insideNestedLoop || NESTS_BREAK.has(t))) {
1289
- return true;
1290
- }
1291
- }
1292
- return false;
1293
- }
1294
- enclosingBindingName(node) {
1295
- let cur = node?.parent;
1296
- for (let depth = 0;cur && depth < 12; depth++) {
1297
- const t = cur.type;
1298
- if (t === "variable_declarator") {
1299
- const name = cur.children?.find((c) => c.isNamed);
1300
- return name?.text ?? "";
1301
- }
1302
- if (t === "assignment_expression") {
1303
- return cur.children?.[0]?.text ?? "";
1304
- }
1305
- if (t === "statement_block" || t === "program" || t === "function_declaration" || t === "function_expression" || t === "arrow_function" || t === "method_definition") {
1306
- return "";
1307
- }
1308
- cur = cur.parent;
1309
- }
1310
- return "";
1311
- }
1312
- resolvesToFileLiteralConst(name, rootNode) {
1313
- const valueNode = this.resolveFileConstValueNode(name, rootNode);
1314
- if (!valueNode)
1315
- return false;
1316
- return this.isFixedUrlLiteralExpr(valueNode);
1317
- }
1318
- resolveFileConstValueNode(name, rootNode) {
1319
- const constDeclarators = [];
1320
- let hasNonConstBinding = false;
1321
- let hasReassignment = false;
1322
- const stack = [rootNode];
1323
- while (stack.length > 0) {
1324
- const node = stack.pop();
1325
- if (!node)
1326
- continue;
1327
- if (node.type === "variable_declarator") {
1328
- const nameNode = node.childForFieldName?.("name");
1329
- if (nameNode?.type === "identifier" && nameNode.text === name) {
1330
- const decl = node.parent;
1331
- const isConst = decl?.type === "lexical_declaration" && (decl.children ?? []).some((c) => c.type === "const");
1332
- if (isConst) {
1333
- constDeclarators.push(node);
1334
- } else {
1335
- hasNonConstBinding = true;
1336
- }
1337
- }
1338
- } else if (node.type === "assignment_expression" || node.type === "augmented_assignment_expression") {
1339
- const left = node.childForFieldName?.("left");
1340
- if (left?.type === "identifier" && left.text === name) {
1341
- hasReassignment = true;
1342
- } else if ((left?.type === "member_expression" || left?.type === "subscript_expression") && left.childForFieldName?.("object")?.type === "identifier" && left.childForFieldName?.("object")?.text === name) {
1343
- const searchOnly = left.type === "member_expression" && left.childForFieldName?.("property")?.text === "search";
1344
- if (!searchOnly) {
1345
- hasReassignment = true;
1346
- }
1347
- }
1348
- }
1349
- for (const child of node.children ?? [])
1350
- stack.push(child);
1351
- }
1352
- if (hasNonConstBinding || hasReassignment || constDeclarators.length !== 1) {
1353
- return null;
1354
- }
1355
- return constDeclarators[0].childForFieldName?.("value") ?? null;
1356
- }
1357
- isFixedUrlLiteralExpr(node) {
1358
- if (node.type === "string")
1359
- return true;
1360
- if (node.type === "template_string") {
1361
- return !(node.children ?? []).some((c) => c.type === "template_substitution");
1362
- }
1363
- return false;
1364
- }
1365
- isImportedBinding(name, rootNode) {
1366
- return this.importedAs(name, rootNode) !== null;
1367
- }
1368
- importedAs(name, rootNode) {
1369
- const stack = [rootNode];
1370
- while (stack.length > 0) {
1371
- const node = stack.pop();
1372
- if (!node)
1373
- continue;
1374
- if (node.type === "import_specifier") {
1375
- const nameNode = node.childForFieldName?.("name");
1376
- const aliasNode = node.childForFieldName?.("alias");
1377
- const local = (aliasNode ?? nameNode)?.text;
1378
- if (local === name)
1379
- return nameNode?.text ?? null;
1380
- } else if (node.type === "namespace_import" || node.type === "import_clause") {
1381
- for (const c of node.children ?? []) {
1382
- if (c.type === "identifier" && c.text === name)
1383
- return name;
1384
- }
1385
- }
1386
- for (const child of node.children ?? [])
1387
- stack.push(child);
1388
- }
1389
- return null;
1390
- }
1391
- isFixedUrlBaseExpr(base, rootNode) {
1392
- if (base.type === "string")
1393
- return true;
1394
- if (base.type === "identifier") {
1395
- return this.isFixedBaseIdentifier(base, rootNode);
1396
- }
1397
- if (base.type === "template_string") {
1398
- for (const child of base.children ?? []) {
1399
- if (child.type !== "template_substitution")
1400
- continue;
1401
- const inner = (child.children ?? []).find((c) => c.isNamed);
1402
- if (!inner || inner.type !== "identifier" || !this.isFixedBaseIdentifier(inner, rootNode)) {
1403
- return false;
1404
- }
1405
- }
1406
- return true;
1407
- }
1408
- return false;
1409
- }
1410
- isFixedBaseIdentifier(ident, rootNode) {
1411
- const name = ident.text;
1412
- if (!this.isShadowedByEnclosingParam(ident, name) && this.resolvesToFileLiteralConst(name, rootNode)) {
1413
- return true;
1414
- }
1415
- return this.isImportedBinding(name, rootNode);
1416
- }
1417
- isShadowedByEnclosingParam(node, name) {
1418
- const FUNCTION_TYPES = new Set([
1419
- "function_declaration",
1420
- "function_expression",
1421
- "generator_function",
1422
- "generator_function_declaration",
1423
- "arrow_function",
1424
- "method_definition"
1425
- ]);
1426
- let current = node.parent;
1427
- while (current) {
1428
- if (FUNCTION_TYPES.has(current.type)) {
1429
- const bare = current.childForFieldName?.("parameter");
1430
- if (bare?.type === "identifier" && bare.text === name)
1431
- return true;
1432
- const params = current.childForFieldName?.("parameters");
1433
- if (params && this.paramsBindName(params, name))
1434
- return true;
1435
- }
1436
- current = current.parent;
1437
- }
1438
- return false;
1439
- }
1440
- paramsBindName(params, name) {
1441
- for (const param of params.children ?? []) {
1442
- if (!param.isNamed)
1443
- continue;
1444
- const pattern = param.type === "required_parameter" || param.type === "optional_parameter" ? param.childForFieldName?.("pattern") : param;
1445
- if (pattern && this.patternBindsName(pattern, name))
1446
- return true;
1447
- }
1448
- return false;
1449
- }
1450
- patternBindsName(pattern, name) {
1451
- if (pattern.type === "identifier")
1452
- return pattern.text === name;
1453
- const stack = [pattern];
1454
- while (stack.length > 0) {
1455
- const n = stack.pop();
1456
- if (!n)
1457
- continue;
1458
- if ((n.type === "identifier" || n.type === "shorthand_property_identifier_pattern") && n.text === name) {
1459
- return true;
1460
- }
1461
- if (n.type === "assignment_pattern") {
1462
- const left = n.childForFieldName?.("left") ?? n.children?.[0];
1463
- if (left)
1464
- stack.push(left);
1465
- continue;
1466
- }
1467
- for (const c of n.children ?? [])
1468
- stack.push(c);
1469
- }
1470
- return false;
1471
- }
1472
- resolvesToFixedNewUrlConst(name, rootNode) {
1473
- const value = this.resolveFileConstValueNode(name, rootNode);
1474
- if (!value || value.type !== "new_expression")
1475
- return false;
1476
- const ctor = value.childForFieldName?.("constructor");
1477
- const ctorName = ctor?.text ?? "";
1478
- const isUrlCtor = ctorName === "URL" || ctor?.type === "identifier" && this.importedAs(ctorName, rootNode) === "URL";
1479
- if (!isUrlCtor)
1480
- return false;
1481
- const args = (value.childForFieldName?.("arguments")?.children ?? []).filter((c) => c.isNamed && c.type !== "comment");
1482
- if (args.length === 0)
1483
- return false;
1484
- if (!this.isFixedUrlLiteralExpr(args[0]))
1485
- return false;
1486
- if (args.length === 1)
1487
- return true;
1488
- return this.isFixedUrlBaseExpr(args[1], rootNode);
1489
- }
1490
- newUrlBaseVarName(urlNode) {
1491
- let member;
1492
- if (urlNode.type === "call_expression") {
1493
- member = urlNode.childForFieldName?.("function");
1494
- } else if (urlNode.type === "member_expression") {
1495
- member = urlNode;
1496
- }
1497
- if (!member || member.type !== "member_expression")
1498
- return null;
1499
- const prop = member.childForFieldName?.("property")?.text;
1500
- if (prop !== "toString" && prop !== "href")
1501
- return null;
1502
- const object = member.childForFieldName?.("object");
1503
- if (object?.type !== "identifier")
1504
- return null;
1505
- return object.text;
1506
- }
1507
- isSafeSqlAlchemyExpressionCall(node) {
1508
- if (node.type !== "call")
1509
- return false;
1510
- const callee = node.children?.[0]?.text ?? "";
1511
- const expression = node.text;
1512
- for (const name of PYTHON_SQLALCHEMY_STATEMENT_BUILDERS) {
1513
- if (callee === name || expression.startsWith(`${name}(`))
1514
- return true;
1515
- }
1516
- return false;
1517
- }
1518
- applyPostFilter(postFilter, postFilterParams, captures, rootNode) {
1519
- function extractSlots(classNode) {
1520
- const classText = classNode.text ?? "";
1521
- if (!classText.includes("__slots__"))
1522
- return null;
1523
- const body = classNode.children?.find((c) => c.type === "block");
1524
- if (!body)
1525
- return null;
1526
- const slots = [];
1527
- for (const stmt of body.children ?? []) {
1528
- if (stmt.type !== "expression_statement")
1529
- continue;
1530
- const assignment = stmt.children?.find((c) => c.type === "assignment");
1531
- if (!assignment)
1532
- continue;
1533
- const lhsText = (assignment.children?.[0]?.text ?? "").trim();
1534
- if (lhsText !== "__slots__")
1535
- continue;
1536
- const rhs = assignment.children?.[2];
1537
- if (!rhs)
1538
- continue;
1539
- if (rhs.type === "string") {
1540
- const s = (rhs.text ?? "").replace(/^["']|["']$/g, "");
1541
- if (s)
1542
- slots.push(s);
1543
- } else if (rhs.type === "tuple" || rhs.type === "list") {
1544
- for (const el of rhs.children ?? []) {
1545
- if (!el.isNamed)
1546
- continue;
1547
- if (el.type === "string") {
1548
- slots.push((el.text ?? "").replace(/^["']|["']$/g, ""));
1549
- }
1550
- }
1551
- }
1552
- break;
1553
- }
1554
- return slots;
1555
- }
1556
- switch (postFilter) {
1557
- case "no_nested_anchor_chain": {
1558
- try {
1559
- const outer = captures.OUTER_ELEMENT;
1560
- if (!outer) {
1561
- this.reportPostFilterFailure(postFilter, "missing OUTER_ELEMENT capture");
1562
- return true;
1563
- }
1564
- const isAnchorElement = (node) => {
1565
- if (node.type !== "jsx_element")
1566
- return false;
1567
- const opening = node.childForFieldName?.("open_tag") ?? node.children?.find((child) => child.type === "jsx_opening_element");
1568
- const name = opening?.childForFieldName?.("name") ?? opening?.children?.find((child) => child.type === "identifier");
1569
- return name?.text === "a";
1570
- };
1571
- let visited = 0;
1572
- let ancestor = outer.parent;
1573
- while (ancestor) {
1574
- if (++visited > NO_NESTED_ANCHOR_VISIT_CAP) {
1575
- this.reportPostFilterFailure(postFilter, `ancestor walk exceeded ${NO_NESTED_ANCHOR_VISIT_CAP} nodes`);
1576
- return true;
1577
- }
1578
- if (isAnchorElement(ancestor))
1579
- return false;
1580
- ancestor = ancestor.parent;
1581
- }
1582
- const pending = [...outer.children ?? []];
1583
- while (pending.length > 0) {
1584
- const node = pending.pop();
1585
- if (!node)
1586
- continue;
1587
- if (++visited > NO_NESTED_ANCHOR_VISIT_CAP) {
1588
- this.reportPostFilterFailure(postFilter, `descendant walk exceeded ${NO_NESTED_ANCHOR_VISIT_CAP} nodes`);
1589
- return true;
1590
- }
1591
- if (isAnchorElement(node))
1592
- return true;
1593
- pending.push(...node.children ?? []);
1594
- }
1595
- return false;
1596
- } catch (error) {
1597
- const reason = error instanceof Error ? error.message : String(error);
1598
- this.reportPostFilterFailure(postFilter, `tree walk failed${reason ? `: ${reason}` : ""}`);
1599
- return true;
1600
- }
1601
- }
1602
- case "differs_only_by_case": {
1603
- try {
1604
- const field = captures.FIELD?.text ?? "";
1605
- const method = captures.METHOD?.text ?? "";
1606
- return field !== method && field.toLocaleLowerCase() === method.toLocaleLowerCase();
1607
- } catch {
1608
- return true;
1609
- }
1610
- }
1611
- case "in_default_package": {
1612
- try {
1613
- if (!rootNode)
1614
- return true;
1615
- return !(rootNode.children ?? []).some((node) => node.type === "package_declaration");
1616
- } catch {
1617
- return true;
1618
- }
1619
- }
1620
- case "missing_mimetype_and_download_name": {
1621
- try {
1622
- const firstArg = captures.FIRST_ARG;
1623
- if (!firstArg)
1624
- return true;
1625
- if (firstArg.type === "string")
1626
- return false;
1627
- const args = firstArg.parent;
1628
- if (!args)
1629
- return true;
1630
- return !(args.children ?? []).some((node) => {
1631
- if (node.type !== "keyword_argument")
1632
- return false;
1633
- const name = node.childForFieldName?.("name")?.text;
1634
- return name === "mimetype" || name === "download_name";
1635
- });
1636
- } catch {
1637
- return true;
1638
- }
1639
- }
1640
- case "missing_super_call": {
1641
- try {
1642
- const body = captures.BODY;
1643
- const method = captures.METHOD?.text ?? "";
1644
- if (!body || !method)
1645
- return true;
1646
- const stack = [body];
1647
- for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
1648
- const node = stack.pop();
1649
- if (!node)
1650
- break;
1651
- if (node.type === "method_invocation" && node.childForFieldName?.("object")?.text === "super" && node.childForFieldName?.("name")?.text === method) {
1652
- return false;
1653
- }
1654
- stack.push(...node.children ?? []);
1655
- }
1656
- return true;
1657
- } catch {
1658
- return true;
1659
- }
1660
- }
1661
- case "no_assertion_call": {
1662
- try {
1663
- const body = captures.BODY;
1664
- if (!body)
1665
- return true;
1666
- const assertionNames = /^(?:assert\w*|fail|verify|expect|check|assume\w*)$/i;
1667
- const stack = [body];
1668
- for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
1669
- const node = stack.pop();
1670
- if (!node)
1671
- break;
1672
- if (node.type === "method_invocation") {
1673
- const name = node.childForFieldName?.("name")?.text ?? "";
1674
- if (assertionNames.test(name))
1675
- return false;
1676
- }
1677
- stack.push(...node.children ?? []);
1678
- }
1679
- return true;
1680
- } catch {
1681
- return true;
1682
- }
1683
- }
1684
- case "not_closed_or_try_with_resources": {
1685
- try {
1686
- const declaration = captures.DECL;
1687
- const resource = captures.RESOURCE?.text ?? "";
1688
- if (!declaration || !resource)
1689
- return true;
1690
- const scope = this.navigator.findParent(declaration, [
1691
- "method_declaration",
1692
- "constructor_declaration",
1693
- "block"
1694
- ]) ?? rootNode;
1695
- if (!scope)
1696
- return true;
1697
- const resourceWord = new RegExp(`\\b${escapeRegExp(resource)}\\b`);
1698
- const stack = [scope];
1699
- for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
1700
- const node = stack.pop();
1701
- if (!node)
1702
- break;
1703
- if (node.type === "method_invocation" && node.childForFieldName?.("object")?.text === resource && node.childForFieldName?.("name")?.text === "close") {
1704
- return false;
1705
- }
1706
- if (node.type === "resource" && resourceWord.test(node.text ?? "")) {
1707
- return false;
1708
- }
1709
- stack.push(...node.children ?? []);
1710
- }
1711
- return true;
1712
- } catch {
1713
- return true;
1714
- }
1715
- }
1716
- case "same_method_no_base_case": {
1717
- try {
1718
- const method = captures.NAME?.text ?? "";
1719
- if (!method || captures.RECURSE?.text !== method)
1720
- return false;
1721
- const call = captures.CALL;
1722
- const declaration = call ? this.navigator.findParent(call, ["method_declaration"]) : undefined;
1723
- if (!declaration)
1724
- return true;
1725
- const stack = [declaration];
1726
- for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
1727
- const node = stack.pop();
1728
- if (!node)
1729
- break;
1730
- if (node.type === "if_statement" || node.type === "switch_expression" || node.type === "switch_statement" || node.type === "while_statement" || node.type === "do_statement" || node.type === "for_statement" || node.type === "enhanced_for_statement" || node.type === "ternary_expression") {
1731
- return false;
1732
- }
1733
- stack.push(...node.children ?? []);
1734
- }
1735
- return stack.length > 0 ? false : true;
1736
- } catch {
1737
- return true;
1738
- }
1739
- }
1740
- case "memset_for_sensitive_data": {
1741
- try {
1742
- const destination = captures.DEST?.text ?? "";
1743
- const value = captures.VALUE?.text?.trim() ?? "";
1744
- if (!/^(?:0|0x0+|NULL|nullptr)$/.test(value))
1745
- return false;
1746
- return /(?:pass(?:word)?|passwd|secret|token|api_?key|private_?key|credential|auth|pin)/i.test(destination);
1747
- } catch {
1748
- return true;
1749
- }
1750
- }
1751
- case "unsafe_regex_dynamic_identifier": {
1752
- try {
1753
- const interpolationNode = captures.INTERPOLATION;
1754
- const interpolation = interpolationNode?.text ?? "";
1755
- const identifier = interpolation.match(/^\$\{\s*([A-Za-z_$][\w$]*)\s*\}$/)?.[1];
1756
- if (!identifier || !rootNode || !interpolationNode)
1757
- return true;
1758
- const useRow = interpolationNode.startPosition.row;
1759
- const hasEscapeSignal = (text) => /\b(?:escape|Escape)\w*\s*\(/.test(text) || /\.\s*replace\s*\(\s*\//.test(text);
1760
- let lastWriteRow = -1;
1761
- let lastWriteSafe = false;
1762
- const consider = (row, valueText) => {
1763
- if (row >= useRow || row < lastWriteRow)
1764
- return;
1765
- lastWriteRow = row;
1766
- lastWriteSafe = valueText !== undefined && hasEscapeSignal(valueText);
1767
- };
1768
- const stack = [rootNode];
1769
- while (stack.length > 0) {
1770
- const node = stack.pop();
1771
- if (!node)
1772
- continue;
1773
- if (node.type === "variable_declarator") {
1774
- const name = node.childForFieldName?.("name");
1775
- if (name?.text === identifier) {
1776
- const initializer = node.childForFieldName?.("value");
1777
- consider(node.startPosition.row, initializer?.text);
1778
- }
1779
- } else if (node.type === "assignment_expression") {
1780
- const left = node.childForFieldName?.("left");
1781
- if (left?.type === "identifier" && left.text === identifier) {
1782
- const right = node.childForFieldName?.("right");
1783
- consider(node.startPosition.row, right?.text);
1784
- }
1785
- }
1786
- stack.push(...node.children);
1787
- }
1788
- return lastWriteRow >= 0 && lastWriteSafe ? false : true;
1789
- } catch {
1790
- return true;
1791
- }
1792
- }
1793
- case "is_generator_with_valued_return": {
1794
- const returnNode = captures.RETURN;
1795
- const functionNode = captures.FUNCTION ?? (returnNode ? this.navigator.findParent(returnNode, ["function_definition"]) : undefined);
1796
- if (!functionNode)
1797
- return false;
1798
- if (this.hasChildToken(functionNode, "async"))
1799
- return false;
1800
- return this.containsYieldInFunctionBody(functionNode);
1801
- }
1802
- case "count_params": {
1803
- const paramsNode = captures.PARAMS;
1804
- if (!paramsNode)
1805
- return true;
1806
- const paramCount = paramsNode.children.filter((c) => {
1807
- if (c.type !== "required_parameter")
1808
- return false;
1809
- if (c.text.includes("="))
1810
- return false;
1811
- if (c.children?.some((ch) => ch.text === "?"))
1812
- return false;
1813
- return true;
1814
- }).length;
1815
- return paramCount >= (postFilterParams?.min_params ?? 6);
1816
- }
1817
- case "empty_body": {
1818
- const bodyNode = captures.BODY;
1819
- if (!bodyNode)
1820
- return true;
1821
- const meaningful = bodyNode.children.filter((c) => c.isNamed && c.type !== "comment" && c.type !== "line_comment" && c.type !== "block_comment");
1822
- return meaningful.length === 0;
1823
- }
1824
- case "is_empty_block": {
1825
- const caseNode = captures.CASE;
1826
- if (!caseNode)
1827
- return false;
1828
- if (this.switchCaseBodyStatements(caseNode).length > 0)
1829
- return false;
1830
- return !this.nextSiblingIsSwitchLabel(caseNode);
1831
- }
1832
- case "no_break_or_return_in_body": {
1833
- const bodyNode = captures.BODY;
1834
- if (!bodyNode)
1835
- return false;
1836
- return !this.bodyHasLoopExit(bodyNode, false);
1837
- }
1838
- case "same_param_name": {
1839
- const first = this.bindingNames(captures.PARAM1);
1840
- const second = this.bindingNames(captures.NAME);
1841
- return [...first].some((name) => second.has(name));
1842
- }
1843
- case "no_terminating_statement": {
1844
- const caseNode = captures.CASE;
1845
- if (!caseNode)
1846
- return false;
1847
- if (this.hasFallthroughMarker(caseNode))
1848
- return false;
1849
- const body = this.switchCaseBodyStatements(caseNode);
1850
- if (body.length === 0)
1851
- return false;
1852
- return !this.statementTerminates(body[body.length - 1]);
1853
- }
1854
- case "no_break_or_return": {
1855
- const bodyNode = captures.BODY;
1856
- if (!bodyNode)
1857
- return false;
1858
- return !this.bodyHasLoopExit(bodyNode, false);
1859
- }
1860
- case "is_double_checked_locking": {
1861
- const outer = captures.FIELD?.text;
1862
- const inner = captures.FIELD2?.text;
1863
- return !!outer && outer === inner;
1864
- }
1865
- case "shadows_parent_field": {
1866
- const parentName = captures.PARENT?.text;
1867
- const fieldName = captures.NAME?.text;
1868
- if (!parentName || !fieldName)
1869
- return false;
1870
- let root = captures.NAME;
1871
- while (root.parent)
1872
- root = root.parent;
1873
- const stack = [root];
1874
- while (stack.length) {
1875
- const node = stack.pop();
1876
- if (node.type === "class_declaration" && node.childForFieldName?.("name")?.text === parentName) {
1877
- const body = node.childForFieldName?.("body");
1878
- for (const member of body?.children ?? []) {
1879
- if (member.type !== "field_declaration")
1880
- continue;
1881
- for (const declarator of member.children ?? []) {
1882
- if (declarator.type === "variable_declarator" && declarator.childForFieldName?.("name")?.text === fieldName) {
1883
- return true;
1884
- }
1885
- }
1886
- }
1887
- }
1888
- for (const child of node.children ?? [])
1889
- stack.push(child);
1890
- }
1891
- return false;
1892
- }
1893
- case "missing_break_between_cases": {
1894
- const label = captures.LABEL;
1895
- const group = label?.parent;
1896
- if (!group)
1897
- return false;
1898
- const TERMINATORS = new Set([
1899
- "break_statement",
1900
- "return_statement",
1901
- "throw_statement",
1902
- "continue_statement",
1903
- "yield_statement"
1904
- ]);
1905
- for (const child of group.children ?? []) {
1906
- if (TERMINATORS.has(child.type))
1907
- return false;
1908
- }
1909
- return true;
1910
- }
1911
- case "scoped_lock_empty_args": {
1912
- const decl = captures.DECL;
1913
- if (!decl)
1914
- return false;
1915
- for (const sibling of decl.parent?.children ?? []) {
1916
- if (sibling.type === "argument_list")
1917
- return false;
1918
- }
1919
- return true;
1920
- }
1921
- case "calc_missing_spaces": {
1922
- const text = captures.EXPR?.text ?? "";
1923
- return /[\w%)][+-][\w.(]/.test(text);
1924
- }
1925
- case "bare_except_only": {
1926
- const clauseNode = captures.CLAUSE;
1927
- if (!clauseNode)
1928
- return true;
1929
- const hasExceptionSpec = clauseNode.children.some((c) => {
1930
- if (!c.isNamed)
1931
- return false;
1932
- return c.type !== "block";
1933
- });
1934
- return !hasExceptionSpec;
1935
- }
1936
- case "eq_mod_fn": {
1937
- const mod = captures.MOD?.text ?? "";
1938
- const fn = captures.FN?.text ?? "";
1939
- return mod === "threading" && fn === "Thread";
1940
- }
1941
- case "regex_first_arg_identifier": {
1942
- const mod = captures.MOD?.text ?? "";
1943
- if (mod !== "re")
1944
- return false;
1945
- const func = captures.FUNC?.text ?? "";
1946
- if (!/^(compile|match|search|fullmatch|findall|finditer|sub|subn|split)$/.test(func)) {
1947
- return false;
1948
- }
1949
- const argsNode = captures.ARGS;
1950
- if (!argsNode)
1951
- return false;
1952
- const firstNamed = (argsNode.children ?? []).find((c) => c.isNamed);
1953
- if (!firstNamed)
1954
- return false;
1955
- return firstNamed.type === "identifier";
1956
- }
1957
- case "open_mode_invalid": {
1958
- const modeNode = captures.MODE;
1959
- if (!modeNode)
1960
- return false;
1961
- const text = modeNode.text ?? "";
1962
- const stripped = text.replace(/^["']|["']$/g, "");
1963
- if (stripped.length === 0)
1964
- return false;
1965
- if (stripped.length === 1)
1966
- return false;
1967
- if (!/^[rwxabt+]+$/.test(stripped))
1968
- return true;
1969
- const validShape = /^[rwax][bt]?\+?$/;
1970
- if (!validShape.test(stripped))
1971
- return true;
1972
- return false;
1973
- }
1974
- case "status_204_with_value_return": {
1975
- const funcNode = captures.FUNC;
1976
- const valNode = captures.VAL;
1977
- if (!funcNode || !valNode)
1978
- return false;
1979
- if (Number(valNode.text ?? 0) !== 204)
1980
- return false;
1981
- const queue = [funcNode];
1982
- while (queue.length > 0) {
1983
- const node = queue.shift();
1984
- if (node.type === "return_statement") {
1985
- const hasValue = node.children.some((c) => c.isNamed && c.type !== "comment");
1986
- if (hasValue)
1987
- return true;
1988
- }
1989
- if (node.children)
1990
- queue.push(...node.children);
1991
- }
1992
- return false;
1993
- }
1994
- case "has_mixed_async": {
1995
- const bodyNode = captures.BODY;
1996
- if (!bodyNode)
1997
- return true;
1998
- const bodyText = bodyNode.text;
1999
- return bodyText.includes("await") && /\.\s*(then|catch)\s*\(/.test(bodyText);
2000
- }
2001
- case "format_arity_mismatch": {
2002
- const formatNode = captures.FORMAT;
2003
- const argsNode = captures.ARGS;
2004
- if (!formatNode || !argsNode)
2005
- return false;
2006
- const fmtText = (formatNode.text ?? "").replace(/^["']|["']$/g, "");
2007
- const fmt = fmtText;
2008
- let placeholderCount = 0;
2009
- const namedKeys = [];
2010
- const positionalRegex = /%(?:\([^)]+\))?[#0\- +]*\d*(?:\.\d+)?[hlL]?[diouxXeEfFgGcrs%]/g;
2011
- const positionalMatches = fmt.match(positionalRegex) ?? [];
2012
- for (const m of positionalMatches) {
2013
- if (m === "%%")
2014
- continue;
2015
- placeholderCount++;
2016
- const namedMatch = m.match(/^%\(([^)]+)\)/);
2017
- if (namedMatch)
2018
- namedKeys.push(namedMatch[1]);
2019
- }
2020
- if (namedKeys.length > 0) {
2021
- if (argsNode.type === "dictionary") {
2022
- const dictKeys = [];
2023
- for (const child of argsNode.children ?? []) {
2024
- if (child.type === "pair" && child.children?.[0]) {
2025
- dictKeys.push((child.children[0].text ?? "").replace(/^["']|["']$/g, ""));
2026
- }
2027
- }
2028
- const missing = namedKeys.filter((k) => !dictKeys.includes(k));
2029
- return missing.length > 0;
2030
- }
2031
- return true;
2032
- }
2033
- if (argsNode.type === "tuple") {
2034
- const argCount = (argsNode.children ?? []).filter((c) => c.isNamed).length;
2035
- if (argCount !== placeholderCount)
2036
- return true;
2037
- }
2038
- return false;
2039
- }
2040
- case "aws_policy_public": {
2041
- const policyNode = captures.POLICY;
2042
- if (!policyNode)
2043
- return false;
2044
- const text = policyNode.text ?? "";
2045
- const patterns = [
2046
- /"Principal"\s*:\s*"\*"/,
2047
- /"Principal"\s*:\s*\{\s*"AWS"\s*:\s*"\*"\s*\}/,
2048
- /"Effect"\s*:\s*"Allow"[\s\S]*?"Action"\s*:\s*"\*"[\s\S]*?"Resource"\s*:\s*"\*"/,
2049
- /"Principal"\s*:\s*"\*"/
2050
- ];
2051
- return patterns.some((p) => p.test(text));
2052
- }
2053
- case "slots_attribute_mismatch": {
2054
- const selfNode = captures.SELF;
2055
- const attrNode = captures.ATTR;
2056
- const methodNode = captures.METHOD;
2057
- if (!selfNode || !attrNode || !methodNode)
2058
- return false;
2059
- if (selfNode.text !== "self")
2060
- return false;
2061
- const attrName = attrNode.text ?? "";
2062
- let parent = methodNode.parent;
2063
- while (parent && parent.type !== "class_definition") {
2064
- parent = parent.parent;
2065
- }
2066
- if (!parent)
2067
- return false;
2068
- const slots = extractSlots(parent);
2069
- if (slots === null || slots.length === 0)
2070
- return false;
2071
- return !slots.includes(attrName);
2072
- }
2073
- case "special_method_arity": {
2074
- const nameNode = captures.NAME;
2075
- const paramsNode = captures.PARAMS;
2076
- if (!nameNode || !paramsNode)
2077
- return false;
2078
- const name = nameNode.text ?? "";
2079
- const expected = {
2080
- __del__: 0,
2081
- __repr__: 0,
2082
- __str__: 0,
2083
- __hash__: 0,
2084
- __bool__: 0,
2085
- __len__: 0,
2086
- __eq__: 1,
2087
- __lt__: 1,
2088
- __le__: 1,
2089
- __gt__: 1,
2090
- __ge__: 1,
2091
- __ne__: 1
2092
- };
2093
- const expectedCount = expected[name];
2094
- if (expectedCount === undefined)
2095
- return false;
2096
- const paramCount = (paramsNode.children ?? []).filter((c) => {
2097
- if (c.type !== "identifier" && c.type !== "typed_parameter")
2098
- return false;
2099
- if (c.text.includes("="))
2100
- return false;
2101
- return true;
2102
- }).length;
2103
- return paramCount !== expectedCount + 1;
2104
- }
2105
- case "no_super_call": {
2106
- const bodyNode = captures.BODY;
2107
- if (!bodyNode)
2108
- return true;
2109
- return !/(?<!\/\/.*)super\s*\(/.test(bodyNode.text);
2110
- }
2111
- case "in_test_block": {
2112
- const first = Object.values(captures)[0];
2113
- return !!first && this.navigator.isInTestBlock(first);
2114
- }
2115
- case "not_in_test_block": {
2116
- const first = Object.values(captures)[0];
2117
- return !first || !this.navigator.isInTestBlock(first);
2118
- }
2119
- case "not_in_try_catch": {
2120
- const first = Object.values(captures)[0];
2121
- return !first || !this.navigator.isInTryCatch(first);
2122
- }
2123
- case "in_try_catch": {
2124
- const first = Object.values(captures)[0];
2125
- return !!first && this.navigator.isInTryCatch(first);
2126
- }
2127
- case "name_matches_param": {
2128
- const nameNode = captures.NAME;
2129
- const paramNode = captures.PARAM;
2130
- return !!nameNode && !!paramNode && nameNode.text === paramNode.text;
2131
- }
2132
- case "not_in_function": {
2133
- const first = Object.values(captures)[0];
2134
- return !first || !this.navigator.isInside(first, [
2135
- "function_definition",
2136
- "function_declaration",
2137
- "method_definition",
2138
- "arrow_function"
2139
- ]);
2140
- }
2141
- case "check_secret_pattern": {
2142
- const varName = captures.VARNAME?.text ?? "";
2143
- const varNameLower = varName.toLowerCase();
2144
- if (varName === varName.toUpperCase() && /[A-Z]/.test(varName)) {
2145
- return false;
2146
- }
2147
- return [
2148
- /api[_-]?key/,
2149
- /api[_-]?secret/,
2150
- /password/,
2151
- /passwd/,
2152
- /secret/,
2153
- /token/,
2154
- /auth/,
2155
- /private[_-]?key/,
2156
- /access[_-]?token/,
2157
- /credentials/,
2158
- /aws[_-]?secret/,
2159
- /github[_-]?token/,
2160
- /client[_-]?secret/
2161
- ].some((p) => p.test(varNameLower));
2162
- }
2163
- case "returns_error": {
2164
- const first = Object.values(captures)[0];
2165
- if (!first)
2166
- return false;
2167
- const funcNode = this.navigator.findParent(first, [
2168
- "function_declaration",
2169
- "method_declaration"
2170
- ]);
2171
- if (!funcNode)
2172
- return false;
2173
- const signature = String(funcNode.text ?? "").split("{", 1)[0]?.trim() ?? "";
2174
- const returnPart = signature.match(/func\s*(?:\([^)]*\)\s*)?[A-Za-z_]\w*\s*\([^)]*\)\s*(.*)$/s)?.[1]?.trim() ?? "";
2175
- return returnPart.length > 0 && /\berror\b/.test(returnPart);
2176
- }
2177
- case "python_empty_except": {
2178
- const bodyNode = captures.BODY;
2179
- if (!bodyNode)
2180
- return true;
2181
- return !bodyNode.children.some((c) => c.isNamed && c.type !== "pass_statement" && c.type !== "comment");
2182
- }
2183
- case "check_in_operator_types": {
2184
- const target = captures.TARGET;
2185
- if (!target)
2186
- return false;
2187
- return ["none", "true", "false", "integer", "float"].includes(target.type);
2188
- }
2189
- case "torchscript_super_call": {
2190
- const call = captures.CALL;
2191
- if (!call)
2192
- return false;
2193
- const isTorchScriptDecorated = (node) => {
2194
- const decorated = node?.parent;
2195
- if (!decorated || decorated.type !== "decorated_definition")
2196
- return false;
2197
- return (decorated.children ?? []).some((c) => c.type === "decorator" && /^@(torch\.jit\.script|jit\.script)$/.test(c.text ?? ""));
2198
- };
2199
- const methodNode = this.navigator.findParent(call, [
2200
- "function_definition"
2201
- ]);
2202
- if (!methodNode)
2203
- return false;
2204
- const classNode = this.navigator.findParent(methodNode, [
2205
- "class_definition"
2206
- ]);
2207
- return isTorchScriptDecorated(methodNode) || isTorchScriptDecorated(classNode);
2208
- }
2209
- case "exit_params_insufficient": {
2210
- const params = captures.PARAMS;
2211
- if (!params)
2212
- return true;
2213
- const named = (params.children ?? []).filter((c) => c.isNamed);
2214
- if (named.some((c) => c.type === "list_splat_pattern" || c.type === "dictionary_splat_pattern")) {
2215
- return false;
2216
- }
2217
- return named.length < 4;
2218
- }
2219
- case "ruby_empty_rescue": {
2220
- const bodyNode = captures.BODY;
2221
- if (!bodyNode)
2222
- return true;
2223
- return !bodyNode.children.some((c) => c.isNamed && !["comment", "nil", "nil_literal"].includes(c.type));
2224
- }
2225
- case "ts_command_injection_sink":
2226
- return captures.MOD?.text === "child_process" && /^(exec|execSync)$/.test(captures.FN?.text ?? "");
2227
- case "ts_sql_injection_sink": {
2228
- const template = captures.TEMPLATE?.text ?? "";
2229
- const rawPrefix = template.replace(/^`/, "").replace(/`$/, "").replace(/^(?:\s|\/\*[\s\S]*?\*\/|--[^\n]*(?:\n|$))*/, "");
2230
- if (/^(?:SELECT[\s\S]*(?:\bFROM\b|\$\{)|INSERT\s+INTO\b|UPDATE\s+[\s\S]*\bSET\b|DELETE\s+FROM\b|CREATE\s+(?:OR\s+REPLACE\s+|TEMP(?:ORARY)?\s+|UNIQUE\s+|MATERIALIZED\s+)*(?:TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|PROCEDURE|SEQUENCE|TYPE|EXTENSION)\b|DROP\s+(?:OR\s+REPLACE\s+|TEMP(?:ORARY)?\s+|UNIQUE\s+|MATERIALIZED\s+)*(?:TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|PROCEDURE|SEQUENCE|TYPE|EXTENSION)\b|ALTER\s+(?:OR\s+REPLACE\s+|TEMP(?:ORARY)?\s+|UNIQUE\s+|MATERIALIZED\s+)*(?:TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|PROCEDURE|SEQUENCE|TYPE|EXTENSION)\b|GRANT\b|REVOKE\b|WITH[\s\S]*\bSELECT\b|MERGE\s+INTO\b|TRUNCATE\s+TABLE\b|REPLACE\s+INTO\b)/i.test(rawPrefix))
2231
- return true;
2232
- if (!rootNode || !captures.OBJ)
2233
- return false;
2234
- let imported = TYPESCRIPT_SQL_IMPORTS.get(rootNode);
2235
- if (!imported) {
2236
- const names = new Set;
2237
- const stack = [rootNode];
2238
- while (stack.length > 0) {
2239
- const node = stack.pop();
2240
- if (!node)
2241
- continue;
2242
- if (node.type === "import_statement") {
2243
- const source = node.childForFieldName?.("source")?.text ?? "";
2244
- const packageName = source.replace(/^['"]|['"]$/g, "");
2245
- const isKnown = [...TYPESCRIPT_SQL_KNOWN_PACKAGES].some((pkg) => packageName === pkg || packageName.startsWith(`${pkg}/`));
2246
- if (isKnown) {
2247
- const importStack = [node];
2248
- while (importStack.length > 0) {
2249
- const importNode = importStack.pop();
2250
- if (!importNode)
2251
- continue;
2252
- if (importNode.type === "identifier" || importNode.type === "import_specifier") {
2253
- names.add(importNode.text);
2254
- }
2255
- importStack.push(...importNode.children);
2256
- }
2257
- }
2258
- }
2259
- stack.push(...node.children);
2260
- }
2261
- const declarations = new Map;
2262
- const declarationStack = [rootNode];
2263
- while (declarationStack.length > 0) {
2264
- const node = declarationStack.pop();
2265
- if (!node)
2266
- continue;
2267
- if (node.type === "variable_declarator") {
2268
- const name = node.childForFieldName?.("name")?.text;
2269
- const value = node.childForFieldName?.("value");
2270
- if (name && value)
2271
- declarations.set(name, value);
2272
- }
2273
- declarationStack.push(...node.children);
2274
- }
2275
- const resolvesKnownValue = (node, seen = new Set) => {
2276
- if (node.type === "identifier") {
2277
- if (names.has(node.text))
2278
- return true;
2279
- if (seen.has(node.text))
2280
- return false;
2281
- const value = declarations.get(node.text);
2282
- if (!value)
2283
- return false;
2284
- seen.add(node.text);
2285
- return resolvesKnownValue(value, seen);
2286
- }
2287
- if (node.type === "await_expression") {
2288
- const value = node.children.find((child) => child.isNamed);
2289
- return value ? resolvesKnownValue(value, seen) : false;
2290
- }
2291
- if (node.type === "new_expression") {
2292
- const ctor = node.childForFieldName?.("constructor");
2293
- return ctor ? resolvesKnownValue(ctor, seen) : false;
2294
- }
2295
- if (node.type === "call_expression") {
2296
- const fn = node.childForFieldName?.("function");
2297
- return fn ? resolvesKnownValue(fn, seen) : false;
2298
- }
2299
- return false;
2300
- };
2301
- let changed = true;
2302
- while (changed) {
2303
- changed = false;
2304
- for (const [name, value] of declarations) {
2305
- if (!names.has(name) && resolvesKnownValue(value)) {
2306
- names.add(name);
2307
- changed = true;
2308
- }
2309
- }
2310
- }
2311
- imported = names;
2312
- TYPESCRIPT_SQL_IMPORTS.set(rootNode, imported);
2313
- }
2314
- const resolvesToKnownClient = (node) => {
2315
- if (node.type === "identifier")
2316
- return imported.has(node.text);
2317
- if (node.type === "await_expression") {
2318
- const value = node.children.find((child) => child.isNamed);
2319
- return value ? resolvesToKnownClient(value) : false;
2320
- }
2321
- if (node.type === "new_expression") {
2322
- const ctor = node.childForFieldName?.("constructor");
2323
- return ctor ? resolvesToKnownClient(ctor) : false;
2324
- }
2325
- if (node.type === "call_expression") {
2326
- const fn = node.childForFieldName?.("function");
2327
- return fn ? resolvesToKnownClient(fn) : false;
2328
- }
2329
- return false;
2330
- };
2331
- return resolvesToKnownClient(captures.OBJ);
2332
- }
2333
- case "ts_ssrf_sink": {
2334
- const fn = captures.FN?.text ?? "";
2335
- const obj = captures.OBJ?.text ?? "";
2336
- const urlText = captures.URL?.text ?? "";
2337
- const allowedFns = new Set([
2338
- "fetch",
2339
- "request",
2340
- "get",
2341
- "post",
2342
- "put",
2343
- "patch",
2344
- "delete"
2345
- ]);
2346
- if (!allowedFns.has(fn))
2347
- return false;
2348
- if (rootNode && captures.URL?.type === "identifier" && this.resolvesToFileLiteralConst(urlText, rootNode)) {
2349
- return false;
2350
- }
2351
- if (rootNode && captures.URL) {
2352
- const urlVar = this.newUrlBaseVarName(captures.URL);
2353
- if (urlVar && this.resolvesToFixedNewUrlConst(urlVar, rootNode)) {
2354
- return false;
2355
- }
2356
- }
2357
- const looksLikeExternalInput = urlText.includes(".") || /user|external|remote|input|target|webhook|callback|redirect|untrusted|arbitrary/i.test(urlText);
2358
- if (!looksLikeExternalInput)
2359
- return false;
2360
- if (!obj)
2361
- return fn === "fetch";
2362
- return new Set([
2363
- "axios",
2364
- "http",
2365
- "https",
2366
- "got",
2367
- "request",
2368
- "superagent",
2369
- "undici"
2370
- ]).has(obj);
2371
- }
2372
- case "ts_weak_hash_algorithm":
2373
- return captures.FN?.text === "createHash" && /^(md5|sha1)$/i.test(captures.ALG?.text ?? "");
2374
- case "ts_insecure_random_source": {
2375
- if (captures.OBJ?.text !== "Math" || captures.FN?.text !== "random")
2376
- return false;
2377
- const varName = this.enclosingBindingName(captures.CALL ?? captures.VAR);
2378
- return /token|secret|password|key|nonce|salt|csrf|auth|session|credential|hash|otp|pin/i.test(varName);
2379
- }
2380
- case "ts_detached_async_call":
2381
- return /(Async$|fetch$|request$)/.test(captures.FN?.text ?? "");
2382
- case "incomplete_assertion": {
2383
- const expectNode = captures.EXPECT;
2384
- if (!expectNode)
2385
- return false;
2386
- const CHAI_PROPERTY_ASSERTIONS = new Set([
2387
- "true",
2388
- "false",
2389
- "null",
2390
- "undefined",
2391
- "empty",
2392
- "NaN",
2393
- "finite",
2394
- "exist",
2395
- "arguments",
2396
- "extensible",
2397
- "sealed",
2398
- "frozen",
2399
- "locked"
2400
- ]);
2401
- let current = expectNode.parent;
2402
- if (!current)
2403
- return false;
2404
- current = current.parent;
2405
- if (!current)
2406
- return false;
2407
- if (current.type === "expression_statement" || current.type === "return_statement")
2408
- return true;
2409
- let lastPropertyName = null;
2410
- while (current && current.type === "member_expression") {
2411
- const propNode = current.children?.find((c) => c.type === "property_identifier");
2412
- if (propNode)
2413
- lastPropertyName = propNode.text;
2414
- const parent = current.parent;
2415
- if (!parent)
2416
- return false;
2417
- if (parent.type === "expression_statement" || parent.type === "return_statement") {
2418
- if (lastPropertyName && CHAI_PROPERTY_ASSERTIONS.has(lastPropertyName))
2419
- return false;
2420
- return true;
2421
- }
2422
- if (parent.type === "call_expression")
2423
- return false;
2424
- current = parent;
2425
- }
2426
- return false;
2427
- }
2428
- case "py_command_injection_sink": {
2429
- const mod = captures.MOD?.text ?? "";
2430
- const fn = captures.FN?.text ?? "";
2431
- const kw = captures.KW?.text ?? "";
2432
- return mod === "os" && /^(system|popen)$/.test(fn) || mod === "subprocess" && /^(run|Popen|call|check_output|check_call)$/.test(fn) && kw === "shell";
2433
- }
2434
- case "go_command_injection_sink":
2435
- return captures.PKG?.text === "exec" && /^(Command|CommandContext)$/.test(captures.FN?.text ?? "") && /^"(sh|bash|zsh|cmd|powershell|pwsh)"$/.test(captures.SHELL?.text ?? "") && /^"(-c|\/c)"$/.test(captures.FLAG?.text ?? "");
2436
- case "ruby_command_injection_sink":
2437
- return /^(system|exec|spawn|popen|capture3|capture2|capture2e)$/.test(captures.FN?.text ?? "");
2438
- case "py_ssrf_sink":
2439
- return captures.MOD?.text === "requests" && /^(get|post|put|patch|delete|request|head|options)$/.test(captures.FN?.text ?? "");
2440
- case "py_path_traversal_sink":
2441
- return /^(open|read_text|read_bytes|write_text|write_bytes|remove|unlink|rmdir)$/.test(captures.FN?.text ?? "");
2442
- case "go_path_traversal_sink":
2443
- return /^(os|ioutil)$/.test(captures.PKG?.text ?? "") && /^(Open|OpenFile|ReadFile|WriteFile|Create|Remove|RemoveAll)$/.test(captures.FN?.text ?? "");
2444
- case "py_sql_injection_sink": {
2445
- const fn = captures.FN?.text ?? "";
2446
- if (!PYTHON_SQL_SINK_METHODS.has(fn))
2447
- return false;
2448
- const sqlNode = captures.SQL;
2449
- const receiver = captures.OBJ;
2450
- if (fn === "execute" && this.isLikelySqlAlchemyReceiver(receiver?.text ?? "")) {
2451
- return false;
2452
- }
2453
- if (sqlNode && this.isSafeSqlAlchemyExpressionCall(sqlNode)) {
2454
- return false;
2455
- }
2456
- if (isProvenSqlAlchemySessionReceiver(receiver, rootNode)) {
2457
- if (fn === "query" && isSqlAlchemyEntityQueryArgument(sqlNode, rootNode)) {
2458
- return false;
2459
- }
2460
- if (fn === "execute" && isSqlAlchemyStatementArgument(sqlNode, rootNode)) {
2461
- return false;
2462
- }
2463
- }
2464
- if (isSafePsycopgIdentifierComposition(sqlNode, rootNode)) {
2465
- return false;
2466
- }
2467
- return true;
2468
- }
2469
- case "go_sql_injection_sink":
2470
- return /^(Query|QueryContext|QueryRow|QueryRowContext|Exec|ExecContext)$/.test(captures.DBFN?.text ?? "") && captures.FMTPKG?.text === "fmt" && captures.FMTFN?.text === "Sprintf";
2471
- case "py_insecure_deserialization_sink":
2472
- return /^(pickle|yaml)$/.test(captures.MOD?.text ?? "") && /^(load|loads|unsafe_load)$/.test(captures.FN?.text ?? "");
2473
- case "ruby_insecure_deserialization_sink":
2474
- return /^(Marshal|YAML|Psych)$/.test(captures.MOD?.text ?? "") && /^(load|unsafe_load)$/.test(captures.FN?.text ?? "");
2475
- case "match_captures": {
2476
- for (const [captureName, pattern] of Object.entries(postFilterParams ?? {})) {
2477
- const node = captures[captureName];
2478
- if (!node)
2479
- return false;
2480
- if (!new RegExp(pattern).test(node.text))
2481
- return false;
2482
- }
2483
- return true;
2484
- }
2485
- case "case_range_single_value": {
2486
- const start = captures.START?.text ?? "";
2487
- const end = captures.END?.text ?? "";
2488
- return start === end;
2489
- }
2490
- case "goto_jumps_backward": {
2491
- const label = captures.LABEL;
2492
- const gotoNode = captures.GOTO;
2493
- if (!label || !gotoNode)
2494
- return false;
2495
- return label.startIndex < gotoNode.startIndex;
2496
- }
2497
- case "goto_targets_inner_block": {
2498
- const target = captures.TARGET;
2499
- if (!target)
2500
- return false;
2501
- let depth = 0;
2502
- let node = target.parent;
2503
- while (node) {
2504
- if (node.type === "compound_statement")
2505
- depth++;
2506
- node = node.parent;
2507
- }
2508
- return depth >= 2;
2509
- }
2510
- case "c_memset_sensitive_arg": {
2511
- const callNode = captures.CALL;
2512
- if (!callNode || callNode.type !== "call_expression")
2513
- return false;
2514
- const argList = callNode.children?.find((c) => c.type === "argument_list");
2515
- if (!argList)
2516
- return false;
2517
- const firstArg = argList.children?.find((c) => c.isNamed);
2518
- if (!firstArg)
2519
- return false;
2520
- const argName = firstArg.text ?? "";
2521
- return /password|secret|key|token|credential|auth|private|passwd|pin|salt|nonce|iv|seed/i.test(argName);
2522
- }
2523
- case "c_stdlib_name": {
2524
- const name = captures.NAME?.text ?? "";
2525
- const STDLIB_NAMES = new Set([
2526
- "malloc",
2527
- "calloc",
2528
- "realloc",
2529
- "free",
2530
- "alloca",
2531
- "printf",
2532
- "fprintf",
2533
- "sprintf",
2534
- "snprintf",
2535
- "vprintf",
2536
- "vfprintf",
2537
- "vsprintf",
2538
- "vsnprintf",
2539
- "scanf",
2540
- "fscanf",
2541
- "sscanf",
2542
- "vscanf",
2543
- "vfscanf",
2544
- "vsscanf",
2545
- "strcpy",
2546
- "strncpy",
2547
- "strcat",
2548
- "strncat",
2549
- "strcmp",
2550
- "strncmp",
2551
- "strlen",
2552
- "strchr",
2553
- "strrchr",
2554
- "strstr",
2555
- "strerror",
2556
- "memcpy",
2557
- "memmove",
2558
- "memset",
2559
- "memcmp",
2560
- "memchr",
2561
- "fopen",
2562
- "fclose",
2563
- "fread",
2564
- "fwrite",
2565
- "fgets",
2566
- "fputs",
2567
- "getc",
2568
- "putc",
2569
- "getchar",
2570
- "putchar",
2571
- "exit",
2572
- "abort",
2573
- "assert",
2574
- "errno",
2575
- "abs",
2576
- "labs",
2577
- "llabs",
2578
- "div",
2579
- "ldiv",
2580
- "lldiv",
2581
- "atoi",
2582
- "atol",
2583
- "atoll",
2584
- "strtol",
2585
- "strtoll",
2586
- "strtoul",
2587
- "strtoull",
2588
- "strtod",
2589
- "strtof",
2590
- "strtold",
2591
- "qsort",
2592
- "bsearch",
2593
- "time",
2594
- "clock",
2595
- "difftime",
2596
- "mktime",
2597
- "strftime",
2598
- "getenv",
2599
- "setenv",
2600
- "putenv",
2601
- "system",
2602
- "isalpha",
2603
- "isdigit",
2604
- "isalnum",
2605
- "isspace",
2606
- "isupper",
2607
- "islower",
2608
- "toupper",
2609
- "tolower",
2610
- "sizeof",
2611
- "offsetof",
2612
- "NULL",
2613
- "EXIT_SUCCESS",
2614
- "EXIT_FAILURE"
2615
- ]);
2616
- return STDLIB_NAMES.has(name);
2617
- }
2618
- case "c_octal_literal": {
2619
- const num = captures.NUM?.text ?? "";
2620
- return /^0[0-7]+$/.test(num);
2621
- }
2622
- case "c_noreturn_attr": {
2623
- const attr = captures.ATTR?.text ?? "";
2624
- return attr === "noreturn";
2625
- }
2626
- case "c_label_in_switch": {
2627
- const stmt = captures.STMT;
2628
- if (!stmt)
2629
- return false;
2630
- let node = stmt.parent;
2631
- while (node) {
2632
- if (node.type === "switch_statement")
2633
- return true;
2634
- node = node.parent;
2635
- }
2636
- return false;
2637
- }
2638
- default:
2639
- this.reportMissingPostFilter(postFilter);
2640
- return false;
2641
- }
2642
- }
2643
- reportedMissingPostFilters = new Set;
2644
- reportedPostFilterFailures = new Set;
2645
- reportPostFilterFailure(postFilter, reason) {
2646
- if (this.reportedPostFilterFailures.has(postFilter))
2647
- return;
2648
- this.reportedPostFilterFailures.add(postFilter);
2649
- logTreeSitterDiagnostic({
2650
- subsystem: "tree-sitter-client",
2651
- message: `tree-sitter rule post_filter '${postFilter}' failed — ` + `keeping the diagnostic (fail-open): ${reason}.`,
2652
- metadata: { postFilter }
2653
- });
2654
- }
2655
- reportMissingPostFilter(postFilter) {
2656
- if (this.reportedMissingPostFilters.has(postFilter))
2657
- return;
2658
- this.reportedMissingPostFilters.add(postFilter);
2659
- logTreeSitterDiagnostic({
2660
- subsystem: "tree-sitter-client",
2661
- message: `tree-sitter rule post_filter '${postFilter}' is not implemented — ` + `matches for the rules using it are suppressed rather than reported unfiltered. ` + `Implement it in applyPostFilter (clients/tree-sitter-client.ts) to re-enable them.`,
2662
- metadata: { postFilter }
2663
- });
2664
- }
2665
- textPredicatesInvalidLogged = false;
2666
- hasValidTextPredicates(query) {
2667
- const valid = Array.isArray(query?.textPredicates);
2668
- if (!valid) {
2669
- if (!this.textPredicatesInvalidLogged) {
2670
- this.textPredicatesInvalidLogged = true;
2671
- logTreeSitterDiagnostic({
2672
- subsystem: "tree-sitter-client",
2673
- message: "web-tree-sitter Query.textPredicates is missing or not an array — " + "#match?/#eq? predicates cannot be evaluated. Failing CLOSED: matches " + "for this query are dropped rather than reported unfiltered.",
2674
- metadata: { textPredicatesType: typeof query?.textPredicates }
2675
- });
2676
- }
2677
- recordDegradationOnce({
2678
- kind: "query-predicates-invalid",
2679
- subject: "web-tree-sitter",
2680
- reason: "Query.textPredicates is missing or not an array — #match?/#eq? " + "predicates cannot be evaluated; structural matches relying on them " + "are dropped fail-closed"
2681
- });
2682
- }
2683
- return valid;
2684
- }
2685
- evaluatePredicates(query, match) {
2686
- if (!this.hasValidTextPredicates(query))
2687
- return false;
2688
- const predicates = query.textPredicates?.[match.patternIndex] ?? [];
2689
- return predicates.every((fn) => fn(match.captures));
2690
- }
2691
- async searchFileWithQuery(filePath, query, metavars, languageId, _originalPattern, postFilter, postFilterParams, contentOverride) {
2692
- const matches = [];
2693
- await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => {
2694
- try {
2695
- const rootNode = tree.rootNode;
2696
- const queryMatches = query.matches(rootNode);
2697
- for (const match of queryMatches) {
2698
- const captures = {};
2699
- for (const capture of match.captures) {
2700
- if (metavars.includes(capture.name)) {
2701
- captures[capture.name] = capture.node;
2702
- }
2703
- }
2704
- if (!this.evaluatePredicates(query, match)) {
2705
- continue;
2706
- }
2707
- if (postFilter && !this.applyPostFilter(postFilter, postFilterParams, captures, rootNode)) {
2708
- continue;
2709
- }
2710
- if (match.captures.length > 0) {
2711
- const firstNode = match.captures[0].node;
2712
- const textCaptures = {};
2713
- for (const [name, node] of Object.entries(captures)) {
2714
- textCaptures[name] = node.text;
2715
- }
2716
- matches.push({
2717
- file: filePath,
2718
- line: firstNode.startPosition.row + 1,
2719
- column: firstNode.startPosition.column + 1,
2720
- matchedText: firstNode.text,
2721
- nodeType: firstNode.type,
2722
- captures: textCaptures
2723
- });
2724
- }
2725
- }
2726
- if (matches.length > 0) {
2727
- this.dbg(`Found ${matches.length} matches in ${path.basename(filePath)}`);
2728
- }
2729
- } catch (err) {
2730
- this.reportWasmAbort(err);
2731
- this.dbg(`Query matching error: ${err}`);
2732
- }
2733
- });
2734
- return matches;
2735
- }
2736
- collectFiles(dir, languageId, fileFilter) {
2737
- const files = [];
2738
- const extensions = this.getExtensionsForLanguage(languageId);
2739
- const rootDir = path.resolve(dir);
2740
- const ignoreMatcher = getProjectIgnoreMatcher(rootDir);
2741
- const scan = (d) => {
2742
- if (files.length >= TREE_SITTER_MAX_SCAN_FILES)
2743
- return;
2744
- try {
2745
- const entries = fs.readdirSync(d, { withFileTypes: true });
2746
- for (const entry of entries) {
2747
- if (files.length >= TREE_SITTER_MAX_SCAN_FILES)
2748
- return;
2749
- const full = path.join(d, entry.name);
2750
- if (entry.isDirectory()) {
2751
- if (isExcludedDirName(entry.name))
2752
- continue;
2753
- if (ignoreMatcher.isIgnored(full, true))
2754
- continue;
2755
- scan(full);
2756
- } else if (extensions.some((ext) => entry.name.endsWith(ext))) {
2757
- if (ignoreMatcher.isIgnored(full, false))
2758
- continue;
2759
- if (!fileFilter || fileFilter(full)) {
2760
- files.push(full);
2761
- }
2762
- }
2763
- }
2764
- } catch {}
2765
- };
2766
- scan(rootDir);
2767
- return files;
2768
- }
2769
- getExtensionsForLanguage(languageId) {
2770
- const mapping = {
2771
- typescript: [".ts", ".mts", ".cts"],
2772
- tsx: [".tsx"],
2773
- javascript: [".js", ".mjs", ".cjs"],
2774
- python: [".py"],
2775
- rust: [".rs"],
2776
- go: [".go"],
2777
- java: [".java"],
2778
- kotlin: [".kt", ".kts"],
2779
- dart: [".dart"],
2780
- c: [".c", ".h"],
2781
- cpp: [".cpp", ".hpp", ".cc", ".hh"],
2782
- elixir: [".ex", ".exs"],
2783
- ruby: [".rb"]
2784
- };
2785
- return mapping[languageId] || [];
2786
- }
2787
- }