@kernlang/review 3.3.8 → 3.3.9
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/dist/cache.js +1 -1
- package/dist/concept-rules/auth-propagation-drift.d.ts +10 -0
- package/dist/concept-rules/auth-propagation-drift.js +83 -0
- package/dist/concept-rules/auth-propagation-drift.js.map +1 -0
- package/dist/concept-rules/body-shape-drift.d.ts +32 -0
- package/dist/concept-rules/body-shape-drift.js +96 -0
- package/dist/concept-rules/body-shape-drift.js.map +1 -0
- package/dist/concept-rules/cross-stack-utils.d.ts +24 -0
- package/dist/concept-rules/cross-stack-utils.js +123 -29
- package/dist/concept-rules/cross-stack-utils.js.map +1 -1
- package/dist/concept-rules/index.d.ts +4 -2
- package/dist/concept-rules/index.js +53 -3
- package/dist/concept-rules/index.js.map +1 -1
- package/dist/concept-rules/mutation-without-idempotency.d.ts +10 -0
- package/dist/concept-rules/mutation-without-idempotency.js +47 -0
- package/dist/concept-rules/mutation-without-idempotency.js.map +1 -0
- package/dist/concept-rules/request-validation-drift.d.ts +11 -0
- package/dist/concept-rules/request-validation-drift.js +96 -0
- package/dist/concept-rules/request-validation-drift.js.map +1 -0
- package/dist/concept-rules/unbounded-collection-query.d.ts +10 -0
- package/dist/concept-rules/unbounded-collection-query.js +56 -0
- package/dist/concept-rules/unbounded-collection-query.js.map +1 -0
- package/dist/concept-rules/unhandled-api-error-shape.d.ts +10 -0
- package/dist/concept-rules/unhandled-api-error-shape.js +57 -0
- package/dist/concept-rules/unhandled-api-error-shape.js.map +1 -0
- package/dist/external-tools.js +52 -3
- package/dist/external-tools.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +136 -62
- package/dist/index.js.map +1 -1
- package/dist/mappers/ts-concepts.js +663 -1
- package/dist/mappers/ts-concepts.js.map +1 -1
- package/dist/python-fallback.d.ts +2 -0
- package/dist/python-fallback.js +506 -0
- package/dist/python-fallback.js.map +1 -0
- package/dist/reporter.js +84 -1
- package/dist/reporter.js.map +1 -1
- package/dist/rules/base.js +21 -3
- package/dist/rules/base.js.map +1 -1
- package/dist/rules/index.js +40 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/kern-source.js +1 -0
- package/dist/rules/kern-source.js.map +1 -1
- package/dist/types.d.ts +7 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/cache.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync
|
|
|
3
3
|
import { homedir } from 'os';
|
|
4
4
|
import { dirname, join, resolve } from 'path';
|
|
5
5
|
// Version stamp for cache invalidation — changes when rules/analyzers change
|
|
6
|
-
const REVIEW_CACHE_VERSION = '3.
|
|
6
|
+
const REVIEW_CACHE_VERSION = '3.3.9-review-cache-1';
|
|
7
7
|
const IMPORT_SPECIFIER_RE = /(?:import|export)\s+(?:[^'"`]*?\s+from\s+)?['"]([^'"]+)['"]|import\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
8
8
|
const EXTENSION_FALLBACK = {
|
|
9
9
|
'.js': ['.ts', '.tsx', '.mts', '.cts'],
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: auth-propagation-drift
|
|
3
|
+
*
|
|
4
|
+
* Cross-stack rule — complements `auth-drift` by catching non-raw-fetch API
|
|
5
|
+
* clients (axios/got/ky-style direct calls) that call an authenticated server
|
|
6
|
+
* route without visible auth/session propagation.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReviewFinding } from '../types.js';
|
|
9
|
+
import type { ConceptRuleContext } from './index.js';
|
|
10
|
+
export declare function authPropagationDrift(ctx: ConceptRuleContext): ReviewFinding[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: auth-propagation-drift
|
|
3
|
+
*
|
|
4
|
+
* Cross-stack rule — complements `auth-drift` by catching non-raw-fetch API
|
|
5
|
+
* clients (axios/got/ky-style direct calls) that call an authenticated server
|
|
6
|
+
* route without visible auth/session propagation.
|
|
7
|
+
*/
|
|
8
|
+
import { createFingerprint } from '../types.js';
|
|
9
|
+
import { CROSS_STACK_EXACT_CONFIDENCE, collectRoutesAcrossGraph, findHighConfidenceRouteForMethod, findMatchingRouteForMethod, normalizeClientUrl, } from './cross-stack-utils.js';
|
|
10
|
+
export function authPropagationDrift(ctx) {
|
|
11
|
+
if (!ctx.allConcepts || ctx.allConcepts.size === 0)
|
|
12
|
+
return [];
|
|
13
|
+
const serverRoutes = collectRoutesAcrossGraph(ctx.allConcepts);
|
|
14
|
+
if (serverRoutes.length === 0)
|
|
15
|
+
return [];
|
|
16
|
+
const authGuardContainers = collectAuthGuardContainers(ctx.allConcepts);
|
|
17
|
+
if (authGuardContainers.size === 0)
|
|
18
|
+
return [];
|
|
19
|
+
const findings = [];
|
|
20
|
+
const localConcepts = ctx.allConcepts.get(ctx.filePath) ?? ctx.concepts;
|
|
21
|
+
for (const node of localConcepts.nodes) {
|
|
22
|
+
if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
|
|
23
|
+
continue;
|
|
24
|
+
if (node.payload.authPropagation !== 'absent')
|
|
25
|
+
continue;
|
|
26
|
+
// `auth-drift` owns the raw fetch/no Authorization case for backward
|
|
27
|
+
// compatibility with the existing rule ID. This rule covers richer clients.
|
|
28
|
+
if (node.payload.hasAuthHeader === false)
|
|
29
|
+
continue;
|
|
30
|
+
const target = node.payload.target;
|
|
31
|
+
if (typeof target !== 'string')
|
|
32
|
+
continue;
|
|
33
|
+
const normalized = normalizeClientUrl(target);
|
|
34
|
+
if (!normalized)
|
|
35
|
+
continue;
|
|
36
|
+
const route = ctx.crossStackMode === 'audit'
|
|
37
|
+
? findMatchingRouteForMethod(normalized, node.payload.method, serverRoutes)
|
|
38
|
+
: findHighConfidenceRouteForMethod(normalized, node.payload.method, serverRoutes);
|
|
39
|
+
if (!route?.node)
|
|
40
|
+
continue;
|
|
41
|
+
const fileGuards = authGuardContainers.get(route.node.primarySpan.file);
|
|
42
|
+
if (!fileGuards)
|
|
43
|
+
continue;
|
|
44
|
+
const routeContainer = route.node.containerId;
|
|
45
|
+
const guardedByContainer = routeContainer !== undefined && fileGuards.routeContainers.has(routeContainer);
|
|
46
|
+
const guardedBySingleRouteFile = fileGuards.totalRoutesInFile === 1;
|
|
47
|
+
if (!guardedByContainer && !guardedBySingleRouteFile)
|
|
48
|
+
continue;
|
|
49
|
+
findings.push({
|
|
50
|
+
source: 'kern',
|
|
51
|
+
ruleId: 'auth-propagation-drift',
|
|
52
|
+
severity: 'warning',
|
|
53
|
+
category: 'bug',
|
|
54
|
+
message: `Client calls authenticated route \`${target}\` without visible auth/session propagation. Add an Authorization/Cookie/session credential path or route this call through an authenticated client wrapper.`,
|
|
55
|
+
primarySpan: node.primarySpan,
|
|
56
|
+
relatedSpans: [route.node.primarySpan],
|
|
57
|
+
fingerprint: createFingerprint('auth-propagation-drift', node.primarySpan.startLine, node.primarySpan.startCol),
|
|
58
|
+
confidence: node.confidence * CROSS_STACK_EXACT_CONFIDENCE,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return findings;
|
|
62
|
+
}
|
|
63
|
+
function collectAuthGuardContainers(allConcepts) {
|
|
64
|
+
const result = new Map();
|
|
65
|
+
for (const [filePath, map] of allConcepts) {
|
|
66
|
+
const routeContainers = new Set();
|
|
67
|
+
let routeCount = 0;
|
|
68
|
+
for (const node of map.nodes) {
|
|
69
|
+
if (node.kind === 'entrypoint' && node.payload.kind === 'entrypoint' && node.payload.subtype === 'route') {
|
|
70
|
+
routeCount++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (node.kind !== 'guard' || node.payload.kind !== 'guard' || node.payload.subtype !== 'auth')
|
|
74
|
+
continue;
|
|
75
|
+
if (node.containerId !== undefined)
|
|
76
|
+
routeContainers.add(node.containerId);
|
|
77
|
+
}
|
|
78
|
+
if (routeContainers.size > 0)
|
|
79
|
+
result.set(filePath, { routeContainers, totalRoutesInFile: routeCount });
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=auth-propagation-drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-propagation-drift.js","sourceRoot":"","sources":["../../src/concept-rules/auth-propagation-drift.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,gCAAgC,EAChC,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,UAAU,oBAAoB,CAAC,GAAuB;IAC1D,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9D,MAAM,YAAY,GAAG,wBAAwB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/D,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,mBAAmB,GAAG,0BAA0B,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACxE,IAAI,mBAAmB,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9C,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC;IAExE,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS;YAAE,SAAS;QAC7G,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,KAAK,QAAQ;YAAE,SAAS;QACxD,qEAAqE;QACrE,4EAA4E;QAC5E,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,KAAK,KAAK;YAAE,SAAS;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACnC,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,SAAS;QACzC,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,UAAU;YAAE,SAAS;QAE1B,MAAM,KAAK,GACT,GAAG,CAAC,cAAc,KAAK,OAAO;YAC5B,CAAC,CAAC,0BAA0B,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC;YAC3E,CAAC,CAAC,gCAAgC,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACtF,IAAI,CAAC,KAAK,EAAE,IAAI;YAAE,SAAS;QAC3B,MAAM,UAAU,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxE,IAAI,CAAC,UAAU;YAAE,SAAS;QAE1B,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC;QAC9C,MAAM,kBAAkB,GAAG,cAAc,KAAK,SAAS,IAAI,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC1G,MAAM,wBAAwB,GAAG,UAAU,CAAC,iBAAiB,KAAK,CAAC,CAAC;QACpE,IAAI,CAAC,kBAAkB,IAAI,CAAC,wBAAwB;YAAE,SAAS;QAE/D,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,wBAAwB;YAChC,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,sCAAsC,MAAM,8JAA8J;YACnN,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC;YACtC,WAAW,EAAE,iBAAiB,CAAC,wBAAwB,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YAC/G,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,4BAA4B;SAC3D,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAOD,SAAS,0BAA0B,CAAC,WAA4C;IAC9E,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC/C,KAAK,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,WAAW,EAAE,CAAC;QAC1C,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;QAC1C,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;gBACzG,UAAU,EAAE,CAAC;gBACb,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,MAAM;gBAAE,SAAS;YACxG,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;gBAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,eAAe,CAAC,IAAI,GAAG,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,eAAe,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: body-shape-drift
|
|
3
|
+
*
|
|
4
|
+
* Cross-stack rule — fires when a client `fetch(url, { body: JSON.stringify({ … }) })`
|
|
5
|
+
* omits one or more fields that the matching server handler reads off
|
|
6
|
+
* `req.body` (Express inline handler). Classic LLM-authored drift:
|
|
7
|
+
*
|
|
8
|
+
* client: fetch('/api/users', { body: JSON.stringify({ name }) })
|
|
9
|
+
* server: app.post('/api/users', (req, res) => {
|
|
10
|
+
* const { name, email } = req.body; // ← email never sent
|
|
11
|
+
* ...
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* V1 is deliberately narrow per the red-team consensus on the body-shape
|
|
15
|
+
* plan:
|
|
16
|
+
* - Express only (no Pydantic/FastAPI cross-file model resolution).
|
|
17
|
+
* - Raw `fetch` only on the client (no axios/ky/got/wrapper clients).
|
|
18
|
+
* - Inline handlers only (imported-identifier handlers stay silent).
|
|
19
|
+
* - Missing-fields direction only (client omits what server needs). The
|
|
20
|
+
* "extra" direction (client sends what server ignores) conflicts with
|
|
21
|
+
* legitimate pass-through handlers and is deferred.
|
|
22
|
+
* - Fires only when BOTH `sentFieldsResolved` and `bodyFieldsResolved`
|
|
23
|
+
* are true. Opaque bodies, wrapper clients, whole-body forwarding all
|
|
24
|
+
* stay silent by design — false negatives are the price of zero
|
|
25
|
+
* false positives.
|
|
26
|
+
*
|
|
27
|
+
* Requires graph mode: the client call and server route live in different
|
|
28
|
+
* files by definition. Single-file review returns no findings.
|
|
29
|
+
*/
|
|
30
|
+
import type { ReviewFinding } from '../types.js';
|
|
31
|
+
import type { ConceptRuleContext } from './index.js';
|
|
32
|
+
export declare function bodyShapeDrift(ctx: ConceptRuleContext): ReviewFinding[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: body-shape-drift
|
|
3
|
+
*
|
|
4
|
+
* Cross-stack rule — fires when a client `fetch(url, { body: JSON.stringify({ … }) })`
|
|
5
|
+
* omits one or more fields that the matching server handler reads off
|
|
6
|
+
* `req.body` (Express inline handler). Classic LLM-authored drift:
|
|
7
|
+
*
|
|
8
|
+
* client: fetch('/api/users', { body: JSON.stringify({ name }) })
|
|
9
|
+
* server: app.post('/api/users', (req, res) => {
|
|
10
|
+
* const { name, email } = req.body; // ← email never sent
|
|
11
|
+
* ...
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* V1 is deliberately narrow per the red-team consensus on the body-shape
|
|
15
|
+
* plan:
|
|
16
|
+
* - Express only (no Pydantic/FastAPI cross-file model resolution).
|
|
17
|
+
* - Raw `fetch` only on the client (no axios/ky/got/wrapper clients).
|
|
18
|
+
* - Inline handlers only (imported-identifier handlers stay silent).
|
|
19
|
+
* - Missing-fields direction only (client omits what server needs). The
|
|
20
|
+
* "extra" direction (client sends what server ignores) conflicts with
|
|
21
|
+
* legitimate pass-through handlers and is deferred.
|
|
22
|
+
* - Fires only when BOTH `sentFieldsResolved` and `bodyFieldsResolved`
|
|
23
|
+
* are true. Opaque bodies, wrapper clients, whole-body forwarding all
|
|
24
|
+
* stay silent by design — false negatives are the price of zero
|
|
25
|
+
* false positives.
|
|
26
|
+
*
|
|
27
|
+
* Requires graph mode: the client call and server route live in different
|
|
28
|
+
* files by definition. Single-file review returns no findings.
|
|
29
|
+
*/
|
|
30
|
+
import { createFingerprint } from '../types.js';
|
|
31
|
+
import { API_PATH_RE, CROSS_STACK_HEURISTIC_CONFIDENCE, collectRoutesAcrossGraph, findMatchingRoute, normalizeClientUrl, } from './cross-stack-utils.js';
|
|
32
|
+
export function bodyShapeDrift(ctx) {
|
|
33
|
+
if (!ctx.allConcepts || ctx.allConcepts.size === 0)
|
|
34
|
+
return [];
|
|
35
|
+
const serverRoutes = collectRoutesAcrossGraph(ctx.allConcepts);
|
|
36
|
+
if (serverRoutes.length === 0)
|
|
37
|
+
return [];
|
|
38
|
+
const clientCalls = [];
|
|
39
|
+
for (const [, conceptMap] of ctx.allConcepts) {
|
|
40
|
+
for (const node of conceptMap.nodes) {
|
|
41
|
+
if (node.kind !== 'effect' || node.payload.kind !== 'effect' || node.payload.subtype !== 'network')
|
|
42
|
+
continue;
|
|
43
|
+
if (node.payload.sentFieldsResolved !== true)
|
|
44
|
+
continue;
|
|
45
|
+
const fields = node.payload.sentFields;
|
|
46
|
+
if (!fields)
|
|
47
|
+
continue;
|
|
48
|
+
const target = node.payload.target;
|
|
49
|
+
if (typeof target !== 'string')
|
|
50
|
+
continue;
|
|
51
|
+
const normalized = normalizeClientUrl(target);
|
|
52
|
+
if (!normalized || !API_PATH_RE.test(normalized))
|
|
53
|
+
continue;
|
|
54
|
+
clientCalls.push({ target, normalizedPath: normalized, sentFields: fields, node });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (clientCalls.length === 0)
|
|
58
|
+
return [];
|
|
59
|
+
const findings = [];
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (const call of clientCalls) {
|
|
62
|
+
if (call.node.primarySpan.file !== ctx.filePath)
|
|
63
|
+
continue;
|
|
64
|
+
const route = findMatchingRoute(call.normalizedPath, serverRoutes);
|
|
65
|
+
if (!route || !route.node)
|
|
66
|
+
continue;
|
|
67
|
+
if (route.node.payload.kind !== 'entrypoint')
|
|
68
|
+
continue;
|
|
69
|
+
if (route.node.payload.bodyFieldsResolved !== true)
|
|
70
|
+
continue;
|
|
71
|
+
const serverFields = route.node.payload.bodyFields;
|
|
72
|
+
if (!serverFields || serverFields.length === 0)
|
|
73
|
+
continue;
|
|
74
|
+
const missing = serverFields.filter((f) => !call.sentFields.includes(f));
|
|
75
|
+
if (missing.length === 0)
|
|
76
|
+
continue;
|
|
77
|
+
const fingerprint = createFingerprint('body-shape-drift', call.node.primarySpan.startLine, call.node.primarySpan.startCol);
|
|
78
|
+
if (seen.has(fingerprint))
|
|
79
|
+
continue;
|
|
80
|
+
seen.add(fingerprint);
|
|
81
|
+
const missingList = missing.map((f) => `\`${f}\``).join(', ');
|
|
82
|
+
const plural = missing.length === 1 ? 'field' : 'fields';
|
|
83
|
+
findings.push({
|
|
84
|
+
source: 'kern',
|
|
85
|
+
ruleId: 'body-shape-drift',
|
|
86
|
+
severity: 'warning',
|
|
87
|
+
category: 'bug',
|
|
88
|
+
message: `Frontend POSTs to \`${call.normalizedPath}\` without ${plural} ${missingList}, but the matching server handler reads ${missing.length === 1 ? 'it' : 'them'} off \`req.body\`. The handler will see \`undefined\` for the missing ${plural}.`,
|
|
89
|
+
primarySpan: call.node.primarySpan,
|
|
90
|
+
fingerprint,
|
|
91
|
+
confidence: call.node.confidence * CROSS_STACK_HEURISTIC_CONFIDENCE,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return findings;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=body-shape-drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-shape-drift.js","sourceRoot":"","sources":["../../src/concept-rules/body-shape-drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -84,6 +84,17 @@ export declare function normalizeClientUrl(raw: string): string | undefined;
|
|
|
84
84
|
* behaviour).
|
|
85
85
|
*/
|
|
86
86
|
export declare function findMatchingRoute(clientPath: string, routes: readonly ServerRoute[]): ServerRoute | undefined;
|
|
87
|
+
export declare function findMatchingRouteForMethod(clientPath: string, clientMethod: string | undefined, routes: readonly ServerRoute[]): ServerRoute | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Noise-gated route match for newer cross-stack rules.
|
|
90
|
+
*
|
|
91
|
+
* The older matcher intentionally returns the first path-shaped match so
|
|
92
|
+
* legacy rules retain broad coverage. Newer rules that can feel speculative
|
|
93
|
+
* should use this helper instead: it requires a known client method, exactly
|
|
94
|
+
* one matching server route for that method, a concrete internal API path, and
|
|
95
|
+
* no catch-all/wildcard route shapes.
|
|
96
|
+
*/
|
|
97
|
+
export declare function findHighConfidenceRouteForMethod(clientPath: string, clientMethod: string | undefined, routes: readonly ServerRoute[]): ServerRoute | undefined;
|
|
87
98
|
/** Boolean-returning thin wrapper preserved for callers that just need a yes/no. */
|
|
88
99
|
export declare function hasMatchingRoute(clientPath: string, routes: readonly ServerRoute[]): boolean;
|
|
89
100
|
/**
|
|
@@ -94,3 +105,16 @@ export declare function hasMatchingRoute(clientPath: string, routes: readonly Se
|
|
|
94
105
|
* no one calls it" (method-drift / orphan-route territory).
|
|
95
106
|
*/
|
|
96
107
|
export declare function findRoutesAtPath(clientPath: string, routes: readonly ServerRoute[]): ServerRoute[];
|
|
108
|
+
export declare function routeMethodMatches(routeMethod: string | undefined, clientMethod: string): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Resolve an inline Express handler's concept from a route node. Only
|
|
111
|
+
* meaningful for `route` entrypoints whose mapper set `handlerConceptId`
|
|
112
|
+
* (inline arrow/function handlers — not imported identifiers). Returns
|
|
113
|
+
* undefined when the route has no inline handler or the expected concept
|
|
114
|
+
* is absent from the map (e.g., stripped during serialisation).
|
|
115
|
+
*
|
|
116
|
+
* Rules that reason about handler body contents — body-shape drift, auth
|
|
117
|
+
* checks, response envelope detection — use this as the single lookup
|
|
118
|
+
* point so callers don't re-implement span-or-id matching in each rule.
|
|
119
|
+
*/
|
|
120
|
+
export declare function findHandlerConcept(map: ConceptMap, route: ConceptNode): ConceptNode | undefined;
|
|
@@ -197,25 +197,52 @@ export function normalizeClientUrl(raw) {
|
|
|
197
197
|
export function findMatchingRoute(clientPath, routes) {
|
|
198
198
|
const clientSegments = trimTrailing(clientPath).split('/');
|
|
199
199
|
for (const route of routes) {
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
if (routePathMatchesSegments(route.path, clientSegments))
|
|
201
|
+
return route;
|
|
202
|
+
}
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
export function findMatchingRouteForMethod(clientPath, clientMethod, routes) {
|
|
206
|
+
const clientSegments = trimTrailing(clientPath).split('/');
|
|
207
|
+
for (const route of routes) {
|
|
208
|
+
if (!routePathMatchesSegments(route.path, clientSegments))
|
|
202
209
|
continue;
|
|
203
|
-
|
|
204
|
-
for (let i = 0; i < routeSegments.length; i++) {
|
|
205
|
-
const rs = routeSegments[i];
|
|
206
|
-
const cs = clientSegments[i];
|
|
207
|
-
if (isParamSegment(rs))
|
|
208
|
-
continue;
|
|
209
|
-
if (rs !== cs) {
|
|
210
|
-
matched = false;
|
|
211
|
-
break;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (matched)
|
|
210
|
+
if (!clientMethod || routeMethodMatches(route.method, clientMethod))
|
|
215
211
|
return route;
|
|
216
212
|
}
|
|
217
213
|
return undefined;
|
|
218
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Noise-gated route match for newer cross-stack rules.
|
|
217
|
+
*
|
|
218
|
+
* The older matcher intentionally returns the first path-shaped match so
|
|
219
|
+
* legacy rules retain broad coverage. Newer rules that can feel speculative
|
|
220
|
+
* should use this helper instead: it requires a known client method, exactly
|
|
221
|
+
* one matching server route for that method, a concrete internal API path, and
|
|
222
|
+
* no catch-all/wildcard route shapes.
|
|
223
|
+
*/
|
|
224
|
+
export function findHighConfidenceRouteForMethod(clientPath, clientMethod, routes) {
|
|
225
|
+
if (!isHighConfidenceClientPath(clientPath))
|
|
226
|
+
return undefined;
|
|
227
|
+
if (!clientMethod)
|
|
228
|
+
return undefined;
|
|
229
|
+
const matches = findRoutesAtPath(clientPath, routes).filter((route) => {
|
|
230
|
+
if (!route.node)
|
|
231
|
+
return false;
|
|
232
|
+
if (route.node.confidence < 0.75)
|
|
233
|
+
return false;
|
|
234
|
+
if (!route.method)
|
|
235
|
+
return false;
|
|
236
|
+
if (WILDCARD_METHODS.has(route.method.toUpperCase()))
|
|
237
|
+
return false;
|
|
238
|
+
if (!routeMethodMatches(route.method, clientMethod))
|
|
239
|
+
return false;
|
|
240
|
+
if (!isHighConfidenceServerPath(route.path))
|
|
241
|
+
return false;
|
|
242
|
+
return true;
|
|
243
|
+
});
|
|
244
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
245
|
+
}
|
|
219
246
|
/** Boolean-returning thin wrapper preserved for callers that just need a yes/no. */
|
|
220
247
|
export function hasMatchingRoute(clientPath, routes) {
|
|
221
248
|
return findMatchingRoute(clientPath, routes) !== undefined;
|
|
@@ -231,21 +258,7 @@ export function findRoutesAtPath(clientPath, routes) {
|
|
|
231
258
|
const clientSegments = trimTrailing(clientPath).split('/');
|
|
232
259
|
const matches = [];
|
|
233
260
|
for (const route of routes) {
|
|
234
|
-
|
|
235
|
-
if (routeSegments.length !== clientSegments.length)
|
|
236
|
-
continue;
|
|
237
|
-
let matched = true;
|
|
238
|
-
for (let i = 0; i < routeSegments.length; i++) {
|
|
239
|
-
const rs = routeSegments[i];
|
|
240
|
-
const cs = clientSegments[i];
|
|
241
|
-
if (isParamSegment(rs))
|
|
242
|
-
continue;
|
|
243
|
-
if (rs !== cs) {
|
|
244
|
-
matched = false;
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (matched)
|
|
261
|
+
if (routePathMatchesSegments(route.path, clientSegments))
|
|
249
262
|
matches.push(route);
|
|
250
263
|
}
|
|
251
264
|
return matches;
|
|
@@ -253,6 +266,87 @@ export function findRoutesAtPath(clientPath, routes) {
|
|
|
253
266
|
function trimTrailing(path) {
|
|
254
267
|
return path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path;
|
|
255
268
|
}
|
|
269
|
+
function routePathMatchesSegments(routePath, clientSegments) {
|
|
270
|
+
const routeSegments = trimTrailing(routePath).split('/');
|
|
271
|
+
if (routeSegments.length !== clientSegments.length)
|
|
272
|
+
return false;
|
|
273
|
+
for (let i = 0; i < routeSegments.length; i++) {
|
|
274
|
+
const rs = routeSegments[i];
|
|
275
|
+
const cs = clientSegments[i];
|
|
276
|
+
if (isParamSegment(rs))
|
|
277
|
+
continue;
|
|
278
|
+
if (rs !== cs)
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
function isHighConfidenceClientPath(path) {
|
|
284
|
+
if (!API_PATH_RE.test(path))
|
|
285
|
+
return false;
|
|
286
|
+
if (hasCatchAllOrWildcardSegment(path))
|
|
287
|
+
return false;
|
|
288
|
+
const segments = trimTrailing(path).split('/').filter(Boolean);
|
|
289
|
+
return segments.every((segment) => {
|
|
290
|
+
if (!segment.includes('${'))
|
|
291
|
+
return true;
|
|
292
|
+
return /^\$\{[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)?\}$/.test(segment);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function isHighConfidenceServerPath(path) {
|
|
296
|
+
if (!API_PATH_RE.test(path))
|
|
297
|
+
return false;
|
|
298
|
+
return !hasCatchAllOrWildcardSegment(path);
|
|
299
|
+
}
|
|
300
|
+
function hasCatchAllOrWildcardSegment(path) {
|
|
301
|
+
return trimTrailing(path)
|
|
302
|
+
.split('/')
|
|
303
|
+
.some((segment) => {
|
|
304
|
+
if (!segment)
|
|
305
|
+
return false;
|
|
306
|
+
if (segment === '*' || segment.includes('...'))
|
|
307
|
+
return true;
|
|
308
|
+
if (segment.startsWith('*'))
|
|
309
|
+
return true;
|
|
310
|
+
if (/^\{[^}:]+:path\}$/.test(segment))
|
|
311
|
+
return true;
|
|
312
|
+
return false;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
// Verbs emitted by route declarations that intentionally accept any method.
|
|
316
|
+
const WILDCARD_METHODS = new Set(['ALL', 'ANY']);
|
|
317
|
+
export function routeMethodMatches(routeMethod, clientMethod) {
|
|
318
|
+
if (!routeMethod)
|
|
319
|
+
return true;
|
|
320
|
+
const r = routeMethod.toUpperCase();
|
|
321
|
+
if (WILDCARD_METHODS.has(r))
|
|
322
|
+
return true;
|
|
323
|
+
const c = clientMethod.toUpperCase();
|
|
324
|
+
if (r === c)
|
|
325
|
+
return true;
|
|
326
|
+
// Express and Starlette/FastAPI both auto-respond to HEAD on GET routes.
|
|
327
|
+
if (c === 'HEAD' && r === 'GET')
|
|
328
|
+
return true;
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Resolve an inline Express handler's concept from a route node. Only
|
|
333
|
+
* meaningful for `route` entrypoints whose mapper set `handlerConceptId`
|
|
334
|
+
* (inline arrow/function handlers — not imported identifiers). Returns
|
|
335
|
+
* undefined when the route has no inline handler or the expected concept
|
|
336
|
+
* is absent from the map (e.g., stripped during serialisation).
|
|
337
|
+
*
|
|
338
|
+
* Rules that reason about handler body contents — body-shape drift, auth
|
|
339
|
+
* checks, response envelope detection — use this as the single lookup
|
|
340
|
+
* point so callers don't re-implement span-or-id matching in each rule.
|
|
341
|
+
*/
|
|
342
|
+
export function findHandlerConcept(map, route) {
|
|
343
|
+
if (route.kind !== 'entrypoint' || route.payload.kind !== 'entrypoint')
|
|
344
|
+
return undefined;
|
|
345
|
+
const handlerId = route.payload.handlerConceptId;
|
|
346
|
+
if (!handlerId)
|
|
347
|
+
return undefined;
|
|
348
|
+
return map.nodes.find((n) => n.id === handlerId);
|
|
349
|
+
}
|
|
256
350
|
function isParamSegment(seg) {
|
|
257
351
|
return seg.startsWith(':') || (seg.startsWith('{') && seg.endsWith('}'));
|
|
258
352
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross-stack-utils.js","sourceRoot":"","sources":["../../src/concept-rules/cross-stack-utils.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cross-stack-utils.js","sourceRoot":"","sources":["../../src/concept-rules/cross-stack-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Language-agnostic by design.
|
|
6
6
|
*/
|
|
7
7
|
import type { ConceptMap } from '@kernlang/core';
|
|
8
|
-
import type { ReviewFinding } from '../types.js';
|
|
8
|
+
import type { ReviewConfig, ReviewFinding } from '../types.js';
|
|
9
9
|
export interface ConceptRuleContext {
|
|
10
10
|
concepts: ConceptMap;
|
|
11
11
|
filePath: string;
|
|
@@ -13,7 +13,9 @@ export interface ConceptRuleContext {
|
|
|
13
13
|
allConcepts?: Map<string, ConceptMap>;
|
|
14
14
|
/** Resolved import graph — filePath → imported file paths */
|
|
15
15
|
graphImports?: Map<string, string[]>;
|
|
16
|
+
/** Cross-stack precision mode. Defaults to guard. */
|
|
17
|
+
crossStackMode?: 'guard' | 'audit';
|
|
16
18
|
}
|
|
17
19
|
export type ConceptRule = (ctx: ConceptRuleContext) => ReviewFinding[];
|
|
18
20
|
export declare const conceptRules: ConceptRule[];
|
|
19
|
-
export declare function runConceptRules(concepts: ConceptMap, filePath: string, allConcepts?: Map<string, ConceptMap>, graphImports?: Map<string, string[]>): ReviewFinding[];
|
|
21
|
+
export declare function runConceptRules(concepts: ConceptMap, filePath: string, allConcepts?: Map<string, ConceptMap>, graphImports?: Map<string, string[]>, config?: Pick<ReviewConfig, 'crossStackMode'>): ReviewFinding[];
|
|
@@ -5,43 +5,93 @@
|
|
|
5
5
|
* Language-agnostic by design.
|
|
6
6
|
*/
|
|
7
7
|
import { authDrift } from './auth-drift.js';
|
|
8
|
+
import { authPropagationDrift } from './auth-propagation-drift.js';
|
|
9
|
+
import { bodyShapeDrift } from './body-shape-drift.js';
|
|
8
10
|
import { boundaryMutation } from './boundary-mutation.js';
|
|
9
11
|
import { contractDrift } from './contract-drift.js';
|
|
10
12
|
import { contractMethodDrift } from './contract-method-drift.js';
|
|
11
13
|
import { duplicateRoute } from './duplicate-route.js';
|
|
12
14
|
import { ignoredError } from './ignored-error.js';
|
|
13
15
|
import { missingResponseModel } from './missing-response-model.js';
|
|
16
|
+
import { mutationWithoutIdempotency } from './mutation-without-idempotency.js';
|
|
14
17
|
import { orphanRoute } from './orphan-route.js';
|
|
15
18
|
import { paramNameSwap } from './param-name-swap.js';
|
|
19
|
+
import { requestValidationDrift } from './request-validation-drift.js';
|
|
16
20
|
import { syncHandlerDoesIo } from './sync-handler-does-io.js';
|
|
17
21
|
import { taintedAcrossWire } from './tainted-across-wire.js';
|
|
22
|
+
import { unboundedCollectionQuery } from './unbounded-collection-query.js';
|
|
18
23
|
import { unguardedEffect } from './unguarded-effect.js';
|
|
24
|
+
import { unhandledApiErrorShape } from './unhandled-api-error-shape.js';
|
|
19
25
|
import { unrecoveredEffect } from './unrecovered-effect.js';
|
|
20
26
|
import { untypedApiResponse } from './untyped-api-response.js';
|
|
21
27
|
import { untypedBothEndsResponse } from './untyped-both-ends-response.js';
|
|
22
28
|
export const conceptRules = [
|
|
23
29
|
authDrift,
|
|
30
|
+
authPropagationDrift,
|
|
31
|
+
bodyShapeDrift,
|
|
24
32
|
boundaryMutation,
|
|
25
33
|
contractDrift,
|
|
26
34
|
contractMethodDrift,
|
|
27
35
|
duplicateRoute,
|
|
28
36
|
ignoredError,
|
|
29
37
|
missingResponseModel,
|
|
38
|
+
mutationWithoutIdempotency,
|
|
30
39
|
orphanRoute,
|
|
31
40
|
paramNameSwap,
|
|
41
|
+
requestValidationDrift,
|
|
32
42
|
syncHandlerDoesIo,
|
|
33
43
|
taintedAcrossWire,
|
|
44
|
+
unboundedCollectionQuery,
|
|
34
45
|
unguardedEffect,
|
|
46
|
+
unhandledApiErrorShape,
|
|
35
47
|
unrecoveredEffect,
|
|
36
48
|
untypedApiResponse,
|
|
37
49
|
untypedBothEndsResponse,
|
|
38
50
|
];
|
|
39
|
-
export function runConceptRules(concepts, filePath, allConcepts, graphImports) {
|
|
40
|
-
const ctx = {
|
|
51
|
+
export function runConceptRules(concepts, filePath, allConcepts, graphImports, config) {
|
|
52
|
+
const ctx = {
|
|
53
|
+
concepts,
|
|
54
|
+
filePath,
|
|
55
|
+
allConcepts,
|
|
56
|
+
graphImports,
|
|
57
|
+
crossStackMode: config?.crossStackMode,
|
|
58
|
+
};
|
|
41
59
|
const findings = [];
|
|
42
60
|
for (const rule of conceptRules) {
|
|
43
61
|
findings.push(...rule(ctx));
|
|
44
62
|
}
|
|
45
|
-
|
|
63
|
+
if (ctx.crossStackMode === 'audit')
|
|
64
|
+
return findings;
|
|
65
|
+
return suppressOverlappingNewRuleFindings(findings);
|
|
66
|
+
}
|
|
67
|
+
const NEW_RULE_OWNERS = {
|
|
68
|
+
'auth-propagation-drift': ['auth-drift'],
|
|
69
|
+
'unhandled-api-error-shape': ['auth-drift', 'contract-drift', 'contract-method-drift'],
|
|
70
|
+
'unbounded-collection-query': ['contract-drift', 'contract-method-drift'],
|
|
71
|
+
'request-validation-drift': ['body-shape-drift', 'contract-drift', 'contract-method-drift'],
|
|
72
|
+
};
|
|
73
|
+
function suppressOverlappingNewRuleFindings(findings) {
|
|
74
|
+
const ownerSpans = new Map();
|
|
75
|
+
for (const finding of findings) {
|
|
76
|
+
const spanKey = findingSpanKey(finding);
|
|
77
|
+
for (const owners of Object.values(NEW_RULE_OWNERS)) {
|
|
78
|
+
if (!owners.includes(finding.ruleId))
|
|
79
|
+
continue;
|
|
80
|
+
const existing = ownerSpans.get(finding.ruleId) ?? new Set();
|
|
81
|
+
existing.add(spanKey);
|
|
82
|
+
ownerSpans.set(finding.ruleId, existing);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return findings.filter((finding) => {
|
|
86
|
+
const owners = NEW_RULE_OWNERS[finding.ruleId];
|
|
87
|
+
if (!owners)
|
|
88
|
+
return true;
|
|
89
|
+
const spanKey = findingSpanKey(finding);
|
|
90
|
+
return !owners.some((owner) => ownerSpans.get(owner)?.has(spanKey));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function findingSpanKey(finding) {
|
|
94
|
+
const span = finding.primarySpan;
|
|
95
|
+
return `${span.file}:${span.startLine}:${span.startCol}`;
|
|
46
96
|
}
|
|
47
97
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/concept-rules/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/concept-rules/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: mutation-without-idempotency
|
|
3
|
+
*
|
|
4
|
+
* Backend rule — fires on mutating HTTP routes that perform a DB write without
|
|
5
|
+
* visible idempotency, transaction, unique/upsert, or duplicate-protection
|
|
6
|
+
* evidence.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReviewFinding } from '../types.js';
|
|
9
|
+
import type { ConceptRuleContext } from './index.js';
|
|
10
|
+
export declare function mutationWithoutIdempotency(ctx: ConceptRuleContext): ReviewFinding[];
|