code-auditor-mcp 3.1.0 → 3.4.0

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 (360) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +665 -1
  3. package/README.md +85 -11
  4. package/dist/__test_tmp__/spec18_test.d.ts +2 -0
  5. package/dist/__test_tmp__/spec18_test.d.ts.map +1 -0
  6. package/dist/__test_tmp__/spec18_test.js +5 -0
  7. package/dist/__test_tmp__/spec18_test.js.map +1 -0
  8. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +6 -0
  9. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/spec-20/scripts/tool.js +12 -0
  11. package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +1 -0
  12. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +7 -0
  13. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +1 -0
  14. package/dist/__tests__/fixtures/spec-20/src/demo-module.js +13 -0
  15. package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +1 -0
  16. package/dist/analyzers/analyzerUtils.js +2 -2
  17. package/dist/analyzers/analyzerUtils.js.map +1 -1
  18. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +1 -1
  19. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
  20. package/dist/analyzers/cross-language/APIContractAnalyzer.js +2 -4
  21. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  22. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +3 -0
  23. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
  24. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +3 -5
  25. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
  26. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  27. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +0 -3
  28. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  29. package/dist/analyzers/cross-language/SchemaValidator.d.ts +1 -1
  30. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  31. package/dist/analyzers/cross-language/SchemaValidator.js +2 -5
  32. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  33. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +83 -0
  34. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -0
  35. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +611 -0
  36. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -0
  37. package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
  38. package/dist/analyzers/documentationAnalyzer.js +8 -4
  39. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  40. package/dist/analyzers/orm/adapterRegistry.d.ts +41 -0
  41. package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -0
  42. package/dist/analyzers/orm/adapterRegistry.js +73 -0
  43. package/dist/analyzers/orm/adapterRegistry.js.map +1 -0
  44. package/dist/analyzers/orm/drizzleAdapter.d.ts +42 -0
  45. package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -0
  46. package/dist/analyzers/orm/drizzleAdapter.js +166 -0
  47. package/dist/analyzers/orm/drizzleAdapter.js.map +1 -0
  48. package/dist/analyzers/orm/index.d.ts +14 -0
  49. package/dist/analyzers/orm/index.d.ts.map +1 -0
  50. package/dist/analyzers/orm/index.js +24 -0
  51. package/dist/analyzers/orm/index.js.map +1 -0
  52. package/dist/analyzers/orm/prismaAdapter.d.ts +45 -0
  53. package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -0
  54. package/dist/analyzers/orm/prismaAdapter.js +170 -0
  55. package/dist/analyzers/orm/prismaAdapter.js.map +1 -0
  56. package/dist/analyzers/orm/types.d.ts +63 -0
  57. package/dist/analyzers/orm/types.d.ts.map +1 -0
  58. package/dist/analyzers/orm/types.js +10 -0
  59. package/dist/analyzers/orm/types.js.map +1 -0
  60. package/dist/analyzers/provenance.d.ts +133 -0
  61. package/dist/analyzers/provenance.d.ts.map +1 -0
  62. package/dist/analyzers/provenance.js +1041 -0
  63. package/dist/analyzers/provenance.js.map +1 -0
  64. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  65. package/dist/analyzers/reactAnalyzer.js +117 -1
  66. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  67. package/dist/analyzers/ruleRegistry.d.ts +38 -0
  68. package/dist/analyzers/ruleRegistry.d.ts.map +1 -0
  69. package/dist/analyzers/ruleRegistry.js +123 -0
  70. package/dist/analyzers/ruleRegistry.js.map +1 -0
  71. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +58 -0
  72. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -0
  73. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +445 -0
  74. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -0
  75. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +52 -0
  76. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  77. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +108 -26
  78. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  79. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +64 -0
  80. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  81. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +259 -103
  82. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  83. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
  84. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +5 -15
  85. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  86. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  87. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +10 -10
  88. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  89. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +25 -0
  90. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  91. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +250 -59
  92. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  93. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +103 -0
  94. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -0
  95. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +750 -0
  96. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -0
  97. package/dist/auditRunner.d.ts.map +1 -1
  98. package/dist/auditRunner.js +483 -3
  99. package/dist/auditRunner.js.map +1 -1
  100. package/dist/baseline.d.ts +98 -0
  101. package/dist/baseline.d.ts.map +1 -0
  102. package/dist/baseline.js +184 -0
  103. package/dist/baseline.js.map +1 -0
  104. package/dist/churn/churnExtractor.d.ts +32 -0
  105. package/dist/churn/churnExtractor.d.ts.map +1 -0
  106. package/dist/churn/churnExtractor.js +261 -0
  107. package/dist/churn/churnExtractor.js.map +1 -0
  108. package/dist/cli.js +1215 -11
  109. package/dist/cli.js.map +1 -1
  110. package/dist/codeIndexDB-enhanced.js +1 -1
  111. package/dist/codeIndexDB-enhanced.js.map +1 -1
  112. package/dist/codeIndexDB.d.ts +101 -0
  113. package/dist/codeIndexDB.d.ts.map +1 -1
  114. package/dist/codeIndexDB.js +661 -5
  115. package/dist/codeIndexDB.js.map +1 -1
  116. package/dist/config/configLoader.d.ts.map +1 -1
  117. package/dist/config/configLoader.js +95 -1
  118. package/dist/config/configLoader.js.map +1 -1
  119. package/dist/config/defaults.d.ts +72 -1
  120. package/dist/config/defaults.d.ts.map +1 -1
  121. package/dist/config/defaults.js +146 -15
  122. package/dist/config/defaults.js.map +1 -1
  123. package/dist/config/pathProfiles.d.ts +38 -0
  124. package/dist/config/pathProfiles.d.ts.map +1 -0
  125. package/dist/config/pathProfiles.js +47 -0
  126. package/dist/config/pathProfiles.js.map +1 -0
  127. package/dist/conventions/conventionMiner.d.ts +63 -0
  128. package/dist/conventions/conventionMiner.d.ts.map +1 -0
  129. package/dist/conventions/conventionMiner.js +696 -0
  130. package/dist/conventions/conventionMiner.js.map +1 -0
  131. package/dist/coverage/coverageService.d.ts +49 -0
  132. package/dist/coverage/coverageService.d.ts.map +1 -0
  133. package/dist/coverage/coverageService.js +117 -0
  134. package/dist/coverage/coverageService.js.map +1 -0
  135. package/dist/coverage/istanbulParser.d.ts +84 -0
  136. package/dist/coverage/istanbulParser.d.ts.map +1 -0
  137. package/dist/coverage/istanbulParser.js +120 -0
  138. package/dist/coverage/istanbulParser.js.map +1 -0
  139. package/dist/coverage/lcovParser.d.ts +40 -0
  140. package/dist/coverage/lcovParser.d.ts.map +1 -0
  141. package/dist/coverage/lcovParser.js +168 -0
  142. package/dist/coverage/lcovParser.js.map +1 -0
  143. package/dist/fingerprint.d.ts +17 -0
  144. package/dist/fingerprint.d.ts.map +1 -1
  145. package/dist/fingerprint.js +42 -0
  146. package/dist/fingerprint.js.map +1 -1
  147. package/dist/grammars/tree-sitter-css.wasm +0 -0
  148. package/dist/graph/blastRadius.d.ts +27 -0
  149. package/dist/graph/blastRadius.d.ts.map +1 -0
  150. package/dist/graph/blastRadius.js +150 -0
  151. package/dist/graph/blastRadius.js.map +1 -0
  152. package/dist/graph/callGraph.d.ts +78 -0
  153. package/dist/graph/callGraph.d.ts.map +1 -0
  154. package/dist/graph/callGraph.js +484 -0
  155. package/dist/graph/callGraph.js.map +1 -0
  156. package/dist/graph/importGraph.d.ts +91 -0
  157. package/dist/graph/importGraph.d.ts.map +1 -0
  158. package/dist/graph/importGraph.js +602 -0
  159. package/dist/graph/importGraph.js.map +1 -0
  160. package/dist/graph/outputFormatter.d.ts +55 -0
  161. package/dist/graph/outputFormatter.d.ts.map +1 -0
  162. package/dist/graph/outputFormatter.js +283 -0
  163. package/dist/graph/outputFormatter.js.map +1 -0
  164. package/dist/hooks/codex.d.ts +36 -1
  165. package/dist/hooks/codex.d.ts.map +1 -1
  166. package/dist/hooks/codex.js +88 -54
  167. package/dist/hooks/codex.js.map +1 -1
  168. package/dist/hotspots/hotspotScorer.d.ts +20 -0
  169. package/dist/hotspots/hotspotScorer.d.ts.map +1 -0
  170. package/dist/hotspots/hotspotScorer.js +202 -0
  171. package/dist/hotspots/hotspotScorer.js.map +1 -0
  172. package/dist/invariants/invariant-rules.schema.json +42 -1
  173. package/dist/invariants/ruleEngine.d.ts +4 -1
  174. package/dist/invariants/ruleEngine.d.ts.map +1 -1
  175. package/dist/invariants/ruleEngine.js +141 -5
  176. package/dist/invariants/ruleEngine.js.map +1 -1
  177. package/dist/invariants/ruleValidator.d.ts.map +1 -1
  178. package/dist/invariants/ruleValidator.js +62 -1
  179. package/dist/invariants/ruleValidator.js.map +1 -1
  180. package/dist/invariants/types.d.ts +23 -2
  181. package/dist/invariants/types.d.ts.map +1 -1
  182. package/dist/languages/UniversalAnalyzer.d.ts +1 -1
  183. package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
  184. package/dist/languages/UniversalAnalyzer.js +59 -24
  185. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  186. package/dist/languages/index.d.ts +1 -0
  187. package/dist/languages/index.d.ts.map +1 -1
  188. package/dist/languages/index.js +3 -0
  189. package/dist/languages/index.js.map +1 -1
  190. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +59 -0
  191. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -0
  192. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +236 -0
  193. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -0
  194. package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
  195. package/dist/languages/tree-sitter/parser.js +2 -0
  196. package/dist/languages/tree-sitter/parser.js.map +1 -1
  197. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  198. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +61 -10
  199. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  200. package/dist/ledger.d.ts +128 -0
  201. package/dist/ledger.d.ts.map +1 -0
  202. package/dist/ledger.js +345 -0
  203. package/dist/ledger.js.map +1 -0
  204. package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
  205. package/dist/mcp-tools/projectTasks.js +9 -14
  206. package/dist/mcp-tools/projectTasks.js.map +1 -1
  207. package/dist/mcp.js +0 -0
  208. package/dist/mcpAuditJobs.d.ts.map +1 -1
  209. package/dist/mcpAuditJobs.js +3 -0
  210. package/dist/mcpAuditJobs.js.map +1 -1
  211. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  212. package/dist/reporting/csvReportGenerator.js +7 -1
  213. package/dist/reporting/csvReportGenerator.js.map +1 -1
  214. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  215. package/dist/reporting/htmlReportGenerator.js +5 -1
  216. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  217. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  218. package/dist/reporting/jsonReportGenerator.js +10 -2
  219. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  220. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  221. package/dist/reporting/sarifReportGenerator.js +17 -17
  222. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  223. package/dist/scripts/runBench.d.ts +94 -0
  224. package/dist/scripts/runBench.d.ts.map +1 -0
  225. package/dist/scripts/runBench.js +1559 -0
  226. package/dist/scripts/runBench.js.map +1 -0
  227. package/dist/search/QueryParser.d.ts.map +1 -1
  228. package/dist/search/QueryParser.js +51 -4
  229. package/dist/search/QueryParser.js.map +1 -1
  230. package/dist/services/CodeMapGenerator.d.ts +33 -0
  231. package/dist/services/CodeMapGenerator.d.ts.map +1 -1
  232. package/dist/services/CodeMapGenerator.js +404 -0
  233. package/dist/services/CodeMapGenerator.js.map +1 -1
  234. package/dist/styles/normalizer.d.ts +37 -0
  235. package/dist/styles/normalizer.d.ts.map +1 -0
  236. package/dist/styles/normalizer.js +301 -0
  237. package/dist/styles/normalizer.js.map +1 -0
  238. package/dist/styles/styleExtractor.d.ts +38 -0
  239. package/dist/styles/styleExtractor.d.ts.map +1 -0
  240. package/dist/styles/styleExtractor.js +630 -0
  241. package/dist/styles/styleExtractor.js.map +1 -0
  242. package/dist/styles/styleIndexer.d.ts +35 -0
  243. package/dist/styles/styleIndexer.d.ts.map +1 -0
  244. package/dist/styles/styleIndexer.js +266 -0
  245. package/dist/styles/styleIndexer.js.map +1 -0
  246. package/dist/styles/tailwindConfigLoader.d.ts +38 -0
  247. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -0
  248. package/dist/styles/tailwindConfigLoader.js +374 -0
  249. package/dist/styles/tailwindConfigLoader.js.map +1 -0
  250. package/dist/styles/tailwindExpander.d.ts +38 -0
  251. package/dist/styles/tailwindExpander.d.ts.map +1 -0
  252. package/dist/styles/tailwindExpander.js +637 -0
  253. package/dist/styles/tailwindExpander.js.map +1 -0
  254. package/dist/styles/types.d.ts +92 -0
  255. package/dist/styles/types.d.ts.map +1 -0
  256. package/dist/styles/types.js +10 -0
  257. package/dist/styles/types.js.map +1 -0
  258. package/dist/symbols.d.ts +21 -0
  259. package/dist/symbols.d.ts.map +1 -0
  260. package/dist/symbols.js +31 -0
  261. package/dist/symbols.js.map +1 -0
  262. package/dist/types.d.ts +326 -3
  263. package/dist/types.d.ts.map +1 -1
  264. package/dist/types.js.map +1 -1
  265. package/package.json +9 -5
  266. package/plugin/.claude-plugin/plugin.json +1 -1
  267. package/plugin/scripts/hook-audit.sh +5 -1
  268. package/plugin/skills/code-auditor/SKILL.md +51 -0
  269. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts +0 -21
  270. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts.map +0 -1
  271. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js +0 -21
  272. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js.map +0 -1
  273. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts +0 -13
  274. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts.map +0 -1
  275. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js +0 -12
  276. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js.map +0 -1
  277. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts +0 -11
  278. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts.map +0 -1
  279. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js +0 -22
  280. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js.map +0 -1
  281. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts +0 -9
  282. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts.map +0 -1
  283. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js +0 -13
  284. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js.map +0 -1
  285. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts +0 -13
  286. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts.map +0 -1
  287. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js +0 -18
  288. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js.map +0 -1
  289. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts +0 -10
  290. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts.map +0 -1
  291. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js +0 -16
  292. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js.map +0 -1
  293. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts +0 -14
  294. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts.map +0 -1
  295. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js +0 -18
  296. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js.map +0 -1
  297. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts +0 -13
  298. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts.map +0 -1
  299. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js +0 -22
  300. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js.map +0 -1
  301. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts +0 -10
  302. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts.map +0 -1
  303. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js +0 -20
  304. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js.map +0 -1
  305. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts +0 -18
  306. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts.map +0 -1
  307. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js +0 -28
  308. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js.map +0 -1
  309. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts +0 -11
  310. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts.map +0 -1
  311. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js +0 -31
  312. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js.map +0 -1
  313. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts +0 -11
  314. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts.map +0 -1
  315. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js +0 -18
  316. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js.map +0 -1
  317. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts +0 -13
  318. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts.map +0 -1
  319. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js +0 -61
  320. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js.map +0 -1
  321. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts +0 -10
  322. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts.map +0 -1
  323. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js +0 -15
  324. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js.map +0 -1
  325. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts +0 -11
  326. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts.map +0 -1
  327. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js +0 -49
  328. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js.map +0 -1
  329. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts +0 -10
  330. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts.map +0 -1
  331. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js +0 -19
  332. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js.map +0 -1
  333. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts +0 -37
  334. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts.map +0 -1
  335. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js +0 -73
  336. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js.map +0 -1
  337. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts +0 -12
  338. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts.map +0 -1
  339. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js +0 -82
  340. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js.map +0 -1
  341. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts +0 -10
  342. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts.map +0 -1
  343. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js +0 -15
  344. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js.map +0 -1
  345. package/dist/analyzers/dataAccessAnalyzer.d.ts +0 -46
  346. package/dist/analyzers/dataAccessAnalyzer.d.ts.map +0 -1
  347. package/dist/analyzers/dataAccessAnalyzer.js +0 -474
  348. package/dist/analyzers/dataAccessAnalyzer.js.map +0 -1
  349. package/dist/analyzers/dryAnalyzer.d.ts +0 -31
  350. package/dist/analyzers/dryAnalyzer.d.ts.map +0 -1
  351. package/dist/analyzers/dryAnalyzer.js +0 -734
  352. package/dist/analyzers/dryAnalyzer.js.map +0 -1
  353. package/dist/analyzers/schemaAnalyzer.d.ts +0 -23
  354. package/dist/analyzers/schemaAnalyzer.d.ts.map +0 -1
  355. package/dist/analyzers/schemaAnalyzer.js +0 -564
  356. package/dist/analyzers/schemaAnalyzer.js.map +0 -1
  357. package/dist/analyzers/solidAnalyzer.d.ts +0 -33
  358. package/dist/analyzers/solidAnalyzer.d.ts.map +0 -1
  359. package/dist/analyzers/solidAnalyzer.js +0 -847
  360. package/dist/analyzers/solidAnalyzer.js.map +0 -1
