@lbroth/rothunter 1.0.0-rc.1
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/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/adapters/llm.d.ts +68 -0
- package/dist/adapters/llm.d.ts.map +1 -0
- package/dist/adapters/llm.js +189 -0
- package/dist/adapters/llm.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/detector-registry.d.ts +32 -0
- package/dist/detector-registry.d.ts.map +1 -0
- package/dist/detector-registry.js +74 -0
- package/dist/detector-registry.js.map +1 -0
- package/dist/detectors/api-race.d.ts +6 -0
- package/dist/detectors/api-race.d.ts.map +1 -0
- package/dist/detectors/api-race.js +222 -0
- package/dist/detectors/api-race.js.map +1 -0
- package/dist/detectors/bad-config.d.ts +6 -0
- package/dist/detectors/bad-config.d.ts.map +1 -0
- package/dist/detectors/bad-config.js +529 -0
- package/dist/detectors/bad-config.js.map +1 -0
- package/dist/detectors/console-log-prod.d.ts +6 -0
- package/dist/detectors/console-log-prod.d.ts.map +1 -0
- package/dist/detectors/console-log-prod.js +72 -0
- package/dist/detectors/console-log-prod.js.map +1 -0
- package/dist/detectors/dead-api.d.ts +10 -0
- package/dist/detectors/dead-api.d.ts.map +1 -0
- package/dist/detectors/dead-api.js +115 -0
- package/dist/detectors/dead-api.js.map +1 -0
- package/dist/detectors/dead-export.d.ts +12 -0
- package/dist/detectors/dead-export.d.ts.map +1 -0
- package/dist/detectors/dead-export.js +140 -0
- package/dist/detectors/dead-export.js.map +1 -0
- package/dist/detectors/dead-handler.d.ts +12 -0
- package/dist/detectors/dead-handler.d.ts.map +1 -0
- package/dist/detectors/dead-handler.js +40 -0
- package/dist/detectors/dead-handler.js.map +1 -0
- package/dist/detectors/dead-module.d.ts +14 -0
- package/dist/detectors/dead-module.d.ts.map +1 -0
- package/dist/detectors/dead-module.js +50 -0
- package/dist/detectors/dead-module.js.map +1 -0
- package/dist/detectors/deep-nesting.d.ts +12 -0
- package/dist/detectors/deep-nesting.d.ts.map +1 -0
- package/dist/detectors/deep-nesting.js +133 -0
- package/dist/detectors/deep-nesting.js.map +1 -0
- package/dist/detectors/duplicate-function.d.ts +9 -0
- package/dist/detectors/duplicate-function.d.ts.map +1 -0
- package/dist/detectors/duplicate-function.js +199 -0
- package/dist/detectors/duplicate-function.js.map +1 -0
- package/dist/detectors/duplicate-type.d.ts +9 -0
- package/dist/detectors/duplicate-type.d.ts.map +1 -0
- package/dist/detectors/duplicate-type.js +166 -0
- package/dist/detectors/duplicate-type.js.map +1 -0
- package/dist/detectors/hot-hub-file.d.ts +11 -0
- package/dist/detectors/hot-hub-file.d.ts.map +1 -0
- package/dist/detectors/hot-hub-file.js +42 -0
- package/dist/detectors/hot-hub-file.js.map +1 -0
- package/dist/detectors/long-file.d.ts +12 -0
- package/dist/detectors/long-file.d.ts.map +1 -0
- package/dist/detectors/long-file.js +82 -0
- package/dist/detectors/long-file.js.map +1 -0
- package/dist/detectors/long-function.d.ts +12 -0
- package/dist/detectors/long-function.d.ts.map +1 -0
- package/dist/detectors/long-function.js +45 -0
- package/dist/detectors/long-function.js.map +1 -0
- package/dist/detectors/magic-numbers.d.ts +10 -0
- package/dist/detectors/magic-numbers.d.ts.map +1 -0
- package/dist/detectors/magic-numbers.js +332 -0
- package/dist/detectors/magic-numbers.js.map +1 -0
- package/dist/detectors/mutable-globals.d.ts +6 -0
- package/dist/detectors/mutable-globals.d.ts.map +1 -0
- package/dist/detectors/mutable-globals.js +95 -0
- package/dist/detectors/mutable-globals.js.map +1 -0
- package/dist/detectors/mutation.d.ts +11 -0
- package/dist/detectors/mutation.d.ts.map +1 -0
- package/dist/detectors/mutation.js +397 -0
- package/dist/detectors/mutation.js.map +1 -0
- package/dist/detectors/public-any.d.ts +6 -0
- package/dist/detectors/public-any.d.ts.map +1 -0
- package/dist/detectors/public-any.js +52 -0
- package/dist/detectors/public-any.js.map +1 -0
- package/dist/detectors/race-condition.d.ts +6 -0
- package/dist/detectors/race-condition.d.ts.map +1 -0
- package/dist/detectors/race-condition.js +608 -0
- package/dist/detectors/race-condition.js.map +1 -0
- package/dist/detectors/shared-db-write.d.ts +6 -0
- package/dist/detectors/shared-db-write.d.ts.map +1 -0
- package/dist/detectors/shared-db-write.js +656 -0
- package/dist/detectors/shared-db-write.js.map +1 -0
- package/dist/detectors/silent-catch.d.ts +6 -0
- package/dist/detectors/silent-catch.d.ts.map +1 -0
- package/dist/detectors/silent-catch.js +167 -0
- package/dist/detectors/silent-catch.js.map +1 -0
- package/dist/detectors/similar-functions.d.ts +15 -0
- package/dist/detectors/similar-functions.d.ts.map +1 -0
- package/dist/detectors/similar-functions.js +334 -0
- package/dist/detectors/similar-functions.js.map +1 -0
- package/dist/detectors/skip-tests.d.ts +6 -0
- package/dist/detectors/skip-tests.d.ts.map +1 -0
- package/dist/detectors/skip-tests.js +69 -0
- package/dist/detectors/skip-tests.js.map +1 -0
- package/dist/detectors/todo-comments.d.ts +29 -0
- package/dist/detectors/todo-comments.d.ts.map +1 -0
- package/dist/detectors/todo-comments.js +154 -0
- package/dist/detectors/todo-comments.js.map +1 -0
- package/dist/detectors/unused-deps.d.ts +8 -0
- package/dist/detectors/unused-deps.d.ts.map +1 -0
- package/dist/detectors/unused-deps.js +115 -0
- package/dist/detectors/unused-deps.js.map +1 -0
- package/dist/extraction/api-race-confirmer.d.ts +31 -0
- package/dist/extraction/api-race-confirmer.d.ts.map +1 -0
- package/dist/extraction/api-race-confirmer.js +110 -0
- package/dist/extraction/api-race-confirmer.js.map +1 -0
- package/dist/extraction/llm-confirmer.d.ts +25 -0
- package/dist/extraction/llm-confirmer.d.ts.map +1 -0
- package/dist/extraction/llm-confirmer.js +118 -0
- package/dist/extraction/llm-confirmer.js.map +1 -0
- package/dist/extraction/mutation-confirmer.d.ts +30 -0
- package/dist/extraction/mutation-confirmer.d.ts.map +1 -0
- package/dist/extraction/mutation-confirmer.js +73 -0
- package/dist/extraction/mutation-confirmer.js.map +1 -0
- package/dist/extraction/prompt-chunking.d.ts +37 -0
- package/dist/extraction/prompt-chunking.d.ts.map +1 -0
- package/dist/extraction/prompt-chunking.js +61 -0
- package/dist/extraction/prompt-chunking.js.map +1 -0
- package/dist/extraction/race-confirmer.d.ts +28 -0
- package/dist/extraction/race-confirmer.d.ts.map +1 -0
- package/dist/extraction/race-confirmer.js +68 -0
- package/dist/extraction/race-confirmer.js.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts +31 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.js +141 -0
- package/dist/extraction/shared-db-write-confirmer.js.map +1 -0
- package/dist/extraction/triage-confirmer.d.ts +59 -0
- package/dist/extraction/triage-confirmer.d.ts.map +1 -0
- package/dist/extraction/triage-confirmer.js +104 -0
- package/dist/extraction/triage-confirmer.js.map +1 -0
- package/dist/graph/cfg.d.ts +45 -0
- package/dist/graph/cfg.d.ts.map +1 -0
- package/dist/graph/cfg.js +198 -0
- package/dist/graph/cfg.js.map +1 -0
- package/dist/graph/decorator-entries.d.ts +2 -0
- package/dist/graph/decorator-entries.d.ts.map +1 -0
- package/dist/graph/decorator-entries.js +89 -0
- package/dist/graph/decorator-entries.js.map +1 -0
- package/dist/graph/entry-points.d.ts +12 -0
- package/dist/graph/entry-points.d.ts.map +1 -0
- package/dist/graph/entry-points.js +282 -0
- package/dist/graph/entry-points.js.map +1 -0
- package/dist/graph/handler-conventions.d.ts +2 -0
- package/dist/graph/handler-conventions.d.ts.map +1 -0
- package/dist/graph/handler-conventions.js +26 -0
- package/dist/graph/handler-conventions.js.map +1 -0
- package/dist/graph/iac-entries.d.ts +2 -0
- package/dist/graph/iac-entries.d.ts.map +1 -0
- package/dist/graph/iac-entries.js +123 -0
- package/dist/graph/iac-entries.js.map +1 -0
- package/dist/graph/import-graph.d.ts +48 -0
- package/dist/graph/import-graph.d.ts.map +1 -0
- package/dist/graph/import-graph.js +86 -0
- package/dist/graph/import-graph.js.map +1 -0
- package/dist/graph/monorepo-detect.d.ts +3 -0
- package/dist/graph/monorepo-detect.d.ts.map +1 -0
- package/dist/graph/monorepo-detect.js +166 -0
- package/dist/graph/monorepo-detect.js.map +1 -0
- package/dist/graph/tsconfig-paths.d.ts +23 -0
- package/dist/graph/tsconfig-paths.d.ts.map +1 -0
- package/dist/graph/tsconfig-paths.js +217 -0
- package/dist/graph/tsconfig-paths.js.map +1 -0
- package/dist/multi-workspace-scanner.d.ts +13 -0
- package/dist/multi-workspace-scanner.d.ts.map +1 -0
- package/dist/multi-workspace-scanner.js +130 -0
- package/dist/multi-workspace-scanner.js.map +1 -0
- package/dist/normalizers/type-normalizer.d.ts +16 -0
- package/dist/normalizers/type-normalizer.d.ts.map +1 -0
- package/dist/normalizers/type-normalizer.js +189 -0
- package/dist/normalizers/type-normalizer.js.map +1 -0
- package/dist/parsers/typescript-parser.d.ts +57 -0
- package/dist/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/parsers/typescript-parser.js +502 -0
- package/dist/parsers/typescript-parser.js.map +1 -0
- package/dist/reporter/json-reporter.d.ts +12 -0
- package/dist/reporter/json-reporter.d.ts.map +1 -0
- package/dist/reporter/json-reporter.js +28 -0
- package/dist/reporter/json-reporter.js.map +1 -0
- package/dist/reporter/markdown-reporter.d.ts +11 -0
- package/dist/reporter/markdown-reporter.d.ts.map +1 -0
- package/dist/reporter/markdown-reporter.js +77 -0
- package/dist/reporter/markdown-reporter.js.map +1 -0
- package/dist/rothunter.d.ts +125 -0
- package/dist/rothunter.d.ts.map +1 -0
- package/dist/rothunter.js +1038 -0
- package/dist/rothunter.js.map +1 -0
- package/dist/server/false-positives.d.ts +34 -0
- package/dist/server/false-positives.d.ts.map +1 -0
- package/dist/server/false-positives.js +85 -0
- package/dist/server/false-positives.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1529 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/marked-to-fix.d.ts +16 -0
- package/dist/server/marked-to-fix.d.ts.map +1 -0
- package/dist/server/marked-to-fix.js +36 -0
- package/dist/server/marked-to-fix.js.map +1 -0
- package/dist/server/scan-store.d.ts +147 -0
- package/dist/server/scan-store.d.ts.map +1 -0
- package/dist/server/scan-store.js +291 -0
- package/dist/server/scan-store.js.map +1 -0
- package/dist/server/settings-store.d.ts +28 -0
- package/dist/server/settings-store.d.ts.map +1 -0
- package/dist/server/settings-store.js +46 -0
- package/dist/server/settings-store.js.map +1 -0
- package/dist/server/workspace-store.d.ts +39 -0
- package/dist/server/workspace-store.d.ts.map +1 -0
- package/dist/server/workspace-store.js +108 -0
- package/dist/server/workspace-store.js.map +1 -0
- package/dist/types/detector-input.d.ts +37 -0
- package/dist/types/detector-input.d.ts.map +1 -0
- package/dist/types/detector-input.js +2 -0
- package/dist/types/detector-input.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/clustering.d.ts +14 -0
- package/dist/utils/clustering.d.ts.map +1 -0
- package/dist/utils/clustering.js +56 -0
- package/dist/utils/clustering.js.map +1 -0
- package/dist/utils/gitignore.d.ts +32 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +122 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/hash.d.ts +11 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +14 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/ignore-annotation.d.ts +28 -0
- package/dist/utils/ignore-annotation.d.ts.map +1 -0
- package/dist/utils/ignore-annotation.js +46 -0
- package/dist/utils/ignore-annotation.js.map +1 -0
- package/dist/utils/llm-json.d.ts +2 -0
- package/dist/utils/llm-json.d.ts.map +1 -0
- package/dist/utils/llm-json.js +53 -0
- package/dist/utils/llm-json.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +4 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/project-conventions.d.ts +2 -0
- package/dist/utils/project-conventions.d.ts.map +1 -0
- package/dist/utils/project-conventions.js +108 -0
- package/dist/utils/project-conventions.js.map +1 -0
- package/dist/utils/regex.d.ts +9 -0
- package/dist/utils/regex.d.ts.map +1 -0
- package/dist/utils/regex.js +11 -0
- package/dist/utils/regex.js.map +1 -0
- package/dist/utils/snippet.d.ts +20 -0
- package/dist/utils/snippet.d.ts.map +1 -0
- package/dist/utils/snippet.js +28 -0
- package/dist/utils/snippet.js.map +1 -0
- package/dist/utils/source-reader.d.ts +19 -0
- package/dist/utils/source-reader.d.ts.map +1 -0
- package/dist/utils/source-reader.js +32 -0
- package/dist/utils/source-reader.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +92 -0
- package/scripts/start-llm.mjs +161 -0
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { stableHash } from '../utils/hash.js';
|
|
3
|
+
import { trimSnippet, trimEnclosingSource } from '../utils/snippet.js';
|
|
4
|
+
import { Project, SyntaxKind, } from 'ts-morph';
|
|
5
|
+
import { buildCfg, blockOf, reachable } from '../graph/cfg.js';
|
|
6
|
+
// Tier-1 race detector. Three patterns:
|
|
7
|
+
// 1. read → await → write on `this.<id>` / module `let`/`var`
|
|
8
|
+
// 2. Promise.all/allSettled/race arms writing the same shared target
|
|
9
|
+
// 3. emitter.on/.addListener handler closing over outer mutable + read-modify-write across await
|
|
10
|
+
// Severity medium, confidence 0.7. `// rothunter:ignore-race` suppresses.
|
|
11
|
+
const PROMISE_PARALLEL_METHODS = new Set(['all', 'allSettled', 'race']);
|
|
12
|
+
const EMITTER_REGISTRATION_METHODS = new Set(['on', 'once', 'addListener', 'addEventListener']);
|
|
13
|
+
export function detectRaceConditions(input) {
|
|
14
|
+
let project;
|
|
15
|
+
if (input.project) {
|
|
16
|
+
project = input.project;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
project = new Project({
|
|
20
|
+
skipAddingFilesFromTsConfig: true,
|
|
21
|
+
skipFileDependencyResolution: true,
|
|
22
|
+
});
|
|
23
|
+
for (const rel of input.files) {
|
|
24
|
+
project.addSourceFileAtPathIfExists(path.join(input.workspaceRoot, rel));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const rmwCandidates = [];
|
|
28
|
+
const promiseAllRaces = [];
|
|
29
|
+
const findings = [];
|
|
30
|
+
for (const sf of project.getSourceFiles()) {
|
|
31
|
+
const relativeFile = path.relative(input.workspaceRoot, sf.getFilePath());
|
|
32
|
+
const moduleMutables = collectModuleMutables(sf);
|
|
33
|
+
// Pattern 1 — read-modify-write across await (every async callable).
|
|
34
|
+
for (const fn of sf.getFunctions()) {
|
|
35
|
+
rmwCandidates.push(...analyzeAsync(fn, relativeFile, moduleMutables));
|
|
36
|
+
}
|
|
37
|
+
for (const cls of sf.getClasses()) {
|
|
38
|
+
for (const m of cls.getMethods()) {
|
|
39
|
+
rmwCandidates.push(...analyzeAsync(m, relativeFile, moduleMutables));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const arrow of sf.getDescendantsOfKind(SyntaxKind.ArrowFunction)) {
|
|
43
|
+
rmwCandidates.push(...analyzeAsync(arrow, relativeFile, moduleMutables));
|
|
44
|
+
}
|
|
45
|
+
for (const expr of sf.getDescendantsOfKind(SyntaxKind.FunctionExpression)) {
|
|
46
|
+
rmwCandidates.push(...analyzeAsync(expr, relativeFile, moduleMutables));
|
|
47
|
+
}
|
|
48
|
+
// Pattern 2 — Promise.all siblings writing the same target.
|
|
49
|
+
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
50
|
+
const race = analyzePromiseAll(call, relativeFile, moduleMutables);
|
|
51
|
+
if (race)
|
|
52
|
+
promiseAllRaces.push(...race);
|
|
53
|
+
}
|
|
54
|
+
// Pattern 3 — event-emitter handler closing over an outer mutable.
|
|
55
|
+
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
56
|
+
const handlerRaces = analyzeEmitterHandler(call, relativeFile);
|
|
57
|
+
if (handlerRaces.length)
|
|
58
|
+
rmwCandidates.push(...handlerRaces);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Pattern 1 + Pattern 3 share the same `analyzeAsync` engine: a handler that
|
|
62
|
+
// closes over a module-scope `let` is independently picked up by both the
|
|
63
|
+
// top-level arrow walker (anonymous arrow → no enclosingName) and the
|
|
64
|
+
// emitter-handler walker (carries the outer named function on its
|
|
65
|
+
// candidates). Sort so named candidates win the fingerprint dedup.
|
|
66
|
+
rmwCandidates.sort((a, b) => (b.enclosingName ? 1 : 0) - (a.enclosingName ? 1 : 0));
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
for (const c of rmwCandidates) {
|
|
69
|
+
const f = toFinding(c);
|
|
70
|
+
if (seen.has(f.fingerprint))
|
|
71
|
+
continue;
|
|
72
|
+
seen.add(f.fingerprint);
|
|
73
|
+
findings.push(f);
|
|
74
|
+
}
|
|
75
|
+
for (const r of promiseAllRaces) {
|
|
76
|
+
const f = promiseAllToFinding(r);
|
|
77
|
+
if (seen.has(f.fingerprint))
|
|
78
|
+
continue;
|
|
79
|
+
seen.add(f.fingerprint);
|
|
80
|
+
findings.push(f);
|
|
81
|
+
}
|
|
82
|
+
return findings;
|
|
83
|
+
}
|
|
84
|
+
function collectModuleMutables(sf) {
|
|
85
|
+
const out = new Set();
|
|
86
|
+
const statements = sf.getVariableStatements();
|
|
87
|
+
for (const stmt of statements) {
|
|
88
|
+
const kind = stmt.getDeclarationKind();
|
|
89
|
+
if (kind !== 'let' && kind !== 'var')
|
|
90
|
+
continue;
|
|
91
|
+
for (const decl of stmt.getDeclarations()) {
|
|
92
|
+
if (decl.getNameNode().getKind() !== SyntaxKind.Identifier)
|
|
93
|
+
continue;
|
|
94
|
+
out.add(decl.getName());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
function analyzeAsync(fn, file, moduleMutables) {
|
|
100
|
+
if (!fn.isAsync())
|
|
101
|
+
return [];
|
|
102
|
+
const body = fn.getBody?.();
|
|
103
|
+
if (!body)
|
|
104
|
+
return [];
|
|
105
|
+
const reads = [];
|
|
106
|
+
const awaits = [];
|
|
107
|
+
const writes = [];
|
|
108
|
+
// --- Reads: variable initialisers that pull from shared state -------------
|
|
109
|
+
for (const decl of body.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
|
|
110
|
+
const init = decl.getInitializer();
|
|
111
|
+
if (!init)
|
|
112
|
+
continue;
|
|
113
|
+
const target = canonicalSharedTarget(init, moduleMutables);
|
|
114
|
+
if (!target)
|
|
115
|
+
continue;
|
|
116
|
+
reads.push({ target, line: decl.getStartLineNumber(), node: decl });
|
|
117
|
+
}
|
|
118
|
+
// Also: `const { x } = this`-style destructuring — treat as a read of `this`.
|
|
119
|
+
// For now we only emit if the destructured source itself is a shared target
|
|
120
|
+
// expression that maps to a canonical key (skipping deep-destructure for v0.1).
|
|
121
|
+
// --- Awaits ---------------------------------------------------------------
|
|
122
|
+
for (const aw of body.getDescendantsOfKind(SyntaxKind.AwaitExpression)) {
|
|
123
|
+
awaits.push({ line: aw.getStartLineNumber(), node: aw });
|
|
124
|
+
}
|
|
125
|
+
if (awaits.length === 0)
|
|
126
|
+
return [];
|
|
127
|
+
// --- Writes: assignment expressions whose LHS is shared state -------------
|
|
128
|
+
for (const bin of body.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
129
|
+
if (bin.getOperatorToken().getText() !== '=')
|
|
130
|
+
continue;
|
|
131
|
+
if (hasIgnoreAnnotation(bin))
|
|
132
|
+
continue;
|
|
133
|
+
const target = canonicalSharedTarget(bin.getLeft(), moduleMutables);
|
|
134
|
+
if (!target)
|
|
135
|
+
continue;
|
|
136
|
+
writes.push({
|
|
137
|
+
target,
|
|
138
|
+
line: bin.getStartLineNumber(),
|
|
139
|
+
endLine: bin.getEndLineNumber(),
|
|
140
|
+
source: bin.getText(),
|
|
141
|
+
node: bin,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (writes.length === 0)
|
|
145
|
+
return [];
|
|
146
|
+
const enclosingName = fn.getName?.();
|
|
147
|
+
const enclosingSource = trimEnclosingSource(fn.getText());
|
|
148
|
+
// Build a CFG of the function body and use forward reachability to
|
|
149
|
+
// confirm that some execution path actually visits read → await → write
|
|
150
|
+
// in that order. Pure line-number ordering misfires on
|
|
151
|
+
// `if (cond) { read } await x; write` — `read` is conditional and may
|
|
152
|
+
// never execute on the path that reaches the write. The CFG resolves
|
|
153
|
+
// that ambiguity (the if-then block and the post-if join block are
|
|
154
|
+
// different CFG nodes, so the read is NOT reachable from the join
|
|
155
|
+
// entry without going through the then branch).
|
|
156
|
+
const cfg = buildCfg(body);
|
|
157
|
+
const blockCache = new Map();
|
|
158
|
+
const blockFor = (n) => {
|
|
159
|
+
const cached = blockCache.get(n);
|
|
160
|
+
if (cached !== undefined)
|
|
161
|
+
return cached;
|
|
162
|
+
const id = blockOf(cfg, n);
|
|
163
|
+
blockCache.set(n, id);
|
|
164
|
+
return id;
|
|
165
|
+
};
|
|
166
|
+
const orderedReach = (fromBlock, fromLine, toBlock, toLine) => {
|
|
167
|
+
if (fromBlock < 0 || toBlock < 0)
|
|
168
|
+
return false;
|
|
169
|
+
if (fromBlock === toBlock) {
|
|
170
|
+
// Same straight-line block — strict line ordering. Back-edges that
|
|
171
|
+
// loop the block to itself are picked up by reachable() below; the
|
|
172
|
+
// first-iteration ordering is what matters for the standard
|
|
173
|
+
// read-modify-write pattern.
|
|
174
|
+
return fromLine < toLine || reachable(cfg, fromBlock, toBlock);
|
|
175
|
+
}
|
|
176
|
+
return reachable(cfg, fromBlock, toBlock);
|
|
177
|
+
};
|
|
178
|
+
const out = [];
|
|
179
|
+
for (const r of reads) {
|
|
180
|
+
const rBlock = blockFor(r.node);
|
|
181
|
+
if (rBlock < 0)
|
|
182
|
+
continue;
|
|
183
|
+
let chosen = null;
|
|
184
|
+
for (const aw of awaits) {
|
|
185
|
+
const aBlock = blockFor(aw.node);
|
|
186
|
+
if (!orderedReach(rBlock, r.line, aBlock, aw.line))
|
|
187
|
+
continue;
|
|
188
|
+
for (const w of writes) {
|
|
189
|
+
if (w.target !== r.target)
|
|
190
|
+
continue;
|
|
191
|
+
const wBlock = blockFor(w.node);
|
|
192
|
+
if (!orderedReach(aBlock, aw.line, wBlock, w.line))
|
|
193
|
+
continue;
|
|
194
|
+
chosen = { aw, w };
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
if (chosen)
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
if (!chosen)
|
|
201
|
+
continue;
|
|
202
|
+
out.push({
|
|
203
|
+
file,
|
|
204
|
+
target: r.target,
|
|
205
|
+
readLine: r.line,
|
|
206
|
+
awaitLine: chosen.aw.line,
|
|
207
|
+
writeLine: chosen.w.line,
|
|
208
|
+
writeEndLine: chosen.w.endLine,
|
|
209
|
+
writeSnippet: trimSnippet(chosen.w.source),
|
|
210
|
+
enclosingName: enclosingName ?? undefined,
|
|
211
|
+
enclosingSource,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
// Pattern 2: Promise.all/allSettled/race. Two shapes:
|
|
217
|
+
// (a) array-literal arms writing same target → cluster finding
|
|
218
|
+
// (b) .map(async ...) parallel callback with read→await→write → single finding
|
|
219
|
+
function analyzePromiseAll(call, file, moduleMutables) {
|
|
220
|
+
const callee = call.getExpression();
|
|
221
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
222
|
+
return [];
|
|
223
|
+
const pa = callee.asKindOrThrow(SyntaxKind.PropertyAccessExpression);
|
|
224
|
+
const head = pa.getExpression().getText();
|
|
225
|
+
if (head !== 'Promise')
|
|
226
|
+
return [];
|
|
227
|
+
if (!PROMISE_PARALLEL_METHODS.has(pa.getName()))
|
|
228
|
+
return [];
|
|
229
|
+
const [arg] = call.getArguments();
|
|
230
|
+
if (!arg)
|
|
231
|
+
return [];
|
|
232
|
+
// (b) `.map(async ...)` parallel-callback shape.
|
|
233
|
+
if (arg.getKind() === SyntaxKind.CallExpression) {
|
|
234
|
+
return analyzeParallelMapCallback(call, arg, file, moduleMutables);
|
|
235
|
+
}
|
|
236
|
+
// (a) array-literal arms.
|
|
237
|
+
const arrayArg = arg.asKind(SyntaxKind.ArrayLiteralExpression);
|
|
238
|
+
if (!arrayArg)
|
|
239
|
+
return [];
|
|
240
|
+
const elements = arrayArg.getElements();
|
|
241
|
+
if (elements.length < 2)
|
|
242
|
+
return [];
|
|
243
|
+
const armWrites = [];
|
|
244
|
+
for (let i = 0; i < elements.length; i++) {
|
|
245
|
+
const armBody = resolveFunctionBody(elements[i]);
|
|
246
|
+
if (!armBody)
|
|
247
|
+
continue;
|
|
248
|
+
for (const bin of armBody.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
249
|
+
if (bin.getOperatorToken().getText() !== '=')
|
|
250
|
+
continue;
|
|
251
|
+
if (hasIgnoreAnnotation(bin))
|
|
252
|
+
continue;
|
|
253
|
+
const target = canonicalSharedTarget(bin.getLeft(), moduleMutables);
|
|
254
|
+
if (!target)
|
|
255
|
+
continue;
|
|
256
|
+
armWrites.push({
|
|
257
|
+
armIndex: i,
|
|
258
|
+
target,
|
|
259
|
+
line: bin.getStartLineNumber(),
|
|
260
|
+
snippet: trimSnippet(bin.getText()),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Cluster by target — only emit when ≥ 2 distinct arms write.
|
|
265
|
+
const byTarget = new Map();
|
|
266
|
+
for (const w of armWrites) {
|
|
267
|
+
const list = byTarget.get(w.target) ?? [];
|
|
268
|
+
list.push(w);
|
|
269
|
+
byTarget.set(w.target, list);
|
|
270
|
+
}
|
|
271
|
+
const enclosingFn = findEnclosingNamedFunction(call);
|
|
272
|
+
const enclosingSource = enclosingFn
|
|
273
|
+
? trimEnclosingSource(enclosingFn.getText())
|
|
274
|
+
: trimEnclosingSource(call.getText());
|
|
275
|
+
const enclosingName = enclosingFn
|
|
276
|
+
? enclosingFn.getName?.()
|
|
277
|
+
: undefined;
|
|
278
|
+
const out = [];
|
|
279
|
+
for (const [target, list] of byTarget) {
|
|
280
|
+
const armIndices = new Set(list.map((w) => w.armIndex));
|
|
281
|
+
if (armIndices.size < 2)
|
|
282
|
+
continue;
|
|
283
|
+
out.push({
|
|
284
|
+
file,
|
|
285
|
+
callLine: call.getStartLineNumber(),
|
|
286
|
+
callEndLine: call.getEndLineNumber(),
|
|
287
|
+
target,
|
|
288
|
+
writes: list.map((w) => ({ line: w.line, snippet: w.snippet })),
|
|
289
|
+
enclosingSource,
|
|
290
|
+
enclosingName: enclosingName ?? undefined,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return out;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* `Promise.all(<arr>.map(async (x) => { ... }))` — single async callback
|
|
297
|
+
* invoked once per element with full parallelism. Every read-modify-write
|
|
298
|
+
* across `await` on a shared target races with every other invocation.
|
|
299
|
+
*/
|
|
300
|
+
function analyzeParallelMapCallback(promiseCall, arg, file, moduleMutables) {
|
|
301
|
+
// arg must be a `.map(<fn>)` / `.forEach(<fn>)` call. `.forEach` does NOT
|
|
302
|
+
// surface its returned promises so it cannot create races through
|
|
303
|
+
// Promise.all — only `.map` counts here.
|
|
304
|
+
if (arg.getKind() !== SyntaxKind.CallExpression)
|
|
305
|
+
return [];
|
|
306
|
+
const mapCall = arg;
|
|
307
|
+
const mapCallee = mapCall.getExpression();
|
|
308
|
+
if (mapCallee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
309
|
+
return [];
|
|
310
|
+
const mapPa = mapCallee.asKindOrThrow(SyntaxKind.PropertyAccessExpression);
|
|
311
|
+
if (mapPa.getName() !== 'map')
|
|
312
|
+
return [];
|
|
313
|
+
const [cb] = mapCall.getArguments();
|
|
314
|
+
if (!cb)
|
|
315
|
+
return [];
|
|
316
|
+
if (cb.getKind() !== SyntaxKind.ArrowFunction &&
|
|
317
|
+
cb.getKind() !== SyntaxKind.FunctionExpression) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
const fn = cb;
|
|
321
|
+
// Only async callbacks can host the read-modify-write-across-await pattern.
|
|
322
|
+
if (!fn.isAsync?.())
|
|
323
|
+
return [];
|
|
324
|
+
const body = fn.getBody?.();
|
|
325
|
+
if (!body)
|
|
326
|
+
return [];
|
|
327
|
+
// Collect shared-target writes that follow an `await`.
|
|
328
|
+
const awaits = body.getDescendantsOfKind(SyntaxKind.AwaitExpression).map((a) => a.getStartLineNumber());
|
|
329
|
+
if (awaits.length === 0)
|
|
330
|
+
return [];
|
|
331
|
+
const firstAwait = Math.min(...awaits);
|
|
332
|
+
const enclosingFn = findEnclosingNamedFunction(promiseCall);
|
|
333
|
+
const enclosingSource = enclosingFn
|
|
334
|
+
? trimEnclosingSource(enclosingFn.getText())
|
|
335
|
+
: trimEnclosingSource(promiseCall.getText());
|
|
336
|
+
const enclosingName = enclosingFn
|
|
337
|
+
? enclosingFn.getName?.()
|
|
338
|
+
: undefined;
|
|
339
|
+
const writesByTarget = new Map();
|
|
340
|
+
for (const bin of body.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
341
|
+
if (bin.getOperatorToken().getText() !== '=')
|
|
342
|
+
continue;
|
|
343
|
+
if (hasIgnoreAnnotation(bin))
|
|
344
|
+
continue;
|
|
345
|
+
if (bin.getStartLineNumber() < firstAwait)
|
|
346
|
+
continue;
|
|
347
|
+
const target = canonicalSharedTarget(bin.getLeft(), moduleMutables);
|
|
348
|
+
if (!target)
|
|
349
|
+
continue;
|
|
350
|
+
const list = writesByTarget.get(target) ?? [];
|
|
351
|
+
list.push({ line: bin.getStartLineNumber(), snippet: trimSnippet(bin.getText()) });
|
|
352
|
+
writesByTarget.set(target, list);
|
|
353
|
+
}
|
|
354
|
+
const out = [];
|
|
355
|
+
for (const [target, writes] of writesByTarget) {
|
|
356
|
+
out.push({
|
|
357
|
+
file,
|
|
358
|
+
callLine: promiseCall.getStartLineNumber(),
|
|
359
|
+
callEndLine: promiseCall.getEndLineNumber(),
|
|
360
|
+
target,
|
|
361
|
+
writes,
|
|
362
|
+
enclosingSource,
|
|
363
|
+
enclosingName: enclosingName ?? undefined,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
return out;
|
|
367
|
+
}
|
|
368
|
+
function resolveFunctionBody(node) {
|
|
369
|
+
let cur = node;
|
|
370
|
+
// Unwrap parens + IIFEs to land on the arrow/function-expression body.
|
|
371
|
+
for (let i = 0; i < 6; i++) {
|
|
372
|
+
const paren = cur.asKind(SyntaxKind.ParenthesizedExpression);
|
|
373
|
+
if (paren) {
|
|
374
|
+
cur = paren.getExpression();
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const callShape = cur.asKind(SyntaxKind.CallExpression);
|
|
378
|
+
if (callShape) {
|
|
379
|
+
let inner = callShape.getExpression();
|
|
380
|
+
let innerParen = inner.asKind(SyntaxKind.ParenthesizedExpression);
|
|
381
|
+
while (innerParen) {
|
|
382
|
+
inner = innerParen.getExpression();
|
|
383
|
+
innerParen = inner.asKind(SyntaxKind.ParenthesizedExpression);
|
|
384
|
+
}
|
|
385
|
+
if (inner.getKind() === SyntaxKind.ArrowFunction ||
|
|
386
|
+
inner.getKind() === SyntaxKind.FunctionExpression) {
|
|
387
|
+
cur = inner;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
if (cur.getKind() === SyntaxKind.ArrowFunction ||
|
|
395
|
+
cur.getKind() === SyntaxKind.FunctionExpression) {
|
|
396
|
+
return cur.getBody?.() ?? null;
|
|
397
|
+
}
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Walk ancestors until a named function-like is hit. Useful for surfacing
|
|
402
|
+
* the outer named function on findings whose immediate enclosing scope is
|
|
403
|
+
* an anonymous arrow/IIFE (Promise.all arms, emitter handlers). Falls back
|
|
404
|
+
* to the nearest function-like when no named ancestor exists.
|
|
405
|
+
*/
|
|
406
|
+
function findEnclosingNamedFunction(node) {
|
|
407
|
+
let nearest = null;
|
|
408
|
+
let cur = node.getParent();
|
|
409
|
+
while (cur) {
|
|
410
|
+
const k = cur.getKind();
|
|
411
|
+
if (k === SyntaxKind.FunctionDeclaration ||
|
|
412
|
+
k === SyntaxKind.MethodDeclaration ||
|
|
413
|
+
k === SyntaxKind.ArrowFunction ||
|
|
414
|
+
k === SyntaxKind.FunctionExpression) {
|
|
415
|
+
if (!nearest)
|
|
416
|
+
nearest = cur;
|
|
417
|
+
const name = cur.getName?.();
|
|
418
|
+
if (name)
|
|
419
|
+
return cur;
|
|
420
|
+
}
|
|
421
|
+
cur = cur.getParent();
|
|
422
|
+
}
|
|
423
|
+
return nearest;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Pattern 3 — `emitter.on(event, handler)` where the handler closes over
|
|
427
|
+
* an outer `let`/`var` and performs a read-modify-write across `await`.
|
|
428
|
+
*
|
|
429
|
+
* We treat any `let`/`var` declared in an enclosing scope (function body,
|
|
430
|
+
* module top level) as a shared mutable. The same `analyzeAsync` engine
|
|
431
|
+
* then runs on the handler body — emitting the canonical read-modify-write
|
|
432
|
+
* race-condition finding.
|
|
433
|
+
*/
|
|
434
|
+
function analyzeEmitterHandler(call, file) {
|
|
435
|
+
const callee = call.getExpression();
|
|
436
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
437
|
+
return [];
|
|
438
|
+
const pa = callee.asKindOrThrow(SyntaxKind.PropertyAccessExpression);
|
|
439
|
+
if (!EMITTER_REGISTRATION_METHODS.has(pa.getName()))
|
|
440
|
+
return [];
|
|
441
|
+
const args = call.getArguments();
|
|
442
|
+
if (args.length < 2)
|
|
443
|
+
return [];
|
|
444
|
+
const handler = args[args.length - 1];
|
|
445
|
+
if (handler.getKind() !== SyntaxKind.ArrowFunction &&
|
|
446
|
+
handler.getKind() !== SyntaxKind.FunctionExpression) {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
const fn = handler;
|
|
450
|
+
// Only async handlers can hit a read-modify-write race across `await`.
|
|
451
|
+
if (!fn.isAsync?.())
|
|
452
|
+
return [];
|
|
453
|
+
const outerMutables = collectOuterMutables(call, fn);
|
|
454
|
+
if (outerMutables.size === 0)
|
|
455
|
+
return [];
|
|
456
|
+
const candidates = analyzeAsync(fn, file, outerMutables);
|
|
457
|
+
// The handler itself is anonymous (arrow / function expression). Surface
|
|
458
|
+
// the outer named function that registered the listener so the finding
|
|
459
|
+
// has a useful enclosingName for downstream callers.
|
|
460
|
+
const outerNamed = findEnclosingNamedFunction(call);
|
|
461
|
+
const outerName = outerNamed
|
|
462
|
+
? outerNamed.getName?.()
|
|
463
|
+
: undefined;
|
|
464
|
+
if (outerName) {
|
|
465
|
+
for (const c of candidates) {
|
|
466
|
+
if (!c.enclosingName)
|
|
467
|
+
c.enclosingName = outerName;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return candidates;
|
|
471
|
+
}
|
|
472
|
+
// let/var bindings the handler closes over. const skipped — mutable
|
|
473
|
+
// contents (Map/Set/Array) are tracked via member writes elsewhere.
|
|
474
|
+
function collectOuterMutables(call, handler) {
|
|
475
|
+
const out = new Set();
|
|
476
|
+
let cur = call.getParent();
|
|
477
|
+
while (cur) {
|
|
478
|
+
if (cur.getKind() === SyntaxKind.Block ||
|
|
479
|
+
cur.getKind() === SyntaxKind.SourceFile) {
|
|
480
|
+
const stmts = cur.getVariableStatements?.();
|
|
481
|
+
if (Array.isArray(stmts)) {
|
|
482
|
+
for (const stmt of stmts) {
|
|
483
|
+
const kind = stmt.getDeclarationKind();
|
|
484
|
+
if (kind !== 'let' && kind !== 'var')
|
|
485
|
+
continue;
|
|
486
|
+
for (const decl of stmt.getDeclarations()) {
|
|
487
|
+
if (decl.getNameNode().getKind() !== SyntaxKind.Identifier)
|
|
488
|
+
continue;
|
|
489
|
+
out.add(decl.getName());
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (cur === handler)
|
|
495
|
+
break;
|
|
496
|
+
cur = cur.getParent();
|
|
497
|
+
}
|
|
498
|
+
return out;
|
|
499
|
+
}
|
|
500
|
+
// Canonical key for shared state. this.foo[.bar] → "this.foo"; bare/elem-access
|
|
501
|
+
// → identifier only if it's a known module mutable. null otherwise.
|
|
502
|
+
function canonicalSharedTarget(expr, moduleMutables) {
|
|
503
|
+
let cur = expr;
|
|
504
|
+
// For PropertyAccessExpression chains, descend to the head identifier or `this`.
|
|
505
|
+
while (true) {
|
|
506
|
+
const propAccess = cur.asKind(SyntaxKind.PropertyAccessExpression);
|
|
507
|
+
const elemAccess = cur.asKind(SyntaxKind.ElementAccessExpression);
|
|
508
|
+
if (!propAccess && !elemAccess)
|
|
509
|
+
break;
|
|
510
|
+
const owner = (propAccess ?? elemAccess).getExpression();
|
|
511
|
+
if (propAccess && owner.getKind() === SyntaxKind.ThisKeyword) {
|
|
512
|
+
// `this.<name>` — canonical key.
|
|
513
|
+
return `this.${propAccess.getName()}`;
|
|
514
|
+
}
|
|
515
|
+
cur = owner;
|
|
516
|
+
}
|
|
517
|
+
if (cur.getKind() === SyntaxKind.Identifier) {
|
|
518
|
+
const name = cur.getText();
|
|
519
|
+
if (moduleMutables.has(name))
|
|
520
|
+
return name;
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
const IGNORE_ANNOTATION = 'rothunter:ignore-race';
|
|
525
|
+
function hasIgnoreAnnotation(node) {
|
|
526
|
+
for (const range of node.getLeadingCommentRanges()) {
|
|
527
|
+
if (range.getText().includes(IGNORE_ANNOTATION))
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
const fullStart = node.getFullStart();
|
|
531
|
+
const sf = node.getSourceFile();
|
|
532
|
+
const text = sf.getFullText().slice(Math.max(0, fullStart - 200), fullStart);
|
|
533
|
+
return text.includes(IGNORE_ANNOTATION);
|
|
534
|
+
}
|
|
535
|
+
function toFinding(c) {
|
|
536
|
+
const title = `Read-modify-write across \`await\` on ${c.target}`;
|
|
537
|
+
const description = [
|
|
538
|
+
`${title}.`,
|
|
539
|
+
`Two concurrent callers can both read \`${c.target}\` before either writes back. The second write overrides the first.`,
|
|
540
|
+
`Locations:`,
|
|
541
|
+
`- ${c.file}:${c.readLine} (read \`${c.target}\`)`,
|
|
542
|
+
`- ${c.file}:${c.awaitLine} (await yields control)`,
|
|
543
|
+
`- ${c.file}:${c.writeLine} (write \`${c.writeSnippet}\`)`,
|
|
544
|
+
'',
|
|
545
|
+
`If the function is invoked behind a mutex / queue / single-flight wrapper, OR the state is per-request and not shared between callers, add a \`// rothunter:ignore-race\` comment above the write OR document the synchronisation.`,
|
|
546
|
+
].join('\n');
|
|
547
|
+
return {
|
|
548
|
+
detectorId: 'race-condition',
|
|
549
|
+
severity: 'medium',
|
|
550
|
+
confidence: 0.7,
|
|
551
|
+
layer: 1,
|
|
552
|
+
title,
|
|
553
|
+
description,
|
|
554
|
+
evidence: [
|
|
555
|
+
{
|
|
556
|
+
file: c.file,
|
|
557
|
+
range: { startLine: c.readLine, endLine: c.writeEndLine },
|
|
558
|
+
snippet: c.enclosingSource,
|
|
559
|
+
note: JSON.stringify({
|
|
560
|
+
target: c.target,
|
|
561
|
+
readLine: c.readLine,
|
|
562
|
+
awaitLine: c.awaitLine,
|
|
563
|
+
writeLine: c.writeLine,
|
|
564
|
+
enclosingName: c.enclosingName ?? '',
|
|
565
|
+
}),
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
suggestion: 'Wrap the critical section in a mutex / single-flight / queue, OR fetch + update + write in one atomic transaction, OR re-fetch the value just before the write.',
|
|
569
|
+
fingerprint: `race:read-modify-write:${stableHash(`${c.file}::${c.target}::${c.readLine}::${c.writeLine}`)}`,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function promiseAllToFinding(r) {
|
|
573
|
+
const title = `Parallel \`Promise.all\` arms write the same shared target ${r.target}`;
|
|
574
|
+
const description = [
|
|
575
|
+
`${title}.`,
|
|
576
|
+
`Two or more sibling arms of the \`Promise.all\` (or \`Promise.allSettled\` / \`Promise.race\`) call write \`${r.target}\`. When both arms execute concurrently the second write overrides the first — guaranteed lost-update, no timing window required.`,
|
|
577
|
+
`Locations:`,
|
|
578
|
+
`- ${r.file}:${r.callLine} (parallel call)`,
|
|
579
|
+
...r.writes.map((w) => `- ${r.file}:${w.line} (write \`${w.snippet}\`)`),
|
|
580
|
+
'',
|
|
581
|
+
'If the arms must run in parallel, write to per-arm locals and merge afterwards, OR serialise the parallel arms, OR use an atomic counter (e.g. mutex / single-flight). Annotate with `// rothunter:ignore-race` on a write line if the parallel writes are mathematically commutative (e.g. set union with no read).',
|
|
582
|
+
].join('\n');
|
|
583
|
+
return {
|
|
584
|
+
detectorId: 'race-condition',
|
|
585
|
+
severity: 'medium',
|
|
586
|
+
confidence: 0.75,
|
|
587
|
+
layer: 1,
|
|
588
|
+
title,
|
|
589
|
+
description,
|
|
590
|
+
evidence: [
|
|
591
|
+
{
|
|
592
|
+
file: r.file,
|
|
593
|
+
range: { startLine: r.callLine, endLine: r.callEndLine },
|
|
594
|
+
snippet: r.enclosingSource,
|
|
595
|
+
note: JSON.stringify({
|
|
596
|
+
pattern: 'promise-all',
|
|
597
|
+
target: r.target,
|
|
598
|
+
callLine: r.callLine,
|
|
599
|
+
writeLines: r.writes.map((w) => w.line),
|
|
600
|
+
enclosingName: r.enclosingName ?? '',
|
|
601
|
+
}),
|
|
602
|
+
},
|
|
603
|
+
],
|
|
604
|
+
suggestion: 'Move the mutating writes out of parallel arms (compute into local results, merge after `await Promise.all`), OR serialise the writes through a single arm, OR wrap with a mutex / atomic primitive.',
|
|
605
|
+
fingerprint: `race:promise-all:${stableHash(`${r.file}::${r.target}::${r.callLine}`)}`,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
//# sourceMappingURL=race-condition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"race-condition.js","sourceRoot":"","sources":["../../src/detectors/race-condition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Finding } from '../types.js';
|
|
2
|
+
import type { FileWalkingDetectorInput } from '../types/detector-input.js';
|
|
3
|
+
export interface SharedDbWriteDetectorInput extends FileWalkingDetectorInput {
|
|
4
|
+
}
|
|
5
|
+
export declare function detectSharedDbWrites(input: SharedDbWriteDetectorInput): Finding[];
|
|
6
|
+
//# sourceMappingURL=shared-db-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-db-write.d.ts","sourceRoot":"","sources":["../../src/detectors/shared-db-write.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAE3E,MAAM,WAAW,0BAA2B,SAAQ,wBAAwB;CAAG;AAsB/E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,EAAE,CAiGjF"}
|