@kernlang/review 3.3.9 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/call-graph.d.ts +10 -0
- package/dist/call-graph.js +138 -9
- package/dist/call-graph.js.map +1 -1
- package/dist/concept-rules/auth-drift.js +2 -0
- package/dist/concept-rules/auth-drift.js.map +1 -1
- package/dist/concept-rules/auth-propagation-drift.js +2 -0
- package/dist/concept-rules/auth-propagation-drift.js.map +1 -1
- package/dist/concept-rules/body-shape-drift.d.ts +1 -1
- package/dist/concept-rules/body-shape-drift.js +5 -3
- package/dist/concept-rules/body-shape-drift.js.map +1 -1
- package/dist/concept-rules/contract-drift.js +3 -1
- package/dist/concept-rules/contract-drift.js.map +1 -1
- package/dist/concept-rules/contract-method-drift.js +2 -0
- package/dist/concept-rules/contract-method-drift.js.map +1 -1
- package/dist/concept-rules/index.js +2 -33
- package/dist/concept-rules/index.js.map +1 -1
- package/dist/concept-rules/request-validation-drift.js +3 -0
- package/dist/concept-rules/request-validation-drift.js.map +1 -1
- package/dist/concept-rules/root-cause.d.ts +4 -0
- package/dist/concept-rules/root-cause.js +31 -0
- package/dist/concept-rules/root-cause.js.map +1 -0
- package/dist/concept-rules/unbounded-collection-query.js +2 -0
- package/dist/concept-rules/unbounded-collection-query.js.map +1 -1
- package/dist/concept-rules/unhandled-api-error-shape.js +2 -0
- package/dist/concept-rules/unhandled-api-error-shape.js.map +1 -1
- package/dist/default-export.d.ts +41 -0
- package/dist/default-export.js +76 -0
- package/dist/default-export.js.map +1 -0
- package/dist/eval.d.ts +67 -0
- package/dist/eval.js +177 -0
- package/dist/eval.js.map +1 -0
- package/dist/file-context.js +32 -13
- package/dist/file-context.js.map +1 -1
- package/dist/file-role.d.ts +6 -0
- package/dist/file-role.js +27 -0
- package/dist/file-role.js.map +1 -1
- package/dist/framework-seeds.d.ts +46 -0
- package/dist/framework-seeds.js +245 -0
- package/dist/framework-seeds.js.map +1 -0
- package/dist/git-env.d.ts +1 -0
- package/dist/git-env.js +25 -0
- package/dist/git-env.js.map +1 -0
- package/dist/graph.js +246 -21
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.js +200 -56
- package/dist/index.js.map +1 -1
- package/dist/mappers/ts-concepts.js +87 -20
- package/dist/mappers/ts-concepts.js.map +1 -1
- package/dist/path-canonical.d.ts +34 -0
- package/dist/path-canonical.js +85 -0
- package/dist/path-canonical.js.map +1 -0
- package/dist/policy.d.ts +22 -0
- package/dist/policy.js +47 -0
- package/dist/policy.js.map +1 -0
- package/dist/project-context.d.ts +135 -0
- package/dist/project-context.js +563 -0
- package/dist/project-context.js.map +1 -0
- package/dist/public-api.d.ts +21 -0
- package/dist/public-api.js +17 -2
- package/dist/public-api.js.map +1 -1
- package/dist/reporter.js +22 -0
- package/dist/reporter.js.map +1 -1
- package/dist/rule-quality.d.ts +58 -0
- package/dist/rule-quality.js +357 -0
- package/dist/rule-quality.js.map +1 -0
- package/dist/rules/dead-code.d.ts +2 -2
- package/dist/rules/dead-code.js +88 -4
- package/dist/rules/dead-code.js.map +1 -1
- package/dist/rules/index.d.ts +22 -0
- package/dist/rules/index.js +32 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/kern-source.d.ts +4 -0
- package/dist/rules/kern-source.js +183 -0
- package/dist/rules/kern-source.js.map +1 -1
- package/dist/rules/react.js +52 -3
- package/dist/rules/react.js.map +1 -1
- package/dist/rules/suggest-kern-primitive.js +0 -1
- package/dist/rules/suggest-kern-primitive.js.map +1 -1
- package/dist/semantic-diff.js +2 -0
- package/dist/semantic-diff.js.map +1 -1
- package/dist/suppression/apply-suppression.js +2 -0
- package/dist/suppression/apply-suppression.js.map +1 -1
- package/dist/suppression/parse-directives.d.ts +13 -5
- package/dist/suppression/parse-directives.js +62 -8
- package/dist/suppression/parse-directives.js.map +1 -1
- package/dist/suppression/types.d.ts +9 -0
- package/dist/suppression/types.js +6 -1
- package/dist/suppression/types.js.map +1 -1
- package/dist/taint-crossfile.js +15 -8
- package/dist/taint-crossfile.js.map +1 -1
- package/dist/telemetry.d.ts +126 -0
- package/dist/telemetry.js +303 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +165 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-Context — repo-level signals for the review pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Reads project configs (tsconfig.json, package.json) and .gitignore so rules
|
|
5
|
+
* can gate on what the user already enforces elsewhere. Designed to be SAFE on
|
|
6
|
+
* adversarial inputs:
|
|
7
|
+
*
|
|
8
|
+
* - **JSON-only.** No executable config readers (no eslint.config.js eval).
|
|
9
|
+
* Phase 2 may add YAML/TOML but only via safeLoad; never `require()` of a
|
|
10
|
+
* user-controlled file.
|
|
11
|
+
* - **Realpath containment.** Any path resolved from a config (extends, etc.)
|
|
12
|
+
* must live under the project root after `realpathSync` — otherwise it is
|
|
13
|
+
* ignored. Defends against `extends: '../../../etc/passwd'` and symlink
|
|
14
|
+
* traversal attacks surfaced by the Phase 1 red-team.
|
|
15
|
+
* - **Content-hash cache.** Cache key is hashed file content + extends chain
|
|
16
|
+
* (reuses the cache.ts pattern). mtime is unsound on second-resolution
|
|
17
|
+
* filesystems and on TOCTOU windows where bytes change but mtime does not.
|
|
18
|
+
* - **LRU eviction.** Max 128 cached project roots — defense against the
|
|
19
|
+
* long-running Guard bot accumulating per-PR worktree paths until OOM.
|
|
20
|
+
* - **Pattern-length cap on .gitignore.** Discards any individual pattern
|
|
21
|
+
* longer than 256 chars. Defense against ReDoS via crafted negation +
|
|
22
|
+
* quantifier patterns from the red-team.
|
|
23
|
+
*/
|
|
24
|
+
import { execFileSync } from 'child_process';
|
|
25
|
+
import { createHash } from 'crypto';
|
|
26
|
+
import { existsSync, readFileSync, realpathSync } from 'fs';
|
|
27
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'path';
|
|
28
|
+
import { withoutLocalGitEnv } from './git-env.js';
|
|
29
|
+
/** Maximum pattern length for a single .gitignore entry (red-team P1 ReDoS guard). */
|
|
30
|
+
const MAX_GITIGNORE_PATTERN_LENGTH = 256;
|
|
31
|
+
/** LRU cap for cached project contexts (red-team P1 OOM guard for Guard bot). */
|
|
32
|
+
const CONTEXT_CACHE_CAP = 128;
|
|
33
|
+
/** Map iteration order is insertion order; deletes + re-inserts give LRU. */
|
|
34
|
+
const contextCache = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* Walk up from a starting directory looking for the nearest `package.json`.
|
|
37
|
+
* Returns undefined if none is found before the filesystem root. Used by
|
|
38
|
+
* per-file review entry points that need a project context but only have a
|
|
39
|
+
* file path.
|
|
40
|
+
*/
|
|
41
|
+
export function findProjectRoot(startDir) {
|
|
42
|
+
let cur = resolve(startDir);
|
|
43
|
+
while (true) {
|
|
44
|
+
if (existsSync(resolve(cur, 'package.json')))
|
|
45
|
+
return cur;
|
|
46
|
+
const parent = dirname(cur);
|
|
47
|
+
if (parent === cur)
|
|
48
|
+
return undefined;
|
|
49
|
+
cur = parent;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the project context for a project root. Cached by content hash —
|
|
54
|
+
* a config file edit invalidates the entry on next call.
|
|
55
|
+
*/
|
|
56
|
+
export function getProjectContext(projectRoot) {
|
|
57
|
+
const root = safeRealpath(projectRoot);
|
|
58
|
+
if (!root) {
|
|
59
|
+
return emptyContext(projectRoot);
|
|
60
|
+
}
|
|
61
|
+
const probe = computeContentHash(root);
|
|
62
|
+
const cached = contextCache.get(root);
|
|
63
|
+
if (cached && cached.hash === probe) {
|
|
64
|
+
// LRU touch: delete + re-insert moves it to most-recently-used.
|
|
65
|
+
contextCache.delete(root);
|
|
66
|
+
contextCache.set(root, cached);
|
|
67
|
+
return cached.context;
|
|
68
|
+
}
|
|
69
|
+
const context = buildContext(root, probe);
|
|
70
|
+
contextCache.set(root, { hash: probe, context });
|
|
71
|
+
// LRU eviction.
|
|
72
|
+
while (contextCache.size > CONTEXT_CACHE_CAP) {
|
|
73
|
+
const oldestKey = contextCache.keys().next().value;
|
|
74
|
+
if (oldestKey === undefined)
|
|
75
|
+
break;
|
|
76
|
+
contextCache.delete(oldestKey);
|
|
77
|
+
}
|
|
78
|
+
return context;
|
|
79
|
+
}
|
|
80
|
+
/** Test-only: clear cache between tests. */
|
|
81
|
+
export function _resetProjectContextCache() {
|
|
82
|
+
contextCache.clear();
|
|
83
|
+
}
|
|
84
|
+
/** Test-only: report current cache size. */
|
|
85
|
+
export function _projectContextCacheSize() {
|
|
86
|
+
return contextCache.size;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns true iff the file is matched by the project's .gitignore. Use
|
|
90
|
+
* `isReviewable` for the full skip-list semantics — this is the gitignore
|
|
91
|
+
* predicate alone.
|
|
92
|
+
*/
|
|
93
|
+
export function isPathIgnored(filePath, ctx) {
|
|
94
|
+
const rel = toRelative(ctx.root, filePath);
|
|
95
|
+
if (rel === undefined)
|
|
96
|
+
return false;
|
|
97
|
+
let ignored = false;
|
|
98
|
+
for (const pattern of ctx.gitignore.rootPatterns) {
|
|
99
|
+
if (pattern.regex.test(rel)) {
|
|
100
|
+
ignored = !pattern.negate;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return ignored;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Per-flag tsconfig strictness — phase 2.3 from the red-team. The composite
|
|
107
|
+
* `strict: true` is shorthand for several flags; users frequently enable
|
|
108
|
+
* `strictNullChecks` alone without the umbrella. Rules should query the
|
|
109
|
+
* specific flag they depend on, not `strict`, so that `strict:false` does
|
|
110
|
+
* not over-debuff a finding whose underlying guarantee is in fact present.
|
|
111
|
+
*/
|
|
112
|
+
export function isStrictFlagEffective(flag, ctx) {
|
|
113
|
+
const ts = ctx.tsconfig;
|
|
114
|
+
if (!ts)
|
|
115
|
+
return false;
|
|
116
|
+
if (ts[flag] === true)
|
|
117
|
+
return true;
|
|
118
|
+
// The umbrella `strict: true` enables strictNullChecks, noImplicitAny, and
|
|
119
|
+
// a handful of others. Treat it as setting them when not explicitly false.
|
|
120
|
+
if (ts.strict === true && (flag === 'strictNullChecks' || flag === 'noImplicitAny') && ts[flag] !== false) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The full skip-list predicate. A file is reviewable iff it is NOT
|
|
127
|
+
* (gitignored AND not git-tracked).
|
|
128
|
+
*
|
|
129
|
+
* This is the Phase 1 red-team's finding #4 fix: a tracked artifact that lives
|
|
130
|
+
* inside a gitignored directory (the classic `packages/sdk/dist/client.gen.ts`
|
|
131
|
+
* case) must remain reviewable. Suppression-by-skip-list is reserved for
|
|
132
|
+
* truly-untracked outputs.
|
|
133
|
+
*/
|
|
134
|
+
export function isReviewable(filePath, ctx) {
|
|
135
|
+
const rel = toRelative(ctx.root, filePath);
|
|
136
|
+
if (rel === undefined)
|
|
137
|
+
return true; // Outside project root — caller decides.
|
|
138
|
+
if (ctx.gitTrackedFiles.has(rel))
|
|
139
|
+
return true;
|
|
140
|
+
return !isPathIgnored(filePath, ctx);
|
|
141
|
+
}
|
|
142
|
+
// ── Implementation ─────────────────────────────────────────────────────────
|
|
143
|
+
function buildContext(root, contentHash) {
|
|
144
|
+
const packageJson = readJson(root, 'package.json');
|
|
145
|
+
return {
|
|
146
|
+
root,
|
|
147
|
+
packageJson,
|
|
148
|
+
tsconfig: readTsconfig(root),
|
|
149
|
+
gitignore: readGitignore(root),
|
|
150
|
+
gitTrackedFiles: readGitTrackedFiles(root),
|
|
151
|
+
external: readExternalLinterConfig(root, packageJson),
|
|
152
|
+
contentHash,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function emptyContext(projectRoot) {
|
|
156
|
+
return {
|
|
157
|
+
root: resolve(projectRoot),
|
|
158
|
+
gitignore: { rootPatterns: [] },
|
|
159
|
+
gitTrackedFiles: new Set(),
|
|
160
|
+
external: { eslintEnabledRules: new Set(), biomeEnabledRules: new Set() },
|
|
161
|
+
contentHash: '',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Read project-level external linter configs. JSON-only and bounded:
|
|
166
|
+
*
|
|
167
|
+
* - **No eslint.config.js eval.** Phase 1 red-team flagged require() of an
|
|
168
|
+
* attacker-controlled config file as straight RCE. We read `.eslintrc.json`,
|
|
169
|
+
* `.eslintrc` (assumed JSON), and `package.json` `eslintConfig` only. If the
|
|
170
|
+
* project uses flat config (eslint.config.js), this reader returns empty;
|
|
171
|
+
* overlap calibration just doesn't fire — fail-safe.
|
|
172
|
+
* - **Relative-only extends.** Same containment rule as tsconfig: a string
|
|
173
|
+
* starting with `.` is followed if it resolves under the project root.
|
|
174
|
+
* Package refs (`eslint:recommended`, `@scope/eslint-config`) are NOT
|
|
175
|
+
* resolved — we don't reach into node_modules.
|
|
176
|
+
* - **Single tier of overrides ignored.** Only the top-level `rules` block is
|
|
177
|
+
* consumed. Per-file `overrides` requires the async ESLint API; reading them
|
|
178
|
+
* out of context here would be incorrect (different files would see the
|
|
179
|
+
* same merged set), so we skip entirely.
|
|
180
|
+
*/
|
|
181
|
+
function readExternalLinterConfig(root, packageJson) {
|
|
182
|
+
const eslintEnabledRules = new Set();
|
|
183
|
+
const biomeEnabledRules = new Set();
|
|
184
|
+
// ── ESLint ──
|
|
185
|
+
// Priority: .eslintrc.json → .eslintrc → package.json#eslintConfig.
|
|
186
|
+
const eslintRoots = [];
|
|
187
|
+
for (const name of ['.eslintrc.json', '.eslintrc']) {
|
|
188
|
+
const data = readJson(root, name);
|
|
189
|
+
if (data) {
|
|
190
|
+
eslintRoots.push(data);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (packageJson?.eslintConfig)
|
|
195
|
+
eslintRoots.push(packageJson.eslintConfig);
|
|
196
|
+
for (const r of eslintRoots) {
|
|
197
|
+
collectEslintRules(root, r, eslintEnabledRules, new Set(), 0);
|
|
198
|
+
}
|
|
199
|
+
// ── Biome ──
|
|
200
|
+
const biome = readJson(root, 'biome.json');
|
|
201
|
+
if (biome)
|
|
202
|
+
collectBiomeRules(root, biome, biomeEnabledRules, new Set(), 0);
|
|
203
|
+
return { eslintEnabledRules, biomeEnabledRules };
|
|
204
|
+
}
|
|
205
|
+
function collectEslintRules(root, config, out, seen, depth) {
|
|
206
|
+
if (depth > 10)
|
|
207
|
+
return;
|
|
208
|
+
if (!config || typeof config !== 'object')
|
|
209
|
+
return;
|
|
210
|
+
const cfg = config;
|
|
211
|
+
// Walk relative `extends`. Package refs are skipped (see header doc).
|
|
212
|
+
const extendsList = Array.isArray(cfg.extends) ? cfg.extends : typeof cfg.extends === 'string' ? [cfg.extends] : [];
|
|
213
|
+
for (const ext of extendsList) {
|
|
214
|
+
if (typeof ext !== 'string' || !ext.startsWith('.'))
|
|
215
|
+
continue;
|
|
216
|
+
const candidate = resolve(root, ext);
|
|
217
|
+
const real = safeRealpath(candidate);
|
|
218
|
+
if (!real || !isWithin(root, real) || seen.has(real))
|
|
219
|
+
continue;
|
|
220
|
+
seen.add(real);
|
|
221
|
+
if (!existsSync(real))
|
|
222
|
+
continue;
|
|
223
|
+
let extData;
|
|
224
|
+
try {
|
|
225
|
+
extData = JSON.parse(readFileSync(real, 'utf-8').replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, ''));
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
collectEslintRules(root, extData, out, seen, depth + 1);
|
|
231
|
+
}
|
|
232
|
+
// Pull rule levels from this config.
|
|
233
|
+
const rules = cfg.rules;
|
|
234
|
+
if (rules && typeof rules === 'object') {
|
|
235
|
+
for (const [ruleId, raw] of Object.entries(rules)) {
|
|
236
|
+
if (isEnabledLevel(raw))
|
|
237
|
+
out.add(ruleId);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function collectBiomeRules(root, config, out, seen, depth) {
|
|
242
|
+
if (depth > 10)
|
|
243
|
+
return;
|
|
244
|
+
if (!config || typeof config !== 'object')
|
|
245
|
+
return;
|
|
246
|
+
const cfg = config;
|
|
247
|
+
const extendsList = Array.isArray(cfg.extends) ? cfg.extends : typeof cfg.extends === 'string' ? [cfg.extends] : [];
|
|
248
|
+
for (const ext of extendsList) {
|
|
249
|
+
if (typeof ext !== 'string' || !ext.startsWith('.'))
|
|
250
|
+
continue;
|
|
251
|
+
const candidate = resolve(root, ext);
|
|
252
|
+
const real = safeRealpath(candidate);
|
|
253
|
+
if (!real || !isWithin(root, real) || seen.has(real))
|
|
254
|
+
continue;
|
|
255
|
+
seen.add(real);
|
|
256
|
+
if (!existsSync(real))
|
|
257
|
+
continue;
|
|
258
|
+
let extData;
|
|
259
|
+
try {
|
|
260
|
+
extData = JSON.parse(readFileSync(real, 'utf-8').replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, ''));
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
collectBiomeRules(root, extData, out, seen, depth + 1);
|
|
266
|
+
}
|
|
267
|
+
// biome.json shape: linter.rules.<group>.<ruleName>: 'error' | 'warn' | 'off' | { level, options }
|
|
268
|
+
const linter = cfg.linter;
|
|
269
|
+
const groups = linter?.rules;
|
|
270
|
+
if (!groups || typeof groups !== 'object')
|
|
271
|
+
return;
|
|
272
|
+
for (const [groupName, group] of Object.entries(groups)) {
|
|
273
|
+
if (!group || typeof group !== 'object')
|
|
274
|
+
continue;
|
|
275
|
+
if (groupName === 'recommended' || groupName === 'all')
|
|
276
|
+
continue;
|
|
277
|
+
for (const [ruleName, raw] of Object.entries(group)) {
|
|
278
|
+
if (isEnabledLevel(raw))
|
|
279
|
+
out.add(ruleName);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function isEnabledLevel(raw) {
|
|
284
|
+
if (raw === 'error' || raw === 'warn' || raw === 1 || raw === 2)
|
|
285
|
+
return true;
|
|
286
|
+
if (Array.isArray(raw) && raw.length > 0)
|
|
287
|
+
return isEnabledLevel(raw[0]);
|
|
288
|
+
if (raw && typeof raw === 'object') {
|
|
289
|
+
const lvl = raw.level;
|
|
290
|
+
return isEnabledLevel(lvl);
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Shells out to `git ls-files -c -z` to get the set of tracked paths. Returns
|
|
296
|
+
* an empty set if not a git repo or if git is unavailable. Bounded execution
|
|
297
|
+
* (10s timeout, 100 MB buffer) so a misbehaving git can't wedge the review.
|
|
298
|
+
*/
|
|
299
|
+
function readGitTrackedFiles(root) {
|
|
300
|
+
try {
|
|
301
|
+
const buf = execFileSync('git', ['ls-files', '-c', '-z'], {
|
|
302
|
+
cwd: root,
|
|
303
|
+
env: withoutLocalGitEnv(),
|
|
304
|
+
timeout: 10_000,
|
|
305
|
+
maxBuffer: 100 * 1024 * 1024,
|
|
306
|
+
encoding: 'utf-8',
|
|
307
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
308
|
+
});
|
|
309
|
+
const set = new Set();
|
|
310
|
+
for (const path of buf.split('\0')) {
|
|
311
|
+
if (path)
|
|
312
|
+
set.add(path);
|
|
313
|
+
}
|
|
314
|
+
return set;
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return new Set();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function safeRealpath(p) {
|
|
321
|
+
try {
|
|
322
|
+
return realpathSync(resolve(p));
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function isWithin(root, candidate) {
|
|
329
|
+
const rel = relative(root, candidate);
|
|
330
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
331
|
+
}
|
|
332
|
+
function toRelative(root, filePath) {
|
|
333
|
+
const abs = realpathOrResolve(filePath);
|
|
334
|
+
if (!isWithin(root, abs))
|
|
335
|
+
return undefined;
|
|
336
|
+
const rel = relative(root, abs);
|
|
337
|
+
// Normalize to POSIX separators for consistent gitignore matching.
|
|
338
|
+
return rel.split(sep).join('/');
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Realpath the candidate (resolves symlinks). Falls back to plain `resolve` if
|
|
342
|
+
* the file does not yet exist or realpath fails. Required because the project
|
|
343
|
+
* root is realpath'd at cache time, so file paths must be compared in the same
|
|
344
|
+
* symlink-resolved form (e.g. macOS `/var → /private/var`).
|
|
345
|
+
*/
|
|
346
|
+
function realpathOrResolve(p) {
|
|
347
|
+
const abs = resolve(p);
|
|
348
|
+
try {
|
|
349
|
+
return realpathSync(abs);
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
// File doesn't exist yet — walk up to deepest existing ancestor and
|
|
353
|
+
// realpath that, then append the missing tail.
|
|
354
|
+
const parts = [];
|
|
355
|
+
let cur = abs;
|
|
356
|
+
while (true) {
|
|
357
|
+
const parent = dirname(cur);
|
|
358
|
+
if (parent === cur)
|
|
359
|
+
return abs;
|
|
360
|
+
try {
|
|
361
|
+
const real = realpathSync(parent);
|
|
362
|
+
return resolve(real, ...parts.reverse(), basenameOf(cur));
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
parts.push(basenameOf(cur));
|
|
366
|
+
cur = parent;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function basenameOf(p) {
|
|
372
|
+
const idx = p.lastIndexOf(sep);
|
|
373
|
+
return idx === -1 ? p : p.slice(idx + 1);
|
|
374
|
+
}
|
|
375
|
+
function computeContentHash(root) {
|
|
376
|
+
const hash = createHash('sha256');
|
|
377
|
+
for (const file of ['package.json', 'tsconfig.json', '.gitignore', '.eslintrc.json', '.eslintrc', 'biome.json']) {
|
|
378
|
+
const abs = resolve(root, file);
|
|
379
|
+
hash.update(file);
|
|
380
|
+
if (existsSync(abs)) {
|
|
381
|
+
try {
|
|
382
|
+
hash.update(readFileSync(abs, 'utf-8'));
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
// unreadable file — included as length-0 contribution
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return hash.digest('hex');
|
|
390
|
+
}
|
|
391
|
+
function readJson(root, name) {
|
|
392
|
+
const abs = resolve(root, name);
|
|
393
|
+
if (!existsSync(abs))
|
|
394
|
+
return undefined;
|
|
395
|
+
if (!isWithin(root, abs))
|
|
396
|
+
return undefined;
|
|
397
|
+
try {
|
|
398
|
+
const raw = readFileSync(abs, 'utf-8');
|
|
399
|
+
// Strip JSONC comments — common in tsconfig.
|
|
400
|
+
const stripped = raw.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
|
|
401
|
+
return JSON.parse(stripped);
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function readTsconfig(root) {
|
|
408
|
+
const merged = readTsconfigChain(root, resolve(root, 'tsconfig.json'), new Set(), 0);
|
|
409
|
+
if (!merged)
|
|
410
|
+
return undefined;
|
|
411
|
+
const opts = merged.compilerOptions ?? {};
|
|
412
|
+
return {
|
|
413
|
+
strict: opts.strict,
|
|
414
|
+
strictNullChecks: opts.strictNullChecks,
|
|
415
|
+
noImplicitAny: opts.noImplicitAny,
|
|
416
|
+
noUnusedLocals: opts.noUnusedLocals,
|
|
417
|
+
noUnusedParameters: opts.noUnusedParameters,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function readTsconfigChain(root, abs, seen, depth) {
|
|
421
|
+
if (depth > 10)
|
|
422
|
+
return undefined;
|
|
423
|
+
if (!isWithin(root, abs))
|
|
424
|
+
return undefined;
|
|
425
|
+
const real = safeRealpath(abs);
|
|
426
|
+
if (!real || !isWithin(root, real))
|
|
427
|
+
return undefined;
|
|
428
|
+
if (seen.has(real))
|
|
429
|
+
return undefined;
|
|
430
|
+
seen.add(real);
|
|
431
|
+
if (!existsSync(real))
|
|
432
|
+
return undefined;
|
|
433
|
+
let raw;
|
|
434
|
+
try {
|
|
435
|
+
const text = readFileSync(real, 'utf-8').replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
|
|
436
|
+
raw = JSON.parse(text);
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
// Merge extends shallowly: extended config provides defaults; current overrides.
|
|
442
|
+
const extendsList = Array.isArray(raw?.extends)
|
|
443
|
+
? raw?.extends
|
|
444
|
+
: typeof raw?.extends === 'string'
|
|
445
|
+
? [raw.extends]
|
|
446
|
+
: [];
|
|
447
|
+
let merged = {};
|
|
448
|
+
for (const ext of extendsList ?? []) {
|
|
449
|
+
if (typeof ext !== 'string')
|
|
450
|
+
continue;
|
|
451
|
+
// Only relative extends are walked. Package refs (`@scope/tsconfig`) live in
|
|
452
|
+
// node_modules; resolving them is overkill for our purposes and would re-open
|
|
453
|
+
// the eval-arbitrary-code surface.
|
|
454
|
+
if (!ext.startsWith('.'))
|
|
455
|
+
continue;
|
|
456
|
+
const candidate = resolve(dirname(real), ext);
|
|
457
|
+
const withJson = candidate.endsWith('.json') ? candidate : `${candidate}.json`;
|
|
458
|
+
const sub = readTsconfigChain(root, withJson, seen, depth + 1);
|
|
459
|
+
if (sub) {
|
|
460
|
+
merged = {
|
|
461
|
+
...merged,
|
|
462
|
+
...sub,
|
|
463
|
+
compilerOptions: { ...merged.compilerOptions, ...sub.compilerOptions },
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
...merged,
|
|
469
|
+
...raw,
|
|
470
|
+
compilerOptions: { ...merged.compilerOptions, ...raw?.compilerOptions },
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function readGitignore(root) {
|
|
474
|
+
const abs = resolve(root, '.gitignore');
|
|
475
|
+
if (!existsSync(abs))
|
|
476
|
+
return { rootPatterns: [] };
|
|
477
|
+
if (!isWithin(root, abs))
|
|
478
|
+
return { rootPatterns: [] };
|
|
479
|
+
let text = '';
|
|
480
|
+
try {
|
|
481
|
+
text = readFileSync(abs, 'utf-8');
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
return { rootPatterns: [] };
|
|
485
|
+
}
|
|
486
|
+
const patterns = [];
|
|
487
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
488
|
+
const trimmed = rawLine.trim();
|
|
489
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
490
|
+
continue;
|
|
491
|
+
if (trimmed.length > MAX_GITIGNORE_PATTERN_LENGTH)
|
|
492
|
+
continue; // ReDoS guard.
|
|
493
|
+
const negate = trimmed.startsWith('!');
|
|
494
|
+
const body = negate ? trimmed.slice(1) : trimmed;
|
|
495
|
+
const matchDirsOnly = body.endsWith('/');
|
|
496
|
+
const cleaned = matchDirsOnly ? body.slice(0, -1) : body;
|
|
497
|
+
const regex = compileGitignoreRegex(cleaned, matchDirsOnly);
|
|
498
|
+
if (!regex)
|
|
499
|
+
continue;
|
|
500
|
+
patterns.push({ raw: trimmed, regex, negate, matchDirsOnly });
|
|
501
|
+
}
|
|
502
|
+
return { rootPatterns: patterns };
|
|
503
|
+
}
|
|
504
|
+
function compileGitignoreRegex(pattern, matchDirsOnly) {
|
|
505
|
+
// Hand-rolled minimal gitignore-style glob → regex. Supports:
|
|
506
|
+
// * → [^/]*
|
|
507
|
+
// **/ → (anything-or-nothing)
|
|
508
|
+
// /xxx → root-anchored
|
|
509
|
+
// xxx → match anywhere in path (with leading dir boundary)
|
|
510
|
+
// xxx/ → directory match (handled via matchDirsOnly param + trailing match)
|
|
511
|
+
// Other extended globs (?, [], **) intentionally limited — keeps the regex
|
|
512
|
+
// shapes bounded and ReDoS-safe.
|
|
513
|
+
let body = pattern;
|
|
514
|
+
const anchored = body.startsWith('/');
|
|
515
|
+
if (anchored)
|
|
516
|
+
body = body.slice(1);
|
|
517
|
+
let regex = '';
|
|
518
|
+
for (let i = 0; i < body.length; i++) {
|
|
519
|
+
const ch = body[i];
|
|
520
|
+
if (ch === '*') {
|
|
521
|
+
if (body[i + 1] === '*' && body[i + 2] === '/') {
|
|
522
|
+
regex += '(?:.*/)?';
|
|
523
|
+
i += 2;
|
|
524
|
+
}
|
|
525
|
+
else if (body[i + 1] === '*') {
|
|
526
|
+
regex += '.*';
|
|
527
|
+
i += 1;
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
regex += '[^/]*';
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
else if (ch === '?') {
|
|
534
|
+
regex += '[^/]';
|
|
535
|
+
}
|
|
536
|
+
else if (ch === '.' ||
|
|
537
|
+
ch === '+' ||
|
|
538
|
+
ch === '(' ||
|
|
539
|
+
ch === ')' ||
|
|
540
|
+
ch === '|' ||
|
|
541
|
+
ch === '^' ||
|
|
542
|
+
ch === '$' ||
|
|
543
|
+
ch === '{' ||
|
|
544
|
+
ch === '}' ||
|
|
545
|
+
ch === '[' ||
|
|
546
|
+
ch === ']' ||
|
|
547
|
+
ch === '\\') {
|
|
548
|
+
regex += `\\${ch}`;
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
regex += ch;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const prefix = anchored ? '^' : '^(?:.*/)?';
|
|
555
|
+
const suffix = matchDirsOnly ? '(?:/.*)?$' : '(?:$|/.*$)';
|
|
556
|
+
try {
|
|
557
|
+
return new RegExp(prefix + regex + suffix);
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
return undefined;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
//# sourceMappingURL=project-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-context.js","sourceRoot":"","sources":["../src/project-context.ts"],"names":[],"mappings":"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"}
|
package/dist/public-api.d.ts
CHANGED
|
@@ -42,6 +42,27 @@ interface PackageJsonLike {
|
|
|
42
42
|
types?: string;
|
|
43
43
|
bin?: string | Record<string, string>;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Convert a POSIX-style glob pattern to a `RegExp` that matches full paths.
|
|
47
|
+
*
|
|
48
|
+
* Supported syntax:
|
|
49
|
+
* - `*` — any run of non-separator chars
|
|
50
|
+
* - `**` — any path fragment, including separators (zero or more segments)
|
|
51
|
+
* - `?` — a single non-separator char
|
|
52
|
+
* - `[abc]` / `[a-z]` — character class
|
|
53
|
+
* - `[!abc]` — negated character class (POSIX-style; translated to regex `[^abc]`)
|
|
54
|
+
*
|
|
55
|
+
* All other regex metacharacters are escaped. Brace expansion (`{a,b}`) is
|
|
56
|
+
* NOT supported — keep config patterns simple; split into multiple entries.
|
|
57
|
+
*
|
|
58
|
+
* The pattern is expected to be POSIX-separated (forward slashes). The caller
|
|
59
|
+
* normalizes Windows backslashes to `/` before calling this. Consecutive `*`
|
|
60
|
+
* runs are collapsed first to prevent catastrophic backtracking on patterns
|
|
61
|
+
* like `**\/**\/**`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function globToRegex(pattern: string): RegExp;
|
|
64
|
+
/** Normalize path separators so glob matching works on Windows. */
|
|
65
|
+
export declare function toPosix(p: string): string;
|
|
45
66
|
/**
|
|
46
67
|
* Resolve a package.json specifier (e.g. `./dist/index.js`) to the source file
|
|
47
68
|
* the review operates on (e.g. `./src/index.ts`). Returns undefined if nothing
|
package/dist/public-api.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import { existsSync, readFileSync } from 'fs';
|
|
19
19
|
import { dirname, isAbsolute, join, resolve } from 'path';
|
|
20
20
|
import { SyntaxKind } from 'ts-morph';
|
|
21
|
+
import { getFrameworkSeed } from './framework-seeds.js';
|
|
21
22
|
const SRC_EXTS = ['.ts', '.tsx'];
|
|
22
23
|
/** Characters that, when present in a pattern, trigger glob expansion. */
|
|
23
24
|
const GLOB_CHARS = /[*?[]/;
|
|
@@ -42,7 +43,7 @@ function hasGlobChars(pattern) {
|
|
|
42
43
|
* runs are collapsed first to prevent catastrophic backtracking on patterns
|
|
43
44
|
* like `**\/**\/**`.
|
|
44
45
|
*/
|
|
45
|
-
function globToRegex(pattern) {
|
|
46
|
+
export function globToRegex(pattern) {
|
|
46
47
|
const squashed = pattern.replace(/\*{2,}/g, '**').replace(/(?:\*\*\/)+/g, '**/');
|
|
47
48
|
let out = '';
|
|
48
49
|
let i = 0;
|
|
@@ -94,7 +95,7 @@ function globToRegex(pattern) {
|
|
|
94
95
|
return new RegExp(`^${out}$`);
|
|
95
96
|
}
|
|
96
97
|
/** Normalize path separators so glob matching works on Windows. */
|
|
97
|
-
function toPosix(p) {
|
|
98
|
+
export function toPosix(p) {
|
|
98
99
|
return p.replace(/\\/g, '/');
|
|
99
100
|
}
|
|
100
101
|
function collectSpecifiers(value) {
|
|
@@ -268,6 +269,20 @@ export function buildPublicApiMap(filePaths, overrides) {
|
|
|
268
269
|
explicitSymbols.add(`${abs}#${name}`);
|
|
269
270
|
}
|
|
270
271
|
}
|
|
272
|
+
// Per-symbol framework seeds — Next.js conventions, etc. Each match
|
|
273
|
+
// contributes (filePath, exportName) tuples to explicitSymbols, NEVER
|
|
274
|
+
// a whole-file flag in entryFiles. That's the fix for red-team CRITICAL
|
|
275
|
+
// #2: a stale helper sitting next to a Next.js page must not inherit
|
|
276
|
+
// public-API status from the page convention. Step 7a covers stable
|
|
277
|
+
// Next.js patterns; 7b adds metadata files / parallel routes / proxy.
|
|
278
|
+
for (const fp of filePaths) {
|
|
279
|
+
const seed = getFrameworkSeed(fp);
|
|
280
|
+
if (!seed)
|
|
281
|
+
continue;
|
|
282
|
+
for (const symbol of seed.symbols) {
|
|
283
|
+
explicitSymbols.add(`${fp}#${symbol}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
271
286
|
return { entryFiles, explicitSymbols };
|
|
272
287
|
}
|
|
273
288
|
export const EMPTY_PUBLIC_API = {
|