@@ -4,7 +4,7 @@
4
4
  * Analyzes database access patterns and data layer interactions
5
5
  */
6
6
  import { UniversalAnalyzer } from '../../languages/UniversalAnalyzer.js';
7
- import { IS_DEV_MODE } from '../../constants.js';
7
+ import { buildProvenanceContext, isDBProvenanced, DB_CALL_METHODS, } from '../provenance.js';
8
8
  export const DEFAULT_DATA_ACCESS_CONFIG = {
9
9
  checkOrgFilters: true,
10
10
  checkSQLInjection: true,
@@ -28,18 +28,22 @@ export const DEFAULT_DATA_ACCESS_CONFIG = {
28
28
  'companyId',
29
29
  'company_id'
30
30
  ],
31
+ // Spec 21 R6.2: three-tier org-filter detection
32
+ orgFilterTables: [], // Tier 1: empty — user must declare
33
+ orgFilterColumns: ['org_id', 'tenant_id', 'organization_id', 'workspace_id'], // Tier 2
34
+ schemas: [], // Tier 2: schema definitions for column-based inference
31
35
  tablePatterns: {
32
36
  orm: [
33
- /from\s*\(\s*["'`]?(\w+)["'`]?\s*\)/gi,
34
- /table\s*[:=]\s*["'`]?(\w+)["'`]?/gi,
35
- /\.from\s*\(\s*(\w+)\s*\)/gi, // Handle .from(users) where users is a variable
36
- /join\s*\(\s*(\w+)\s*,/gi, // Handle joins
37
- /leftJoin\s*\(\s*(\w+)\s*,/gi,
38
- /rightJoin\s*\(\s*(\w+)\s*,/gi,
39
- /innerJoin\s*\(\s*(\w+)\s*,/gi
37
+ /from\s*\(\s*["'`]?([\p{L}\p{N}_]+)["'`]?\s*\)/giu,
38
+ /table\s*[:=]\s*["'`]?([\p{L}\p{N}_]+)["'`]?/giu,
39
+ /\.from\s*\(\s*([\p{L}\p{N}_]+)\s*\)/giu, // Handle .from(users) where users is a variable
40
+ /join\s*\(\s*([\p{L}\p{N}_]+)\s*,/giu, // Handle joins
41
+ /leftJoin\s*\(\s*([\p{L}\p{N}_]+)\s*,/giu,
42
+ /rightJoin\s*\(\s*([\p{L}\p{N}_]+)\s*,/giu,
43
+ /innerJoin\s*\(\s*([\p{L}\p{N}_]+)\s*,/giu
40
44
  ],
41
- sql: [/FROM\s+["'`]?(\w+)["'`]?/gi, /JOIN\s+["'`]?(\w+)["'`]?/gi, /UPDATE\s+["'`]?(\w+)["'`]?/gi],
42
- queryBuilder: [/\.from\s*\(\s*["'`]?(\w+)["'`]?\s*\)/gi]
45
+ sql: [/FROM\s+["'`]?([\p{L}\p{N}_]+)["'`]?/giu, /JOIN\s+["'`]?([\p{L}\p{N}_]+)["'`]?/giu, /UPDATE\s+["'`]?([\p{L}\p{N}_]+)["'`]?/giu],
46
+ queryBuilder: [/\.from\s*\(\s*["'`]?([\p{L}\p{N}_]+)["'`]?\s*\)/giu]
43
47
  },
44
48
  performanceThresholds: {
45
49
  complexQueryCount: 3,
@@ -58,26 +62,33 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
58
62
  async analyzeAST(ast, adapter, config, sourceCode) {
59
63
  const violations = [];
60
64
  const finalConfig = { ...DEFAULT_DATA_ACCESS_CONFIG, ...config };
61
- if (IS_DEV_MODE) {
62
- console.error('[DEBUG] Data access config merge:', {
63
- defaultPatterns: DEFAULT_DATA_ACCESS_CONFIG.organizationPatterns,
64
- inputConfig: config,
65
- finalPatterns: finalConfig.organizationPatterns
66
- });
67
- }
65
+ // Spec 21: Build provenance context for this file (R1 — provenance-primary detection)
66
+ const detectionMode = config.detection?.mode ?? 'hybrid';
67
+ const schemaConfig = config;
68
+ const p0 = performance.now();
69
+ const provenanceContext = buildProvenanceContext(ast, adapter, sourceCode, {
70
+ mode: detectionMode,
71
+ dbReceiverNames: schemaConfig.dbReceiverNames ?? ['db', 'database', 'sql', 'stmt', 'connection', 'pool', 'client'],
72
+ dbBindingNames: schemaConfig.dbBindingNames ?? ['env.DB'],
73
+ dbCallMethods: schemaConfig.dbCallMethods ?? [...DB_CALL_METHODS],
74
+ });
75
+ const timingAcc = schemaConfig._provenanceTiming;
76
+ if (timingAcc)
77
+ timingAcc.totalMs += performance.now() - p0;
68
78
  // Check imports for database libraries
69
79
  const imports = adapter.extractImports(ast);
70
80
  const dbImports = this.mapDatabaseImports(imports, finalConfig);
71
- // Find database calls
72
- const calls = this.extractDatabaseCalls(ast, adapter, sourceCode, dbImports, finalConfig);
73
- // Analyze each call
81
+ // Find database calls (Spec 21: uses provenance context)
82
+ const calls = this.extractDatabaseCalls(ast, adapter, sourceCode, dbImports, finalConfig, provenanceContext);
83
+ // Analyze each call — track symbol ordinals for fingerprint stability
84
+ const symbolOrdinals = new Map();
74
85
  for (const call of calls) {
75
86
  const analysis = this.analyzeQuery(call, sourceCode, finalConfig);
76
87
  // Check for violations
77
- violations.push(...this.checkViolations(call, analysis, ast.filePath, finalConfig));
88
+ violations.push(...this.checkViolations(call, analysis, ast.filePath, finalConfig, symbolOrdinals));
78
89
  }
79
- // R4.1: Check for database queries inside loops (N+1 detection)
80
- violations.push(...this.checkLoopQueries(ast, adapter, sourceCode, finalConfig));
90
+ // R4.1: Check for database queries inside loops (N+1 detection, Spec 21: provenance-gated)
91
+ violations.push(...this.checkLoopQueries(ast, adapter, sourceCode, finalConfig, provenanceContext));
81
92
  // Check for general data access patterns
82
93
  violations.push(...this.checkGeneralPatterns(ast, adapter, sourceCode, finalConfig));
83
94
  return violations;
@@ -99,26 +110,28 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
99
110
  /**
100
111
  * Extract database calls from AST
101
112
  */
102
- extractDatabaseCalls(ast, adapter, sourceCode, dbImports, config) {
113
+ extractDatabaseCalls(ast, adapter, sourceCode, dbImports, config, provenanceContext) {
103
114
  const calls = [];
104
115
  // Find all relevant nodes
105
116
  const allNodes = adapter.findNodes(ast, {
106
117
  custom: (node) => {
107
118
  const nodeText = adapter.getNodeText(node, sourceCode);
108
- // Check if it's a function call that might be database-related
119
+ // Check if it's a function call whose callee is DB-related
109
120
  if (this.isFunctionCall(node, adapter)) {
110
- // Check for ORM patterns or database method calls
111
- const dbPatterns = ['select', 'insert', 'update', 'delete', 'from', 'where', 'execute', 'query', 'find', 'aggregate', 'count', 'distinct'];
112
- const hasDbPattern = dbPatterns.some(pattern => nodeText.toLowerCase().includes(pattern));
113
- if (hasDbPattern) {
114
- // Ensure we get the full method call including arguments
115
- // For method calls like db.users.find(...), we want the entire expression
121
+ // Spec 21: Use provenance when available, fall back to name-based check
122
+ if (provenanceContext) {
123
+ if (isDBProvenanced(node, adapter, sourceCode, provenanceContext, DB_CALL_METHODS)) {
124
+ return true;
125
+ }
126
+ }
127
+ else if (this.isDBCallee(node, adapter, sourceCode)) {
116
128
  return true;
117
129
  }
118
130
  }
119
- // Check if it's a template literal with SQL
131
+ // Check if it's a template literal with SQL in a DB-call context
120
132
  if (this.isTemplateLiteral(node, adapter)) {
121
- return this.containsSQLKeywords(nodeText);
133
+ return this.containsSQLKeywords(nodeText) &&
134
+ this.isTemplateInDBCallOrVariableContext(node, adapter, sourceCode, provenanceContext);
122
135
  }
123
136
  // Check if it's a variable declaration with SQL (but not if it contains a template literal)
124
137
  if (this.isVariableAssignment(node, adapter)) {
@@ -171,24 +184,7 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
171
184
  if (isSqlQuery || isOrmCall) {
172
185
  const tables = this.extractTables(nodeText, config);
173
186
  const hasOrgFilter = this.hasOrganizationFilter(nodeText, config);
174
- // Debug: Log the analysis
175
- if (IS_DEV_MODE) {
176
- console.error('[DEBUG] Database call analysis:', {
177
- line: node.location.start.line,
178
- nodeText: nodeText.substring(0, 100),
179
- tables,
180
- hasOrgFilter,
181
- patterns: config.organizationPatterns || []
182
- });
183
- }
184
187
  const security = this.checkQuerySecurity(nodeText, config);
185
- // Debug: Log extracted tables
186
- if (tables.length === 0 && isOrmCall) {
187
- console.error('[DataAccess Debug] No tables extracted from ORM call:', {
188
- line: node.location.start.line,
189
- nodeText: nodeText.substring(0, 100)
190
- });
191
- }
192
188
  // Determine the type based on imports or patterns
193
189
  let callType = 'unknown';
194
190
  if (isSqlQuery) {
@@ -212,7 +208,8 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
212
208
  tables,
213
209
  hasOrganizationFilter: hasOrgFilter,
214
210
  hasParameterizedQuery: security.parameterized,
215
- hasSqlInjectionRisk: security.injectionRisk
211
+ hasSqlInjectionRisk: security.injectionRisk,
212
+ enclosingFunction: this.findEnclosingFunctionName(node, adapter),
216
213
  });
217
214
  }
218
215
  }
@@ -251,8 +248,15 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
251
248
  /**
252
249
  * Check for violations in a database call
253
250
  */
254
- checkViolations(call, analysis, filePath, config) {
251
+ checkViolations(call, analysis, filePath, config, symbolOrdinals) {
255
252
  const violations = [];
253
+ // Build a stable base symbol key — enclosing function + callee method.
254
+ // Ordinal added for genuine repeats within the same function.
255
+ const fnName = call.enclosingFunction ?? 'top-level';
256
+ const baseSymbol = `${fnName}:${call.method}`;
257
+ const ordinal = (symbolOrdinals.get(baseSymbol) ?? 0) + 1;
258
+ symbolOrdinals.set(baseSymbol, ordinal);
259
+ const symbol = ordinal > 1 ? `${baseSymbol}:${ordinal}` : baseSymbol;
256
260
  // Security: SQL Injection Risk
257
261
  // Spec 11 (calibration): Detection uses AST-level heuristics (string
258
262
  // concatenation in query construction) without type information. Findings
@@ -261,19 +265,19 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
261
265
  // rate on ExcAlDraw and Gin corpora to decide whether heuristics should be
262
266
  // tightened or severity re-escalated.
263
267
  if (config.checkSQLInjection && call.hasSqlInjectionRisk) {
264
- violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Potential SQL injection risk in ${call.method}. Use parameterized queries.`, 'warning', 'sql-injection-risk'));
268
+ violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Potential SQL injection risk in ${call.method}. Use parameterized queries.`, 'suggestion', 'sql-injection-risk', undefined, symbol));
265
269
  }
266
270
  // Security: Missing Organization Filter
267
- if (config.checkOrgFilters && !call.hasOrganizationFilter && call.tables.length > 0 && this.requiresOrgFilter(call.tables)) {
268
- violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Query on ${call.tables.join(', ')} missing organization/tenant filter`, 'warning', 'missing-org-filter'));
271
+ if (config.checkOrgFilters && !call.hasOrganizationFilter && call.tables.length > 0 && this.requiresOrgFilter(call.tables, config)) {
272
+ violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Query on ${call.tables.join(', ')} missing organization/tenant filter`, 'warning', 'missing-org-filter', undefined, symbol));
269
273
  }
270
274
  // Performance: Complex Query
271
275
  if (analysis.performanceRisk === 'high') {
272
- violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Complex query with ${call.tables.length} tables may have performance issues`, 'warning', 'complex-query'));
276
+ violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Complex query with ${call.tables.length} tables may have performance issues`, 'warning', 'complex-query', undefined, symbol));
273
277
  }
274
278
  // Performance: Unfiltered Query
275
279
  if (!call.hasOrganizationFilter && analysis.performanceRisk === 'medium') {
276
- violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Unfiltered query on ${call.tables.join(', ')} may cause performance issues`, 'suggestion', 'unfiltered-query'));
280
+ violations.push(this.createViolation(filePath, { line: call.line, column: call.column }, `Unfiltered query on ${call.tables.join(', ')} may cause performance issues`, 'suggestion', 'unfiltered-query', undefined, symbol));
277
281
  }
278
282
  return violations;
279
283
  }
@@ -290,11 +294,17 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
290
294
  const stringNodes = adapter.findNodes(ast, {
291
295
  custom: (node) => this.isStringLiteral(node, adapter)
292
296
  });
297
+ const hardcodedOrdinals = new Map();
293
298
  for (const node of stringNodes) {
294
299
  const text = adapter.getNodeText(node, sourceCode);
295
300
  if (this.isConnectionString(text)) {
301
+ const fnName = this.findEnclosingFunctionName(node, adapter);
302
+ const baseSym = `${fnName}:hardcoded-connection`;
303
+ const count = (hardcodedOrdinals.get(baseSym) ?? 0) + 1;
304
+ hardcodedOrdinals.set(baseSym, count);
305
+ const sym = count > 1 ? `${baseSym}:${count}` : baseSym;
296
306
  violations.push(this.createViolation(ast.filePath, node.location.start, 'Hardcoded database connection string detected. Use environment variables. (On Cloudflare Workers/D1, connection strings are injected via bindings.)', 'suggestion', // R7: direct-access → suggestion
297
- 'hardcoded-connection'));
307
+ 'hardcoded-connection', undefined, sym));
298
308
  }
299
309
  }
300
310
  // Check for direct SQL execution without ORM
@@ -302,7 +312,7 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
302
312
  for (const pattern of sqlPatterns) {
303
313
  if (pattern.test(sourceCode)) {
304
314
  violations.push(this.createViolation(ast.filePath, { line: 1, column: 1 }, 'Direct SQL execution detected. Consider using an ORM or query builder.', 'suggestion', // R7: direct-access → suggestion
305
- 'direct-sql'));
315
+ 'direct-sql', undefined, 'direct-sql'));
306
316
  break;
307
317
  }
308
318
  }
@@ -328,6 +338,59 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
328
338
  const upperText = text.toUpperCase();
329
339
  return sqlKeywords.some(keyword => upperText.includes(keyword));
330
340
  }
341
+ /**
342
+ * R3.1: Check if a call expression's callee (excluding arguments)
343
+ * matches database-related patterns. Prevents false positives from
344
+ * non-DB calls like page.evaluate() whose arguments happen to contain
345
+ * SQL-like substrings.
346
+ */
347
+ isDBCallee(node, adapter, sourceCode) {
348
+ const children = adapter.getChildren(node);
349
+ const calleeParts = [];
350
+ for (const child of children) {
351
+ if (adapter.getNodeType(child) === 'arguments')
352
+ break;
353
+ calleeParts.push(adapter.getNodeText(child, sourceCode));
354
+ }
355
+ const calleeText = calleeParts.join('');
356
+ const dbPatterns = ['select', 'insert', 'update', 'delete', 'from', 'where', 'execute', 'query', 'find', 'aggregate', 'count', 'distinct'];
357
+ return dbPatterns.some(pattern => calleeText.toLowerCase().includes(pattern));
358
+ }
359
+ /**
360
+ * R3.1: Gate template literal SQL detection by parent context.
361
+ * Template literals in non-DB sinks (console.log, page.evaluate) must not
362
+ * trigger sql-injection-risk. Conservative: variable assignments and bare
363
+ * statements pass the gate (they may flow to a DB call).
364
+ *
365
+ * Spec 21: When provenance context is available, uses provenance-based callee
366
+ * check (conjunctive guard). Falls back to isDBCallee() when no context.
367
+ */
368
+ isTemplateInDBCallOrVariableContext(node, adapter, sourceCode, provenanceContext) {
369
+ const parent = adapter.getParent(node);
370
+ if (!parent)
371
+ return false;
372
+ const parentType = adapter.getNodeType(parent);
373
+ // Inside arguments of a call expression → check callee is DB-related
374
+ if (parentType === 'arguments') {
375
+ const callExpr = adapter.getParent(parent);
376
+ if (!callExpr || adapter.getNodeType(callExpr) !== 'call_expression')
377
+ return false;
378
+ // Spec 21: Use provenance when available
379
+ if (provenanceContext) {
380
+ return isDBProvenanced(callExpr, adapter, sourceCode, provenanceContext, DB_CALL_METHODS);
381
+ }
382
+ return this.isDBCallee(callExpr, adapter, sourceCode);
383
+ }
384
+ // Variable assignment → conservative (may eventually reach a DB call)
385
+ if (parentType === 'binary_expression' || parentType === 'variable_declarator' || parentType === 'variable_declaration') {
386
+ return true;
387
+ }
388
+ // Statement-level or program-level → conservative
389
+ if (parentType === 'expression_statement' || parentType === 'program') {
390
+ return true;
391
+ }
392
+ return false;
393
+ }
331
394
  isOrmPattern(text) {
332
395
  // Common ORM method patterns
333
396
  const ormPatterns = [
@@ -381,7 +444,7 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
381
444
  });
382
445
  // Additional check for common ORM patterns that might be missed
383
446
  // Handle patterns like db.select().from(users) where 'users' is a variable
384
- const ormVariablePattern = /\.from\s*\(\s*([a-zA-Z_]\w*)\s*\)/g;
447
+ const ormVariablePattern = /\.from\s*\(\s*([\p{L}_][\p{L}\p{N}_]*)\s*\)/gu;
385
448
  const ormMatches = text.matchAll(ormVariablePattern);
386
449
  for (const match of ormMatches) {
387
450
  if (match[1] && !match[1].includes('"') && !match[1].includes("'")) {
@@ -389,7 +452,7 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
389
452
  }
390
453
  }
391
454
  // Handle patterns like db.users.find() or db.orders.findOne()
392
- const dbTablePattern = /db\.([a-zA-Z_]\w*)\.\w+\s*\(/g;
455
+ const dbTablePattern = /db\.([\p{L}_][\p{L}\p{N}_]*)\.\p{L}[\p{L}\p{N}_]*\s*\(/gu;
393
456
  const dbMatches = text.matchAll(dbTablePattern);
394
457
  for (const match of dbMatches) {
395
458
  if (match[1]) {
@@ -401,33 +464,17 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
401
464
  hasOrganizationFilter(text, config) {
402
465
  const patterns = config.organizationPatterns || [];
403
466
  const lowerText = text.toLowerCase();
404
- if (IS_DEV_MODE) {
405
- console.error('[DEBUG] hasOrganizationFilter called with:', {
406
- text: text.substring(0, 100),
407
- patternsFromConfig: patterns,
408
- patternsLength: patterns.length
409
- });
410
- }
411
467
  // If no patterns provided, check for hardcoded common patterns as fallback
412
468
  const fallbackPatterns = patterns.length === 0 ? [
413
469
  'organizationid', 'organization_id', 'orgid', 'org_id',
414
470
  'tenantid', 'tenant_id', 'companyid', 'company_id'
415
471
  ] : patterns;
416
- if (IS_DEV_MODE) {
417
- console.error('[DEBUG] Using fallback patterns:', fallbackPatterns);
418
- }
419
472
  // Check for simple pattern matches first
420
473
  const hasSimpleMatch = fallbackPatterns.some(pattern => {
421
474
  const match = lowerText.includes(pattern.toLowerCase());
422
- if (IS_DEV_MODE) {
423
- console.error(`[DEBUG] Pattern "${pattern}" -> "${pattern.toLowerCase()}" in "${lowerText}": ${match}`);
424
- }
425
475
  return match;
426
476
  });
427
477
  if (hasSimpleMatch) {
428
- if (IS_DEV_MODE) {
429
- console.error('[DEBUG] Found simple match, returning true');
430
- }
431
478
  return true;
432
479
  }
433
480
  // Enhanced pattern matching for object properties and SQL WHERE clauses
@@ -456,15 +503,6 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
456
503
  return true;
457
504
  }
458
505
  }
459
- // Debug logging
460
- if (IS_DEV_MODE) {
461
- console.error('[DEBUG] hasOrganizationFilter check:', {
462
- text: text.substring(0, 150),
463
- patterns,
464
- fallbackPatterns,
465
- hasFilter: false
466
- });
467
- }
468
506
  return false;
469
507
  }
470
508
  checkQuerySecurity(text, config) {
@@ -482,23 +520,60 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
482
520
  ];
483
521
  const configPatterns = config.securityPatterns?.sqlInjectionRisks || [];
484
522
  const allPatterns = [...sqlInjectionPatterns, ...configPatterns];
485
- const injectionRisk = allPatterns.some(pattern => text.includes(pattern)) &&
523
+ const injectionRisk = !parameterized &&
524
+ allPatterns.some(pattern => text.includes(pattern)) &&
486
525
  this.containsSQLKeywords(text);
487
526
  return { parameterized, injectionRisk };
488
527
  }
489
528
  extractMethodName(node, adapter, sourceCode) {
490
529
  const text = adapter.getNodeText(node, sourceCode);
491
- const match = text.match(/(\w+)\s*\(/);
530
+ const match = text.match(/([\p{L}\p{N}_]+)\s*\(/u);
492
531
  return match ? match[1] : 'unknown';
493
532
  }
494
- requiresOrgFilter(tables) {
495
- // Tables that typically need organization filtering
496
- const orgTables = ['users', 'projects', 'orders', 'customers', 'accounts', 'teams'];
533
+ /**
534
+ * Spec 21 R6.2: Three-tier org-filter detection.
535
+ *
536
+ * Tier 1 (config-primary): `orgFilterTables` — the user's explicit declaration
537
+ * of which tables are multi-tenant. Tenancy is policy; this is the
538
+ * declaration of record.
539
+ *
540
+ * Tier 2 (usage-inference secondary): A table requires the filter if the
541
+ * project's own corpus shows it scoped — i.e., a column matching
542
+ * `orgFilterColumns` (default: org_id/tenant_id/organization_id/workspace_id)
543
+ * exists on it in the schema definitions in config.
544
+ * This is what makes non-English table names (e.g., 注文) detectable
545
+ * with zero explicit orgFilterTables declaration.
546
+ *
547
+ * Tier 3 (fallback): English table list retained as defaults, evidence-tagged
548
+ * `fallback` like every other name list in Spec 21.
549
+ */
550
+ requiresOrgFilter(tables, config) {
551
+ const orgFilterTables = config.orgFilterTables ?? [];
552
+ const orgFilterColumns = config.orgFilterColumns ?? ['org_id', 'tenant_id', 'organization_id', 'workspace_id'];
553
+ const schemas = config.schemas ?? [];
554
+ const fallbackOrgTables = ['users', 'projects', 'orders', 'customers', 'accounts', 'teams'];
555
+ // Build a lookup set of tables from schema definitions that have an
556
+ // org-filter column — this is the usage-inference tier.
557
+ const schemaOrgTables = new Set();
558
+ for (const schema of schemas) {
559
+ for (const table of schema.tables) {
560
+ if (table.columns.some(c => orgFilterColumns.includes(c.name.toLowerCase()))) {
561
+ schemaOrgTables.add(table.name.toLowerCase());
562
+ }
563
+ }
564
+ }
497
565
  return tables.some(table => {
498
- // Handle both actual table names and variable names that likely represent tables
499
- const tableName = table.toLowerCase();
500
- // Check exact match or if the table variable name matches (e.g., 'users' variable for 'users' table)
501
- return orgTables.includes(tableName) || orgTables.some(orgTable => tableName === orgTable);
566
+ const tableLower = table.toLowerCase();
567
+ // Tier 1: config-primary — explicit user declaration
568
+ if (orgFilterTables.some(t => t.toLowerCase() === tableLower)) {
569
+ return true;
570
+ }
571
+ // Tier 2: schema-based inference — table has an org-filter column
572
+ if (schemaOrgTables.has(tableLower)) {
573
+ return true;
574
+ }
575
+ // Tier 3: English fallback
576
+ return fallbackOrgTables.includes(tableLower);
502
577
  });
503
578
  }
504
579
  isStringLiteral(node, adapter) {
@@ -519,14 +594,16 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
519
594
  * R4.1: Find database queries inside loops and flag them as N+1 risks.
520
595
  * Each finding carries the query call location (never line 1).
521
596
  */
522
- checkLoopQueries(ast, adapter, sourceCode, config) {
597
+ checkLoopQueries(ast, adapter, sourceCode, config, provenanceContext) {
523
598
  const violations = [];
524
- // Find all nodes that look like database calls
599
+ // Find all nodes that look like database calls (Spec 21: provenance-gated)
525
600
  const dbNodes = adapter.findNodes(ast, {
526
- custom: (node) => this.isDbCallNode(node, adapter, sourceCode),
601
+ custom: (node) => this.isDbCallNode(node, adapter, sourceCode, provenanceContext),
527
602
  });
528
- // Track reported line+loop combos to avoid duplicates
603
+ // Track reported line+loop combos to avoid duplicates (runtime dedup only)
529
604
  const reported = new Set();
605
+ // Track symbol ordinals per enclosing function for fingerprint stability
606
+ const loopOrdinals = new Map();
530
607
  for (const node of dbNodes) {
531
608
  const nodeText = adapter.getNodeText(node, sourceCode);
532
609
  if (!nodeText || nodeText.trim().length < 10)
@@ -541,6 +618,12 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
541
618
  if (reported.has(dedupKey))
542
619
  continue;
543
620
  reported.add(dedupKey);
621
+ // Stable fingerprint symbol: enclosing function + ordinal
622
+ const enclosingFn = this.findEnclosingFunctionName(node, adapter);
623
+ const baseSym = `${enclosingFn}:loop-query`;
624
+ const count = (loopOrdinals.get(baseSym) ?? 0) + 1;
625
+ loopOrdinals.set(baseSym, count);
626
+ const sym = count > 1 ? `${baseSym}:${count}` : baseSym;
544
627
  // R4.2: Nested-loop attribution
545
628
  const depthMsg = loopInfo.depth > 1
546
629
  ? ` (nested ${loopInfo.depth} levels deep)`
@@ -549,7 +632,7 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
549
632
  `Database query inside loop${depthMsg} ` +
550
633
  `(loop at line ${loopInfo.loopNode.location.start.line}). ` +
551
634
  `This may cause N+1 performance issues. Consider batching queries or using a join.`, 'warning', // R7: loop-query → warning
552
- 'loop-query'));
635
+ 'loop-query', undefined, sym));
553
636
  }
554
637
  return violations;
555
638
  }
@@ -557,16 +640,35 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
557
640
  * R4.1: Determine if a node is a database call expression.
558
641
  * Lightweight check — reused from extractDatabaseCalls logic.
559
642
  */
560
- isDbCallNode(node, adapter, sourceCode) {
643
+ /**
644
+ * R4.1: Determine if a node is a database call expression.
645
+ * Spec 21: When provenance context is available, uses provenance-based detection
646
+ * (conjunctive guard — never name alone). In names mode or without context,
647
+ * falls back to the legacy dbPatterns text match.
648
+ */
649
+ isDbCallNode(node, adapter, sourceCode, provenanceContext) {
650
+ // Spec 21: Provenance-first detection when context is available
651
+ if (provenanceContext && provenanceContext.mode !== 'names') {
652
+ // In provenance or hybrid mode, use provenance check
653
+ if (this.isFunctionCall(node, adapter)) {
654
+ if (isDBProvenanced(node, adapter, sourceCode, provenanceContext, DB_CALL_METHODS)) {
655
+ return true;
656
+ }
657
+ }
658
+ // Template literal with SQL keywords — syntax check, no name involved
659
+ if (this.isTemplateLiteral(node, adapter)) {
660
+ return this.containsSQLKeywords(adapter.getNodeText(node, sourceCode));
661
+ }
662
+ return false;
663
+ }
664
+ // Legacy fallback: name-based matching for names mode / no context
561
665
  const nodeText = adapter.getNodeText(node, sourceCode);
562
- // Call-expression with DB patterns
563
666
  if (this.isFunctionCall(node, adapter)) {
564
667
  const dbPatterns = ['select', 'insert', 'update', 'delete', 'from', 'where', 'execute', 'query', 'find', 'aggregate', 'count', 'distinct'];
565
668
  if (dbPatterns.some(pattern => nodeText.toLowerCase().includes(pattern))) {
566
669
  return true;
567
670
  }
568
671
  }
569
- // Template literal with SQL keywords
570
672
  if (this.isTemplateLiteral(node, adapter)) {
571
673
  return this.containsSQLKeywords(nodeText);
572
674
  }
@@ -653,5 +755,59 @@ export class UniversalDataAccessAnalyzer extends UniversalAnalyzer {
653
755
  }
654
756
  return '';
655
757
  }
758
+ /**
759
+ * Walk the parent chain to find the enclosing function/method/arrow name.
760
+ * Returns 'top-level' if no enclosing function is found.
761
+ *
762
+ * Used for stable fingerprint symbols — the enclosing function name is
763
+ * immune to line drift (Spec 18 Gap 2).
764
+ */
765
+ findEnclosingFunctionName(node, adapter) {
766
+ let current = adapter.getParent(node);
767
+ while (current) {
768
+ const type = adapter.getNodeType(current);
769
+ // Arrow functions, function declarations, function expressions
770
+ if (type === 'arrow_function' ||
771
+ type === 'function_declaration' ||
772
+ type === 'function_expression' ||
773
+ type === 'generator_function_declaration' ||
774
+ type === 'generator_function_expression') {
775
+ const name = this.getNodeName(current, adapter);
776
+ if (name)
777
+ return name;
778
+ }
779
+ // Method definitions on classes/objects
780
+ if (type === 'method_definition' || adapter.isMethod(current)) {
781
+ const name = this.getNodeName(current, adapter);
782
+ if (name)
783
+ return name;
784
+ }
785
+ current = adapter.getParent(current);
786
+ }
787
+ return 'top-level';
788
+ }
789
+ /**
790
+ * Extract the name/identifier from a function or method AST node.
791
+ */
792
+ getNodeName(node, adapter) {
793
+ // Try named children first
794
+ if (node.name && typeof node.name === 'string')
795
+ return node.name;
796
+ if (node.text && typeof node.text === 'string')
797
+ return node.text;
798
+ // Walk children for identifier / property_identifier
799
+ if (node.children) {
800
+ for (const child of node.children) {
801
+ const childType = adapter.getNodeType(child);
802
+ if (childType === 'identifier' ||
803
+ childType === 'property_identifier') {
804
+ const name = this.getNodeName(child, adapter);
805
+ if (name)
806
+ return name;
807
+ }
808
+ }
809
+ }
810
+ return '';
811
+ }
656
812
  }
657
813
  //# sourceMappingURL=UniversalDataAccessAnalyzer.js.map