projscan 1.9.0 → 1.10.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.
@@ -0,0 +1,209 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ /**
5
+ * Plugin API preview (1.10+).
6
+ *
7
+ * Gated behind PROJSCAN_PLUGINS_PREVIEW=1. The shape declared here is what
8
+ * 2.0 will commit to; until 2.0 ships, the schema, the discovery path,
9
+ * and the dispatch lifecycle may change between minors. Without the env
10
+ * flag, `loadPlugins()` returns [] and projscan ignores any manifests on
11
+ * disk — so a half-written plugin can't accidentally light up.
12
+ *
13
+ * Goal for 2.0: third parties write `.projscan-plugin.json` declaring an
14
+ * analyzer that produces Issue[], projscan dispatches to it during
15
+ * `doctor` / `ci` / `analyze`, and the issue stream merges with the
16
+ * built-ins. No LLM inside; no codemods; mechanical only — same scope
17
+ * discipline as the rest of projscan.
18
+ */
19
+ export const PLUGIN_PREVIEW_FLAG = 'PROJSCAN_PLUGINS_PREVIEW';
20
+ export const PLUGIN_SCHEMA_VERSION = 1;
21
+ export const PLUGIN_DIR = '.projscan-plugins';
22
+ export const PLUGIN_MANIFEST_EXT = '.projscan-plugin.json';
23
+ export function pluginsEnabled() {
24
+ const v = process.env[PLUGIN_PREVIEW_FLAG];
25
+ return v === '1' || v === 'true';
26
+ }
27
+ /**
28
+ * Discover every plugin manifest under `<root>/.projscan-plugins/`. Manifests
29
+ * that fail to parse or validate are returned with `manifest: null` and an
30
+ * `error` so the CLI / MCP tool can surface them without throwing.
31
+ */
32
+ export async function discoverPluginManifests(rootPath) {
33
+ const dir = path.join(rootPath, PLUGIN_DIR);
34
+ let entries;
35
+ try {
36
+ entries = await fs.readdir(dir);
37
+ }
38
+ catch {
39
+ return [];
40
+ }
41
+ const out = [];
42
+ for (const name of entries.sort()) {
43
+ if (!name.endsWith(PLUGIN_MANIFEST_EXT))
44
+ continue;
45
+ const manifestPath = path.join(dir, name);
46
+ let raw;
47
+ try {
48
+ raw = await fs.readFile(manifestPath, 'utf-8');
49
+ }
50
+ catch (err) {
51
+ out.push({
52
+ manifestPath,
53
+ manifest: null,
54
+ error: `unable to read manifest: ${err instanceof Error ? err.message : String(err)}`,
55
+ });
56
+ continue;
57
+ }
58
+ let parsed;
59
+ try {
60
+ parsed = JSON.parse(raw);
61
+ }
62
+ catch (err) {
63
+ out.push({
64
+ manifestPath,
65
+ manifest: null,
66
+ error: `invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
67
+ });
68
+ continue;
69
+ }
70
+ const validation = validateManifest(parsed);
71
+ if (!validation.ok) {
72
+ out.push({ manifestPath, manifest: null, error: validation.reason });
73
+ continue;
74
+ }
75
+ out.push({ manifestPath, manifest: validation.manifest });
76
+ }
77
+ return out;
78
+ }
79
+ /**
80
+ * Discover, validate, and dynamically import every plugin under the
81
+ * project. Returns [] when the preview flag is off.
82
+ *
83
+ * Errors loading an individual plugin are isolated: that plugin is
84
+ * skipped (with a console.warn on stderr) but other plugins still load.
85
+ * We never let one bad plugin break the projscan pipeline.
86
+ */
87
+ export async function loadPlugins(rootPath) {
88
+ if (!pluginsEnabled())
89
+ return [];
90
+ const discovered = await discoverPluginManifests(rootPath);
91
+ const loaded = [];
92
+ for (const entry of discovered) {
93
+ if (!entry.manifest)
94
+ continue;
95
+ const modulePath = path.resolve(path.dirname(entry.manifestPath), entry.manifest.module);
96
+ try {
97
+ const mod = (await import(pathToFileURL(modulePath).href));
98
+ const exportsObj = (mod.default ?? mod);
99
+ if (typeof exportsObj.check !== 'function') {
100
+ process.stderr.write(`[projscan] plugin "${entry.manifest.name}" missing required export "check"; skipped.\n`);
101
+ continue;
102
+ }
103
+ loaded.push({
104
+ manifest: entry.manifest,
105
+ manifestPath: entry.manifestPath,
106
+ modulePath,
107
+ exports: { check: exportsObj.check },
108
+ });
109
+ }
110
+ catch (err) {
111
+ process.stderr.write(`[projscan] plugin "${entry.manifest.name}" failed to load: ${err instanceof Error ? err.message : String(err)}. skipped.\n`);
112
+ }
113
+ }
114
+ return loaded;
115
+ }
116
+ /**
117
+ * Run every loaded analyzer plugin against `files`. Issues that don't pass
118
+ * a tight shape check are dropped so a malformed plugin can't poison the
119
+ * issue stream. Each plugin's output is also re-stamped with `id` prefixed
120
+ * by the plugin name (so two plugins emitting the same local rule id can't
121
+ * collide).
122
+ */
123
+ export async function runAnalyzerPlugins(plugins, rootPath, files) {
124
+ const out = [];
125
+ for (const p of plugins) {
126
+ let raw;
127
+ try {
128
+ raw = (await p.exports.check(rootPath, files)) ?? [];
129
+ }
130
+ catch (err) {
131
+ process.stderr.write(`[projscan] plugin "${p.manifest.name}" threw during check: ${err instanceof Error ? err.message : String(err)}. ignored for this run.\n`);
132
+ continue;
133
+ }
134
+ for (const issue of raw) {
135
+ if (!isWellShapedIssue(issue))
136
+ continue;
137
+ out.push({
138
+ ...issue,
139
+ id: `plugin:${p.manifest.name}:${issue.id}`,
140
+ category: issue.category || p.manifest.category,
141
+ });
142
+ }
143
+ }
144
+ return out;
145
+ }
146
+ export function validateManifest(input) {
147
+ if (!input || typeof input !== 'object') {
148
+ return { ok: false, reason: 'manifest must be a JSON object' };
149
+ }
150
+ const obj = input;
151
+ if (obj.schemaVersion !== PLUGIN_SCHEMA_VERSION) {
152
+ return {
153
+ ok: false,
154
+ reason: `unsupported schemaVersion ${String(obj.schemaVersion)}; expected ${PLUGIN_SCHEMA_VERSION}`,
155
+ };
156
+ }
157
+ if (typeof obj.name !== 'string' || !/^[a-z0-9][a-z0-9._/-]{0,64}$/i.test(obj.name)) {
158
+ return { ok: false, reason: 'name is required and must be 1–65 chars of [a-z0-9._/-]' };
159
+ }
160
+ if (obj.kind !== 'analyzer') {
161
+ return { ok: false, reason: 'only kind:"analyzer" is supported in the 1.10 preview' };
162
+ }
163
+ if (typeof obj.module !== 'string' || obj.module.length === 0) {
164
+ return { ok: false, reason: 'module is required and must be a relative path' };
165
+ }
166
+ // Path-traversal guard. Modules must resolve under the manifest's own dir.
167
+ if (path.isAbsolute(obj.module) || obj.module.split(/[/\\]/).some((seg) => seg === '..')) {
168
+ return { ok: false, reason: 'module must be a relative path inside the plugin dir' };
169
+ }
170
+ if (typeof obj.category !== 'string' || obj.category.length === 0) {
171
+ return { ok: false, reason: 'category is required' };
172
+ }
173
+ if (obj.description !== undefined && typeof obj.description !== 'string') {
174
+ return { ok: false, reason: 'description must be a string when provided' };
175
+ }
176
+ return {
177
+ ok: true,
178
+ manifest: {
179
+ schemaVersion: obj.schemaVersion,
180
+ name: obj.name,
181
+ kind: obj.kind,
182
+ module: obj.module,
183
+ category: obj.category,
184
+ ...(typeof obj.description === 'string' ? { description: obj.description } : {}),
185
+ },
186
+ };
187
+ }
188
+ function isWellShapedIssue(x) {
189
+ if (!x || typeof x !== 'object')
190
+ return false;
191
+ const obj = x;
192
+ if (typeof obj.id !== 'string' || obj.id.length === 0)
193
+ return false;
194
+ if (typeof obj.title !== 'string')
195
+ return false;
196
+ if (typeof obj.description !== 'string')
197
+ return false;
198
+ if (!isSeverity(obj.severity))
199
+ return false;
200
+ if (typeof obj.category !== 'string')
201
+ return false;
202
+ if (typeof obj.fixAvailable !== 'boolean')
203
+ return false;
204
+ return true;
205
+ }
206
+ function isSeverity(x) {
207
+ return x === 'error' || x === 'warning' || x === 'info';
208
+ }
209
+ //# sourceMappingURL=plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/core/plugins.ts"],"names":[],"mappings":"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"}
@@ -104,6 +104,14 @@ export function computeTaint(graph, config) {
104
104
  // shell-exec sink. 12 catches those without exploding fan-out
105
105
  // memory in the BFS frontier.
106
106
  const MAX_DEPTH = 12;
107
+ // 1.10+ — per-step frontier cap. MAX_DEPTH bounds path length, but
108
+ // wide-fan-out graphs (Java/TS with prevalent get/set/toString bare-name
109
+ // collisions) can balloon the frontier exponentially: each step
110
+ // resolves every bare-name callee to every same-named function in the
111
+ // graph. Once a single step would push past this cap, we abort the
112
+ // remaining BFS for this source and surface it in `truncatedSources`,
113
+ // matching how MAX_DEPTH truncation is reported.
114
+ const MAX_FRONTIER_PER_STEP = 5000;
107
115
  for (const sourceFn of fnByQual.values()) {
108
116
  if (!sourceFn.hasSource)
109
117
  continue;
@@ -126,10 +134,14 @@ export function computeTaint(graph, config) {
126
134
  const visited = new Set([`${sourceFn.file}::${sourceFn.qualName}`]);
127
135
  let frontier = [{ node: sourceFn, path: [sourceFn] }];
128
136
  let depth = 0;
137
+ let frontierCapped = false;
129
138
  while (frontier.length > 0 && depth < MAX_DEPTH) {
130
139
  depth += 1;
131
140
  const next = [];
141
+ let aborted = false;
132
142
  for (const entry of frontier) {
143
+ if (aborted)
144
+ break;
133
145
  for (const calleeName of entry.node.callees) {
134
146
  const candidates = fnsByBareName.get(calleeName) ?? [];
135
147
  for (const candidate of candidates) {
@@ -160,15 +172,26 @@ export function computeTaint(graph, config) {
160
172
  continue;
161
173
  }
162
174
  next.push({ node: candidate, path: newPath });
175
+ if (next.length >= MAX_FRONTIER_PER_STEP) {
176
+ // 1.10+ — per-step frontier cap reached. Abort this source's
177
+ // BFS and surface it as truncated. Continuing would just
178
+ // multiply: each entry in `next` will spawn its own bare-name
179
+ // resolutions on the following step.
180
+ frontierCapped = true;
181
+ aborted = true;
182
+ break;
183
+ }
163
184
  }
185
+ if (aborted)
186
+ break;
164
187
  }
165
188
  }
166
189
  frontier = next;
167
190
  }
168
- // If the BFS exited because of MAX_DEPTH (not because the frontier
169
- // emptied), record the source so the caller knows flows beyond that
170
- // depth weren't explored.
171
- if (frontier.length > 0) {
191
+ // If the BFS exited because of MAX_DEPTH or the per-step frontier cap
192
+ // (not because the frontier emptied), record the source so the caller
193
+ // knows flows beyond that point weren't explored.
194
+ if (frontier.length > 0 || frontierCapped) {
172
195
  truncatedSources.push(sourceFn.qualName);
173
196
  }
174
197
  }
@@ -1 +1 @@
1
- {"version":3,"file":"taint.js","sourceRoot":"","sources":["../../src/core/taint.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"taint.js","sourceRoot":"","sources":["../../src/core/taint.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,14 @@ export interface WatchOptions {
13
13
  }
14
14
  export interface WatchHandle {
15
15
  close: () => void;
16
+ /**
17
+ * 1.10+ — await this to wait for any in-flight debounce flush to settle
18
+ * after `close()`. `close()` itself is synchronous and stops new work,
19
+ * but a flush already past its `closed` check would otherwise resolve
20
+ * its onChange asynchronously after `close()` returned. Tests and
21
+ * orderly-shutdown callers should `await handle.closed` after `close()`.
22
+ */
23
+ closed: Promise<void>;
16
24
  /** Resolves once the initial scan + first onChange call have completed. */
17
25
  ready: Promise<void>;
18
26
  }
@@ -35,12 +35,19 @@ export function startWatcher(rootPath, options) {
35
35
  const pending = new Set();
36
36
  let graph = null;
37
37
  let closed = false;
38
- let inFlight = false;
38
+ // 1.10+ track the in-flight flush as a Promise instead of a bare bool
39
+ // so close() can await it. Without this, a flush that was already past
40
+ // its top-of-function `closed` check would still call options.onChange
41
+ // *after* close() returned, leaving the downstream consumer seeing one
42
+ // final stale event.
43
+ let inFlightPromise = null;
39
44
  const ready = (async () => {
40
45
  const scan = await scanRepository(rootPath);
41
46
  const cached = await loadCachedGraph(rootPath);
42
47
  graph = await buildCodeGraph(rootPath, scan.files, cached);
43
48
  await saveCachedGraph(rootPath, graph).catch(() => undefined);
49
+ if (closed)
50
+ return;
44
51
  await options.onChange({ paths: [], graph });
45
52
  })();
46
53
  ready.then(() => {
@@ -67,7 +74,7 @@ export function startWatcher(rootPath, options) {
67
74
  }
68
75
  });
69
76
  async function flush() {
70
- if (closed || inFlight || !graph) {
77
+ if (closed || inFlightPromise || !graph) {
71
78
  // If a flush fires while one is already in flight, leave `pending`
72
79
  // intact - the next debounce will pick up the accumulated set plus
73
80
  // anything new.
@@ -77,23 +84,34 @@ export function startWatcher(rootPath, options) {
77
84
  return;
78
85
  const batch = [...pending];
79
86
  pending.clear();
80
- inFlight = true;
81
- try {
82
- // Tiny stat-retry: editors that delete-then-write can race the watcher.
83
- await sleep(STAT_RETRY_MS);
84
- await incrementallyUpdateGraph(graph, rootPath, batch);
85
- await saveCachedGraph(rootPath, graph).catch(() => undefined);
86
- await options.onChange({ paths: batch, graph });
87
- }
88
- finally {
89
- inFlight = false;
90
- // If new events arrived while we were processing, schedule another flush.
91
- if (pending.size > 0) {
92
- if (debounceTimer)
93
- clearTimeout(debounceTimer);
94
- debounceTimer = setTimeout(() => void flush(), DEBOUNCE_MS);
87
+ const currentGraph = graph;
88
+ const promise = (async () => {
89
+ try {
90
+ // Tiny stat-retry: editors that delete-then-write can race the watcher.
91
+ await sleep(STAT_RETRY_MS);
92
+ await incrementallyUpdateGraph(currentGraph, rootPath, batch);
93
+ await saveCachedGraph(rootPath, currentGraph).catch(() => undefined);
94
+ // 1.10+ — guard against firing onChange after close(). close()
95
+ // sets `closed` synchronously; we may have raced past the top-of-
96
+ // function check while awaiting the graph update. The downstream
97
+ // consumer has shut down; an event delivered now is a use-after-
98
+ // free for them.
99
+ if (!closed) {
100
+ await options.onChange({ paths: batch, graph: currentGraph });
101
+ }
95
102
  }
96
- }
103
+ finally {
104
+ inFlightPromise = null;
105
+ // If new events arrived while we were processing, schedule another flush.
106
+ if (!closed && pending.size > 0) {
107
+ if (debounceTimer)
108
+ clearTimeout(debounceTimer);
109
+ debounceTimer = setTimeout(() => void flush(), DEBOUNCE_MS);
110
+ }
111
+ }
112
+ })();
113
+ inFlightPromise = promise;
114
+ return promise;
97
115
  }
98
116
  return {
99
117
  close: () => {
@@ -103,6 +121,18 @@ export function startWatcher(rootPath, options) {
103
121
  if (watcher)
104
122
  watcher.close();
105
123
  },
124
+ get closed() {
125
+ // Resolve once any in-flight flush has finished. The flush itself
126
+ // checks `closed` before calling onChange so the downstream sees no
127
+ // post-close event; this just lets shutdown callers await full quiet.
128
+ return (async () => {
129
+ // Wait for ready first so we don't miss the initial onChange in
130
+ // tests that close() immediately after construction.
131
+ await ready.catch(() => undefined);
132
+ if (inFlightPromise)
133
+ await inFlightPromise.catch(() => undefined);
134
+ })();
135
+ },
106
136
  ready,
107
137
  };
108
138
  }
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/core/watcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/core/watcher.ts"],"names":[],"mappings":"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"}