code-auditor-mcp 3.4.9 → 3.4.11
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.
- package/.claude-plugin/marketplace.json +10 -3
- package/CHANGELOG.md +81 -0
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +2 -1
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +0 -2
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +74 -99
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +1 -5
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +13 -69
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts +1 -6
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.js +16 -41
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +20 -3
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +4 -165
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +43 -69
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +62 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +357 -3
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +81 -69
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +319 -325
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +32 -37
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/auditRunner.d.ts +2 -7
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +306 -434
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.js +27 -5
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB.d.ts +22 -0
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +92 -44
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/defaults.js +3 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/conventions/conventionMiner.d.ts +5 -4
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +65 -25
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/dataPaths.js +1 -1
- package/dist/dataPaths.js.map +1 -1
- package/dist/invariants/index.d.ts +1 -1
- package/dist/invariants/index.d.ts.map +1 -1
- package/dist/invariants/index.js +1 -1
- package/dist/invariants/index.js.map +1 -1
- package/dist/invariants/ruleEngine.d.ts +38 -3
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +42 -174
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts +2 -2
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +3 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +3 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +60 -16
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +10 -6
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp.js +0 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +4 -1
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/pipeline.d.ts +74 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +620 -0
- package/dist/pipeline.js.bak +465 -0
- package/dist/pipeline.js.bak2 +465 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/pipelineAdapters.d.ts +116 -0
- package/dist/pipelineAdapters.d.ts.map +1 -0
- package/dist/pipelineAdapters.js +1146 -0
- package/dist/pipelineAdapters.js.map +1 -0
- package/dist/pipelineTypes.d.ts +44 -0
- package/dist/pipelineTypes.d.ts.map +1 -0
- package/dist/pipelineTypes.js +118 -0
- package/dist/pipelineTypes.js.map +1 -0
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +2 -1
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +4 -2
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +2 -1
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/styles/cssAstExtractor.d.ts +46 -0
- package/dist/styles/cssAstExtractor.d.ts.map +1 -0
- package/dist/styles/cssAstExtractor.js +439 -0
- package/dist/styles/cssAstExtractor.js.map +1 -0
- package/dist/styles/styleExtractor.d.ts +2 -2
- package/dist/styles/styleExtractor.d.ts.map +1 -1
- package/dist/styles/styleExtractor.js +69 -16
- package/dist/styles/styleExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +4 -0
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindProbe.d.ts +2 -0
- package/dist/styles/tailwindProbe.d.ts.map +1 -1
- package/dist/styles/tailwindProbe.js +5 -5
- package/dist/styles/tailwindProbe.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts +3 -0
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +7 -0
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/types.d.ts +216 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +4 -0
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +7 -1
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +32 -0
- package/dist/utils/reactDetection.js.map +1 -1
- package/package.json +2 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
|
@@ -10,11 +10,18 @@
|
|
|
10
10
|
"name": "code-auditor",
|
|
11
11
|
"source": "./plugin",
|
|
12
12
|
"description": "Diff-scoped code quality auditing on every edit. Indexes your codebase, enforces invariants, and feeds violations back to the agent so fixes happen inline.",
|
|
13
|
-
"version": "3.4.
|
|
13
|
+
"version": "3.4.11",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Ben Hammond"
|
|
16
16
|
},
|
|
17
|
-
"keywords": [
|
|
17
|
+
"keywords": [
|
|
18
|
+
"code-quality",
|
|
19
|
+
"audit",
|
|
20
|
+
"solid",
|
|
21
|
+
"linting",
|
|
22
|
+
"invariants"
|
|
23
|
+
]
|
|
18
24
|
}
|
|
19
|
-
]
|
|
25
|
+
],
|
|
26
|
+
"version": "3.4.11"
|
|
20
27
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,87 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the Code Auditor MCP project.
|
|
4
4
|
|
|
5
|
+
## [3.4.11] — unreleased
|
|
6
|
+
|
|
7
|
+
### Spec 26: Consume the ASTs We Already Produce
|
|
8
|
+
|
|
9
|
+
Stage 1 parses every `.css` file into a tree-sitter AST, but all CSS analysis
|
|
10
|
+
operated via regex on raw source text. Spec 26 converts CSS extraction and
|
|
11
|
+
several TypeScript subsystems to walk the AST via adapter methods — the regex
|
|
12
|
+
only classifies tokens; the AST discovers structure.
|
|
13
|
+
|
|
14
|
+
#### Phase 2 — CSS AST extraction (`cssAstExtractor.ts`)
|
|
15
|
+
|
|
16
|
+
CSS declarations, class names, and custom-property tokens are now extracted from
|
|
17
|
+
tree-sitter-css ASTs via three pure functions: `extractDeclarationsFromCSSAst`,
|
|
18
|
+
`extractClassUsageFromCSSAst`, `extractTokensFromCSSAst`. `.scss` files stay on
|
|
19
|
+
the regex path (tree-sitter-css is not an SCSS grammar).
|
|
20
|
+
|
|
21
|
+
Five parser bugs in the retired regex path are eliminated by AST structure:
|
|
22
|
+
|
|
23
|
+
- **Bug 1** (whitespace before `@`-rules): `at_rule` nodes are always correct
|
|
24
|
+
- **Bug 2** (`@apply` in rule sets): handled via ERROR/postcss_statement/at_rule children
|
|
25
|
+
- **Bug 3** (nested `@`-rule brace): tree-sitter handles brace matching
|
|
26
|
+
- **Bug 4** (backward-scan boundary): no backward scanning — selectors from `selectors` child
|
|
27
|
+
- **Bug 5** (CSS comments as class names): comment nodes are typed, naturally excluded
|
|
28
|
+
|
|
29
|
+
The `;` stripping fix in `getPropertyAndValue()` (tree-sitter-css includes the
|
|
30
|
+
trailing semicolon in declaration node text) uncovered 53 legitimate
|
|
31
|
+
cross-mechanism violations previously hidden: 14 token-bypass + 39
|
|
32
|
+
mechanism-fragmentation. Styles total: 119 (was 66).
|
|
33
|
+
|
|
34
|
+
Also fixed: `extractClassUsageFromCSSAst` filters by parent type `class_selector`
|
|
35
|
+
to exclude pseudo-classes (`:hover`, `:focus`) and pseudo-elements.
|
|
36
|
+
|
|
37
|
+
#### Phase 1 — TypeScript subsystems
|
|
38
|
+
|
|
39
|
+
- **Invariants** (`ruleEngine.ts`): `extractImports()` and `extractExportedSymbols()`
|
|
40
|
+
regex functions deleted. `function-index` visitor now emits `imports`,
|
|
41
|
+
`dynamicImports`, and `exports` facts per file — invariants reducer consumes
|
|
42
|
+
them via `fileData` instead of re-extracting from source.
|
|
43
|
+
- **Conventions** (`conventionMiner.ts`): `detectExportForm()` now accepts
|
|
44
|
+
`ExportInfo[]` from `extractExports(ast)` instead of 6 regexes on raw source.
|
|
45
|
+
Export-shape detection found one additional legitimate violation
|
|
46
|
+
(`AdminModerationShell`) the regex path missed.
|
|
47
|
+
- **Documentation** (`documentationAnalyzer.ts`): `getJSDocText()` and
|
|
48
|
+
`getPrecedingJSDocComments()` replaced by `adapter.getDocumentation()`.
|
|
49
|
+
|
|
50
|
+
### SQL injection parameterization detection
|
|
51
|
+
|
|
52
|
+
Four new methods in `UniversalDataAccessAnalyzer.ts` detect safe parameterized
|
|
53
|
+
SQL patterns before the keyword/dynamic-part check:
|
|
54
|
+
|
|
55
|
+
- `isInPrepareBindChain`: detects `.prepare().bind()` chains including the
|
|
56
|
+
two-statement pattern (`const stmt = db.prepare(sql); stmt.bind(x).all()`)
|
|
57
|
+
- `isInExecChain`: detects safe DO `.exec()` with spread binds
|
|
58
|
+
- `isD1ConvenienceCall`: detects D1 convenience methods (`.all()`, `.first()`,
|
|
59
|
+
`.run()`) with bind parameters
|
|
60
|
+
|
|
61
|
+
18 legitimate `sql-injection-risk` violations remain (down from ~55 false
|
|
62
|
+
positives): 3 DO `.exec()` calls without detectable parameterization, 15
|
|
63
|
+
`prepare()` calls where the bind chain cannot be verified (dynamic table/column
|
|
64
|
+
names not bind-able).
|
|
65
|
+
|
|
66
|
+
### unknown-table = 8 (all generation_queue)
|
|
67
|
+
|
|
68
|
+
The DO DDL visitor correctly identifies 8 references to the `generation_queue`
|
|
69
|
+
table in `src/pages/api/admin/generation/queue.ts`. The table was dropped in
|
|
70
|
+
migration 0198 — a dead admin page referencing a removed table.
|
|
71
|
+
|
|
72
|
+
## [3.4.10] — 2026-07-31
|
|
73
|
+
|
|
74
|
+
### Accuracy fix: Phantom cross-domain lifecycle violations from over-broad DB detection defaults
|
|
75
|
+
|
|
76
|
+
`dbReceiverNames` and `dbCallMethods` defaults existed in three (actually seven) copies across the codebase, and they disagreed: the type annotations at `UniversalSchemaAnalyzer.ts` described a narrow 4-entry / 6-entry list, but `DEFAULT_SCHEMA_CONFIG` itself held a wider 7-entry / 9-entry list (`connection`, `pool`, `client`, `query`, `get`, `each`), `pipelineAdapters.ts` hardcoded the narrow list (correct by accident), `UniversalDataAccessAnalyzer.ts` had its own 7-entry / 11-entry copy via `...DB_CALL_METHODS`, and `config/defaults.ts` held the wide list for user-facing help.
|
|
77
|
+
|
|
78
|
+
The wide lists matched non-DB code: `env.VECTORIZE.query()` (Vectorize binding, not a DB client), `Map.get()`, jQuery `.each()`, WebSocket `connection.status`, generic `pool.length`, and `client.` appearing only in comments. Every match was a phantom — the extended entries produced zero signal across the entire recall-protocol corpus.
|
|
79
|
+
|
|
80
|
+
**Breaking ground-truth change:** `DEFAULT_SCHEMA_CONFIG.dbReceiverNames` and `.dbCallMethods` are now the narrow lists (`db, database, sql, stmt` / `exec, prepare, batch, run, all, first`). All inline fallbacks in `passesFileGate`, `findTableReferences`, and `buildProvenanceContext` reference `DEFAULT_SCHEMA_CONFIG` as the single source of truth. `config/defaults.ts` matches. `pipelineAdapters.ts` reads `DEFAULT_SCHEMA_CONFIG` instead of hardcoding.
|
|
81
|
+
|
|
82
|
+
This is not an internal cleanup — any user with a variable named `pool`, `client`, or `connection` was receiving phantom cross-domain `written-never-read` / `read-never-written` / `transaction-boundary` findings, and phantom data-access violations on generic `.query()` / `.get()` / `.each()` calls. The fix removes these false positives.
|
|
83
|
+
|
|
84
|
+
Cross-domain: 37 (was 43). Data-access: 1826 (was 1827). All 803 tests pass.
|
|
85
|
+
|
|
5
86
|
## [3.4.8] — 2026-07-28
|
|
6
87
|
|
|
7
88
|
### Fix: React Analyzer Bench F1 — Config Merge + Heuristic Fix
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzerUtils.d.ts","sourceRoot":"","sources":["../../src/analyzers/analyzerUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"analyzerUtils.d.ts","sourceRoot":"","sources":["../../src/analyzers/analyzerUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AAWrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAE9B;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,GAAG,EACX,UAAU,CAAC,EAAE,MAAM,KAChB,OAAO,CAAC,SAAS,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAEtF;;;GAGG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,oBAAoB,EACjC,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE,GAAQ,EAChB,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,cAAc,CAAC,CA8CzB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,GACd,SAAS,CAEX;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,GACZ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAGlC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAErD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE7D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GACpC,MAAM,CAMR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GACpC,OAAO,EAAE,CAMX;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAC/B,IAAI,CAEN;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,SAAS,EAAE,EACvB,WAAW,CAAC,EAAE,aAAa,GAC1B,SAAS,EAAE,CAWb;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAcnE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,oBAAoB,EAClC,aAAa,GAAE,GAAQ,WAEF,MAAM,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,6BAWnG"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* These replace the BaseAnalyzer class with composable functions
|
|
4
4
|
*/
|
|
5
5
|
import * as fs from 'fs/promises';
|
|
6
|
+
import { makeVisitorStatus } from '../pipeline.js';
|
|
6
7
|
import { walkAST, isExported, getNodeName as bridgeGetNodeName, calculateComplexity as bridgeCalculateComplexity, getLineAndColumn as bridgeGetLineAndColumn, } from '../languages/adapterBridge.js';
|
|
7
8
|
// Re-export utilities from other modules
|
|
8
9
|
export { parseTypeScriptFile } from '../utils/astParser.js';
|
|
@@ -42,7 +43,7 @@ export async function processFiles(files, analyzeFile, analyzerName, config = {}
|
|
|
42
43
|
}
|
|
43
44
|
const result = {
|
|
44
45
|
violations,
|
|
45
|
-
|
|
46
|
+
status: makeVisitorStatus(processedFiles),
|
|
46
47
|
executionTime: Date.now() - startTime,
|
|
47
48
|
analyzerName
|
|
48
49
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzerUtils.js","sourceRoot":"","sources":["../../src/analyzers/analyzerUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAElC,OAAO,EACL,OAAO,EACP,UAAU,EACV,WAAW,IAAI,iBAAiB,EAChC,mBAAmB,IAAI,yBAAyB,EAChD,gBAAgB,IAAI,sBAAsB,GAC3C,MAAM,+BAA+B,CAAC;AAEvC,yCAAyC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAiB9B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAe,EACf,WAAiC,EACjC,YAAoB,EACpB,MAAM,GAAQ,EAAE,EAChB,gBAAmC;IAEnC,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,MAAM,GAA2C,EAAE,CAAC;IAC1D,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,kBAAkB;YAClB,IAAI,gBAAgB,EAAE,CAAC;gBACrB,gBAAgB,CAAC,cAAc,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACvD,CAAC;YAED,sBAAsB;YACtB,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;YAC7E,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;YAEvD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,iBAAiB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjF,CAAC;YAED,qEAAqE;YACrE,MAAM,cAAc,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACrE,UAAU,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;YAEnC,cAAc,EAAE,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YAC3C,OAAO,CAAC,KAAK,CAAC,mBAAmB,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAmB;QAC7B,UAAU;QACV,
|
|
1
|
+
{"version":3,"file":"analyzerUtils.js","sourceRoot":"","sources":["../../src/analyzers/analyzerUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,8 +25,6 @@ export declare class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
25
25
|
readonly name = "cross-domain";
|
|
26
26
|
readonly description = "Detects cross-domain issues (schema lifecycle, validation bypass, coverage gaps)";
|
|
27
27
|
readonly category = "architecture";
|
|
28
|
-
/** Project root for DB scoping (Bug #4 / Item 1). */
|
|
29
|
-
private projectRoot;
|
|
30
28
|
/**
|
|
31
29
|
* Full override: query the code index DB for cross-domain findings.
|
|
32
30
|
* The base-class per-file AST loop is bypassed — all detection is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CrossDomainAnalyzer.d.ts","sourceRoot":"","sources":["../../../src/analyzers/crossDomain/CrossDomainAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAoD,MAAM,gBAAgB,CAAC;AACvG,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"CrossDomainAnalyzer.d.ts","sourceRoot":"","sources":["../../../src/analyzers/crossDomain/CrossDomainAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAoD,MAAM,gBAAgB,CAAC;AACvG,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AA2BzE,qBAAa,mBAAoB,SAAQ,iBAAiB;IACxD,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,WAAW,sFACiE;IACrF,QAAQ,CAAC,QAAQ,kBAAkB;IAEnC;;;;OAIG;IACG,OAAO,CACX,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,GAAE,GAAQ,EAChB,OAAO,GAAE,GAAQ,GAChB,OAAO,CAAC,cAAc,CAAC,CAgFzB;IAED,yCAAyC;IACnC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAEjC;IAID;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAuC9B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAsC9B;;;;;;;OAOG;IACH,OAAO,CAAC,6BAA6B;IAyIrC;;;;;;;;;OASG;IACH,OAAO,CAAC,sBAAsB;IA6K9B;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IA+K3B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;CAwC5B"}
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import * as path from 'node:path';
|
|
23
23
|
import { UniversalAnalyzer } from '../../languages/UniversalAnalyzer.js';
|
|
24
|
-
import { CodeIndexDB } from '../../codeIndexDB.js';
|
|
25
24
|
import { VALIDATOR_PACKAGES } from '../provenance.js';
|
|
25
|
+
import { makeVisitorStatus } from '../../pipeline.js';
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
// Analyzer
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
@@ -30,8 +30,6 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
30
30
|
name = 'cross-domain';
|
|
31
31
|
description = 'Detects cross-domain issues (schema lifecycle, validation bypass, coverage gaps)';
|
|
32
32
|
category = 'architecture';
|
|
33
|
-
/** Project root for DB scoping (Bug #4 / Item 1). */
|
|
34
|
-
projectRoot;
|
|
35
33
|
/**
|
|
36
34
|
* Full override: query the code index DB for cross-domain findings.
|
|
37
35
|
* The base-class per-file AST loop is bypassed — all detection is
|
|
@@ -40,29 +38,24 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
40
38
|
async analyze(files, config = {}, options = {}) {
|
|
41
39
|
const startTime = Date.now();
|
|
42
40
|
const violations = [];
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
let rawDb = null;
|
|
46
|
-
try {
|
|
47
|
-
const db = CodeIndexDB.getInstance(undefined, this.projectRoot);
|
|
48
|
-
await db.initialize();
|
|
49
|
-
rawDb = db.rawDb;
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
41
|
+
const indexHandle = config.indexHandle;
|
|
42
|
+
if (!indexHandle) {
|
|
52
43
|
return {
|
|
53
44
|
violations: [],
|
|
54
45
|
errors: [{ file: '', error: 'Failed to open code index database' }],
|
|
55
|
-
|
|
46
|
+
status: makeVisitorStatus(0),
|
|
56
47
|
executionTime: Date.now() - startTime,
|
|
48
|
+
analyzerName: 'cross-domain',
|
|
57
49
|
metrics: { filesAnalyzed: 0, totalViolations: 0, executionTime: Date.now() - startTime },
|
|
58
50
|
};
|
|
59
51
|
}
|
|
60
|
-
if (!
|
|
52
|
+
if (!indexHandle) {
|
|
61
53
|
return {
|
|
62
54
|
violations: [],
|
|
63
55
|
errors: [],
|
|
64
|
-
|
|
56
|
+
status: makeVisitorStatus(files.length),
|
|
65
57
|
executionTime: Date.now() - startTime,
|
|
58
|
+
analyzerName: 'cross-domain',
|
|
66
59
|
metrics: { filesAnalyzed: files.length, totalViolations: 0, executionTime: Date.now() - startTime },
|
|
67
60
|
};
|
|
68
61
|
}
|
|
@@ -77,37 +70,37 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
77
70
|
// R1 — Schema lifecycle detectors
|
|
78
71
|
const lifecycle = config.schemaLifecycle ?? {};
|
|
79
72
|
if (lifecycle.enableWrittenNeverRead !== false) {
|
|
80
|
-
violations.push(...this.detectWrittenNeverRead(
|
|
73
|
+
violations.push(...this.detectWrittenNeverRead(indexHandle, filePathClause));
|
|
81
74
|
}
|
|
82
75
|
if (lifecycle.enableReadNeverWritten !== false) {
|
|
83
|
-
violations.push(...this.detectReadNeverWritten(
|
|
76
|
+
violations.push(...this.detectReadNeverWritten(indexHandle, filePathClause));
|
|
84
77
|
}
|
|
85
78
|
if (lifecycle.enableTransactionBoundaryRisk !== false) {
|
|
86
79
|
const txnTableMax = lifecycle.txnTableMax ?? 4;
|
|
87
|
-
violations.push(...this.detectTransactionBoundaryRisk(
|
|
80
|
+
violations.push(...this.detectTransactionBoundaryRisk(indexHandle, txnTableMax, filePathClause));
|
|
88
81
|
}
|
|
89
82
|
// R3 — Validation-bypass detection
|
|
90
83
|
const bypass = config.validatorBypass;
|
|
91
84
|
if (bypass) {
|
|
92
|
-
violations.push(...this.detectValidationBypass(
|
|
85
|
+
violations.push(...this.detectValidationBypass(indexHandle, bypass, filePathClause));
|
|
93
86
|
}
|
|
94
87
|
// R4 — Coverage by importance
|
|
95
88
|
const coverage = config.coverage;
|
|
96
89
|
if (coverage) {
|
|
97
|
-
violations.push(...this.detectUncoveredRisk(
|
|
90
|
+
violations.push(...this.detectUncoveredRisk(indexHandle, coverage, filePathClause));
|
|
98
91
|
}
|
|
99
92
|
// Count distinct files with schema_usage entries
|
|
100
|
-
const fileRows =
|
|
101
|
-
.
|
|
93
|
+
const fileRows = indexHandle
|
|
94
|
+
.query(filePathClause
|
|
102
95
|
? `SELECT COUNT(DISTINCT file_path) as cnt FROM schema_usage WHERE 1=1 ${filePathClause.clause}`
|
|
103
|
-
: 'SELECT COUNT(DISTINCT file_path) as cnt FROM schema_usage')
|
|
104
|
-
.all(...(filePathClause ? [filePathClause.param] : []));
|
|
96
|
+
: 'SELECT COUNT(DISTINCT file_path) as cnt FROM schema_usage', (filePathClause ? [filePathClause.param] : []));
|
|
105
97
|
const uniqueFiles = fileRows[0]?.cnt ?? 0;
|
|
106
98
|
return {
|
|
107
99
|
violations,
|
|
108
100
|
errors: [],
|
|
109
|
-
|
|
101
|
+
status: makeVisitorStatus(uniqueFiles || files.length),
|
|
110
102
|
executionTime: Date.now() - startTime,
|
|
103
|
+
analyzerName: 'cross-domain',
|
|
111
104
|
metrics: {
|
|
112
105
|
filesAnalyzed: uniqueFiles || files.length,
|
|
113
106
|
totalViolations: violations.length,
|
|
@@ -124,19 +117,18 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
124
117
|
* Detect tables that are written to (INSERT/UPDATE/DELETE/CREATE) but
|
|
125
118
|
* never read from (SELECT). These might be dead writes or missed read paths.
|
|
126
119
|
*/
|
|
127
|
-
detectWrittenNeverRead(
|
|
120
|
+
detectWrittenNeverRead(indexHandle, filePath) {
|
|
128
121
|
const violations = [];
|
|
129
122
|
const fpWhere = filePath ? filePath.clause : '';
|
|
130
|
-
const rows =
|
|
131
|
-
.
|
|
123
|
+
const rows = indexHandle
|
|
124
|
+
.query(`SELECT DISTINCT table_name, file_path, function_name, line, usage_type
|
|
132
125
|
FROM schema_usage
|
|
133
126
|
WHERE usage_type IN ('insert', 'update', 'delete', 'create')
|
|
134
127
|
${fpWhere}
|
|
135
128
|
AND table_name NOT IN (
|
|
136
129
|
SELECT DISTINCT table_name FROM schema_usage WHERE usage_type = 'select' ${fpWhere}
|
|
137
130
|
)
|
|
138
|
-
ORDER BY table_name, file_path
|
|
139
|
-
.all(...(filePath ? [filePath.param, filePath.param] : []));
|
|
131
|
+
ORDER BY table_name, file_path`, (filePath ? [filePath.param, filePath.param] : []));
|
|
140
132
|
// Deduplicate by table_name — one violation per table, anchored to
|
|
141
133
|
// the first writing file encountered.
|
|
142
134
|
const seen = new Set();
|
|
@@ -163,11 +155,11 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
163
155
|
* (INSERT/UPDATE/DELETE/CREATE). These may be external/managed tables
|
|
164
156
|
* or indicate missing write coverage.
|
|
165
157
|
*/
|
|
166
|
-
detectReadNeverWritten(
|
|
158
|
+
detectReadNeverWritten(indexHandle, filePath) {
|
|
167
159
|
const violations = [];
|
|
168
160
|
const fpWhere = filePath ? filePath.clause : '';
|
|
169
|
-
const rows =
|
|
170
|
-
.
|
|
161
|
+
const rows = indexHandle
|
|
162
|
+
.query(`SELECT DISTINCT table_name, file_path, function_name, line, usage_type
|
|
171
163
|
FROM schema_usage
|
|
172
164
|
WHERE usage_type = 'select'
|
|
173
165
|
${fpWhere}
|
|
@@ -175,8 +167,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
175
167
|
SELECT DISTINCT table_name FROM schema_usage
|
|
176
168
|
WHERE usage_type IN ('insert', 'update', 'delete', 'create') ${fpWhere}
|
|
177
169
|
)
|
|
178
|
-
ORDER BY table_name, file_path
|
|
179
|
-
.all(...(filePath ? [filePath.param, filePath.param] : []));
|
|
170
|
+
ORDER BY table_name, file_path`, (filePath ? [filePath.param, filePath.param] : []));
|
|
180
171
|
const seen = new Set();
|
|
181
172
|
for (const row of rows) {
|
|
182
173
|
if (seen.has(row.table_name))
|
|
@@ -204,7 +195,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
204
195
|
* writing to too many tables in a single logical operation can lead to
|
|
205
196
|
* long-running transactions, lock contention, and partial-failure complexity.
|
|
206
197
|
*/
|
|
207
|
-
detectTransactionBoundaryRisk(
|
|
198
|
+
detectTransactionBoundaryRisk(indexHandle, txnTableMax, filePath) {
|
|
208
199
|
const violations = [];
|
|
209
200
|
const fpWhere = filePath ? filePath.clause : '';
|
|
210
201
|
const fpParams = filePath ? [filePath.param] : [];
|
|
@@ -212,14 +203,13 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
212
203
|
// The functions table is only populated during deepSync (code-audit index
|
|
213
204
|
// sync), not during normal audit. We query schema_usage directly so this
|
|
214
205
|
// detector works in both modes.
|
|
215
|
-
const writerRows =
|
|
216
|
-
.
|
|
206
|
+
const writerRows = indexHandle
|
|
207
|
+
.query(`SELECT su.function_name, su.file_path, su.table_name, MIN(su.line) as line
|
|
217
208
|
FROM schema_usage su
|
|
218
209
|
WHERE su.usage_type IN ('insert', 'update', 'delete', 'create')
|
|
219
210
|
${fpWhere}
|
|
220
211
|
GROUP BY su.function_name, su.file_path, su.table_name
|
|
221
|
-
ORDER BY su.function_name, su.file_path
|
|
222
|
-
.all(...fpParams);
|
|
212
|
+
ORDER BY su.function_name, su.file_path`, fpParams);
|
|
223
213
|
if (writerRows.length === 0)
|
|
224
214
|
return violations;
|
|
225
215
|
const funcWrites = new Map();
|
|
@@ -244,7 +234,8 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
244
234
|
// call-graph context.
|
|
245
235
|
const hasGraphData = (() => {
|
|
246
236
|
try {
|
|
247
|
-
const
|
|
237
|
+
const row = indexHandle.query("SELECT COUNT(*) AS n FROM graph_cache WHERE graph_type = 'call'");
|
|
238
|
+
const cnt = row[0];
|
|
248
239
|
return (cnt?.n ?? 0) > 0;
|
|
249
240
|
}
|
|
250
241
|
catch {
|
|
@@ -256,7 +247,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
256
247
|
let fnIdLookup = null;
|
|
257
248
|
if (hasGraphData) {
|
|
258
249
|
try {
|
|
259
|
-
const fnRows =
|
|
250
|
+
const fnRows = indexHandle.query('SELECT id, name, file_path FROM functions');
|
|
260
251
|
if (fnRows.length > 0) {
|
|
261
252
|
fnIdLookup = new Map();
|
|
262
253
|
for (const r of fnRows) {
|
|
@@ -275,20 +266,17 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
275
266
|
if (fnIdLookup && hasGraphData) {
|
|
276
267
|
const funcId = fnIdLookup.get(key);
|
|
277
268
|
if (funcId !== undefined) {
|
|
278
|
-
const calleeRows =
|
|
279
|
-
.
|
|
280
|
-
WHERE graph_type = 'call' AND node_key =
|
|
281
|
-
.all(String(funcId));
|
|
269
|
+
const calleeRows = indexHandle
|
|
270
|
+
.query(`SELECT neighbor_key FROM graph_cache
|
|
271
|
+
WHERE graph_type = 'call' AND node_key = ?`, [String(funcId)]);
|
|
282
272
|
for (const callee of calleeRows) {
|
|
283
|
-
const calleeFuncs =
|
|
284
|
-
.
|
|
285
|
-
.all(parseInt(callee.neighbor_key, 10));
|
|
273
|
+
const calleeFuncs = indexHandle
|
|
274
|
+
.query(`SELECT name, file_path FROM functions WHERE id = ?`, [parseInt(callee.neighbor_key, 10)]);
|
|
286
275
|
for (const cf of calleeFuncs) {
|
|
287
|
-
const calleeTables =
|
|
288
|
-
.
|
|
276
|
+
const calleeTables = indexHandle
|
|
277
|
+
.query(`SELECT DISTINCT table_name FROM schema_usage
|
|
289
278
|
WHERE usage_type IN ('insert', 'update', 'delete', 'create')
|
|
290
|
-
AND function_name = ? AND file_path =
|
|
291
|
-
.all(cf.name, cf.file_path);
|
|
279
|
+
AND function_name = ? AND file_path = ?`, [cf.name, cf.file_path]);
|
|
292
280
|
for (const ct of calleeTables) {
|
|
293
281
|
allTables.add(ct.table_name);
|
|
294
282
|
}
|
|
@@ -323,7 +311,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
323
311
|
* 3. Heuristic fallback: name GLOB 'validate*' OR 'assert*' (when both
|
|
324
312
|
* above are silent)
|
|
325
313
|
*/
|
|
326
|
-
detectValidationBypass(
|
|
314
|
+
detectValidationBypass(indexHandle, config, filePath) {
|
|
327
315
|
const violations = [];
|
|
328
316
|
const { validators: userValidators = [], modeShare = 0.8, minCorpus = 20, depth = 3, } = config;
|
|
329
317
|
// ── 1. Build the validator function ID set ─────────────────────────────
|
|
@@ -334,16 +322,14 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
334
322
|
if (hashIdx >= 0) {
|
|
335
323
|
const vPath = v.substring(0, hashIdx);
|
|
336
324
|
const vName = v.substring(hashIdx + 1);
|
|
337
|
-
const rows =
|
|
338
|
-
.
|
|
339
|
-
.all(vName, vPath);
|
|
325
|
+
const rows = indexHandle
|
|
326
|
+
.query('SELECT id FROM functions WHERE name = ? AND file_path = ?', [vName, vPath]);
|
|
340
327
|
for (const r of rows)
|
|
341
328
|
validatorIds.add(r.id);
|
|
342
329
|
}
|
|
343
330
|
else {
|
|
344
|
-
const rows =
|
|
345
|
-
.
|
|
346
|
-
.all(v);
|
|
331
|
+
const rows = indexHandle
|
|
332
|
+
.query('SELECT id FROM functions WHERE name = ?', [v]);
|
|
347
333
|
for (const r of rows)
|
|
348
334
|
validatorIds.add(r.id);
|
|
349
335
|
}
|
|
@@ -355,23 +341,21 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
355
341
|
if (validatorIds.size === 0) {
|
|
356
342
|
const likeClauses = [...VALIDATOR_PACKAGES].map(() => 'used_imports LIKE ?');
|
|
357
343
|
const likeParams = [...VALIDATOR_PACKAGES].map((pkg) => `%"${pkg}"%`);
|
|
358
|
-
const validatorFuncs =
|
|
359
|
-
.
|
|
344
|
+
const validatorFuncs = indexHandle
|
|
345
|
+
.query(`SELECT id FROM functions
|
|
360
346
|
WHERE used_imports IS NOT NULL
|
|
361
347
|
AND (${likeClauses.join(' OR ')})
|
|
362
|
-
AND is_exported = 1
|
|
363
|
-
.all(...likeParams);
|
|
348
|
+
AND is_exported = 1`, likeParams);
|
|
364
349
|
for (const f of validatorFuncs)
|
|
365
350
|
validatorIds.add(f.id);
|
|
366
351
|
}
|
|
367
352
|
// 1c. Heuristic fallback: name-based matching (only when provenance
|
|
368
353
|
// found nothing AND no user-configured validators exist).
|
|
369
354
|
if (validatorIds.size === 0 && userValidators.length === 0) {
|
|
370
|
-
const heuristicFuncs =
|
|
371
|
-
.
|
|
355
|
+
const heuristicFuncs = indexHandle
|
|
356
|
+
.query(`SELECT id FROM functions
|
|
372
357
|
WHERE (name GLOB 'validate*' OR name GLOB 'assert*')
|
|
373
|
-
AND is_exported = 1`)
|
|
374
|
-
.all();
|
|
358
|
+
AND is_exported = 1`);
|
|
375
359
|
for (const f of heuristicFuncs)
|
|
376
360
|
validatorIds.add(f.id);
|
|
377
361
|
}
|
|
@@ -381,15 +365,14 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
381
365
|
const fpAliasWhere = filePath
|
|
382
366
|
? `AND su.${filePath.clause.slice(4)}`
|
|
383
367
|
: '';
|
|
384
|
-
const writers =
|
|
385
|
-
.
|
|
368
|
+
const writers = indexHandle
|
|
369
|
+
.query(`SELECT DISTINCT su.function_name, su.file_path, su.line, f.id as function_id
|
|
386
370
|
FROM schema_usage su
|
|
387
371
|
JOIN functions f ON f.name = su.function_name
|
|
388
372
|
AND f.file_path = su.file_path
|
|
389
373
|
WHERE su.usage_type IN ('insert', 'update', 'delete', 'create')
|
|
390
374
|
${fpAliasWhere}
|
|
391
|
-
ORDER BY su.file_path, su.function_name
|
|
392
|
-
.all(...(filePath ? [filePath.param] : []));
|
|
375
|
+
ORDER BY su.file_path, su.function_name`, (filePath ? [filePath.param] : []));
|
|
393
376
|
if (writers.length === 0)
|
|
394
377
|
return violations;
|
|
395
378
|
const writerCoverage = new Map();
|
|
@@ -397,7 +380,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
397
380
|
const key = `${w.file_path}::${w.function_name}`;
|
|
398
381
|
if (writerCoverage.has(key))
|
|
399
382
|
continue; // deduplicate
|
|
400
|
-
const covered = this.bfsReachesValidator(
|
|
383
|
+
const covered = this.bfsReachesValidator(indexHandle, w.function_id, validatorIds, depth);
|
|
401
384
|
writerCoverage.set(key, {
|
|
402
385
|
covered,
|
|
403
386
|
line: w.line,
|
|
@@ -464,27 +447,24 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
464
447
|
* to static reach analysis from test files (BFS from test-file functions
|
|
465
448
|
* into the call graph).
|
|
466
449
|
*/
|
|
467
|
-
detectUncoveredRisk(
|
|
450
|
+
detectUncoveredRisk(indexHandle, coverage, filePath) {
|
|
468
451
|
const violations = [];
|
|
469
|
-
const db = CodeIndexDB.getInstance(undefined, this.projectRoot);
|
|
470
452
|
const topRiskDecile = coverage.topRiskDecile ?? 0.1;
|
|
471
453
|
// Check if any measured coverage exists
|
|
472
|
-
const measuredCount =
|
|
473
|
-
.
|
|
474
|
-
.get().cnt;
|
|
454
|
+
const measuredCount = indexHandle
|
|
455
|
+
.query("SELECT COUNT(*) AS cnt FROM coverage_data WHERE basis = 'measured'")[0].cnt;
|
|
475
456
|
if (measuredCount > 0) {
|
|
476
457
|
// Use measured coverage data from lcov/istanbul imports
|
|
477
|
-
const untested =
|
|
458
|
+
const untested = indexHandle.getUntestedTopDecile(topRiskDecile);
|
|
478
459
|
// Determine source format from existing coverage entries
|
|
479
|
-
const sourceRow =
|
|
480
|
-
.
|
|
481
|
-
.get();
|
|
460
|
+
const sourceRow = indexHandle
|
|
461
|
+
.query("SELECT source, imported_at FROM coverage_data WHERE basis = 'measured' LIMIT 1")[0];
|
|
482
462
|
const sourceFormat = sourceRow?.source ?? 'unknown';
|
|
483
463
|
const importedAt = sourceRow?.imported_at ?? null;
|
|
484
464
|
// Stale-import detection: measured coverage predates last full index sync
|
|
485
465
|
let staleWarning = null;
|
|
486
466
|
if (importedAt) {
|
|
487
|
-
const lastSync =
|
|
467
|
+
const lastSync = indexHandle.getMeta?.('last_full_sync_timestamp') ?? null;
|
|
488
468
|
if (lastSync && importedAt < lastSync) {
|
|
489
469
|
staleWarning =
|
|
490
470
|
` — WARNING: this coverage data may be stale (imported ${importedAt}, ` +
|
|
@@ -517,8 +497,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
517
497
|
const params = [topRiskDecile];
|
|
518
498
|
if (filePath)
|
|
519
499
|
params.push(filePath.param);
|
|
520
|
-
const highRiskFns =
|
|
521
|
-
.prepare(`WITH ranked AS (
|
|
500
|
+
const highRiskFns = indexHandle.query(`WITH ranked AS (
|
|
522
501
|
SELECT
|
|
523
502
|
f.name,
|
|
524
503
|
f.file_path,
|
|
@@ -535,8 +514,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
535
514
|
SELECT id, name, file_path, line_number, risk_score
|
|
536
515
|
FROM ranked
|
|
537
516
|
WHERE pct <= ?
|
|
538
|
-
ORDER BY risk_score DESC
|
|
539
|
-
.all(...params);
|
|
517
|
+
ORDER BY risk_score DESC`, params);
|
|
540
518
|
if (highRiskFns.length === 0)
|
|
541
519
|
return violations;
|
|
542
520
|
// Find test-file functions to use as BFS starting points.
|
|
@@ -553,9 +531,8 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
553
531
|
}
|
|
554
532
|
const testFuncIds = new Set();
|
|
555
533
|
if (testFileClause) {
|
|
556
|
-
const testFunctions =
|
|
557
|
-
.
|
|
558
|
-
.all(...testFileParams);
|
|
534
|
+
const testFunctions = indexHandle
|
|
535
|
+
.query(`SELECT id FROM functions WHERE ${testFileClause}`, testFileParams);
|
|
559
536
|
for (const tf of testFunctions)
|
|
560
537
|
testFuncIds.add(tf.id);
|
|
561
538
|
}
|
|
@@ -575,10 +552,9 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
575
552
|
continue;
|
|
576
553
|
visited.add(funcId);
|
|
577
554
|
reachableIds.add(funcId);
|
|
578
|
-
const callees =
|
|
579
|
-
.
|
|
580
|
-
WHERE graph_type = 'call' AND node_key =
|
|
581
|
-
.all(String(funcId));
|
|
555
|
+
const callees = indexHandle
|
|
556
|
+
.query(`SELECT neighbor_key FROM graph_cache
|
|
557
|
+
WHERE graph_type = 'call' AND node_key = ?`, [String(funcId)]);
|
|
582
558
|
for (const callee of callees) {
|
|
583
559
|
const calleeId = parseInt(callee.neighbor_key, 10);
|
|
584
560
|
if (!isNaN(calleeId) && !visited.has(calleeId)) {
|
|
@@ -619,7 +595,7 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
619
595
|
* BFS through the call graph (graph_cache) up to maxDepth to check if
|
|
620
596
|
* any path from startFuncId reaches a validator function ID.
|
|
621
597
|
*/
|
|
622
|
-
bfsReachesValidator(
|
|
598
|
+
bfsReachesValidator(indexHandle, startFuncId, validatorIds, maxDepth) {
|
|
623
599
|
const visited = new Set();
|
|
624
600
|
let currentLevel = [startFuncId];
|
|
625
601
|
for (let d = 0; d < maxDepth; d++) {
|
|
@@ -631,10 +607,9 @@ export class CrossDomainAnalyzer extends UniversalAnalyzer {
|
|
|
631
607
|
continue;
|
|
632
608
|
visited.add(funcId);
|
|
633
609
|
// Get callees from graph_cache call edges
|
|
634
|
-
const callees =
|
|
635
|
-
.
|
|
636
|
-
WHERE graph_type = 'call' AND node_key =
|
|
637
|
-
.all(String(funcId));
|
|
610
|
+
const callees = indexHandle
|
|
611
|
+
.query(`SELECT neighbor_key FROM graph_cache
|
|
612
|
+
WHERE graph_type = 'call' AND node_key = ?`, [String(funcId)]);
|
|
638
613
|
for (const callee of callees) {
|
|
639
614
|
const calleeId = parseInt(callee.neighbor_key, 10);
|
|
640
615
|
if (!isNaN(calleeId) && !visited.has(calleeId)) {
|