sparda-mcp 0.66.2 → 0.68.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.
@@ -6,11 +6,24 @@
6
6
  // Depth stays bounded like the v0 parser: entry file + mounted routers.
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
- import { parseModule, resolveRelImport } from './extract.js';
9
+ import { parseModule, resolveRelImport, authDenyCall } from './extract.js';
10
10
  import { createResolver, relOf } from './resolve.js';
11
11
 
12
12
  const HTTP = new Set(['get', 'post', 'put', 'patch', 'delete']);
13
13
 
14
+ // A minimal scan carrying ONLY the deny signal — attached to a middleware recognized as a known
15
+ // auth-library guard (ADR-069: `passport.authenticate()`, `expressjwt({…})`) whose body lives in
16
+ // node_modules and so can't be read in-repo. The catalog is a VERIFIED published fact, so the guard
17
+ // earns `verified` (→ can reach PROVEN), not merely `asserted`. No effects/reads enter the graph.
18
+ const AUTH_DENY_SCAN = {
19
+ effects: [],
20
+ returnShapes: [],
21
+ calls: [],
22
+ async: false,
23
+ validatesInput: false,
24
+ guardSignals: { deniesWithStatus: true },
25
+ };
26
+
14
27
  // → { routes, globalMiddlewares, helpers, skipped, scannedFiles }
15
28
  export function extractExpress(cwd, entryFile) {
16
29
  const routes = [];
@@ -224,6 +237,10 @@ export function extractExpress(cwd, entryFile) {
224
237
  sourceFile: relFile,
225
238
  sourceLine: arg.loc?.start.line ?? 0,
226
239
  fn: arg,
240
+ // deep-scan the inline handler like every other callable branch: follows its
241
+ // service/model calls AND carries the module's effect-client provenance (import
242
+ // labels), so an SDK call in an inline handler is recognized by origin.
243
+ scan: resolver.deepScan(arg, mod),
227
244
  };
228
245
  }
229
246
  if (arg.type === 'CallExpression') {
@@ -247,6 +264,20 @@ export function extractExpress(cwd, entryFile) {
247
264
  scan: resolver.deepScan(fnArg, mod),
248
265
  };
249
266
  }
267
+ // known auth-library deny-form middleware (ADR-069): `passport.authenticate('jwt')`,
268
+ // `expressjwt({…})`. Its body is in node_modules (opaque), but the catalog is a verified
269
+ // published fact that it denies — so the guard reads VERIFIED, not asserted, and its app
270
+ // can legitimately reach PROVEN. Deny-FORM precision (a custom callback / credentialsRequired
271
+ // false) is handled in authDenyCall, which abstains rather than over-verify.
272
+ if (authDenyCall(arg, mod.authGuards?.pkgOf)) {
273
+ return {
274
+ name,
275
+ sourceFile: relFile,
276
+ sourceLine: arg.loc?.start.line ?? 0,
277
+ fn: null,
278
+ scan: AUTH_DENY_SCAN,
279
+ };
280
+ }
250
281
  // factory middleware: validate(schema), rateLimit({…}) — the factory
251
282
  // name is known, the produced closure is out of static reach (blind node)
252
283
  return {
@@ -329,6 +360,17 @@ export function extractExpress(cwd, entryFile) {
329
360
  };
330
361
  }
331
362
  }
363
+ // aliased auth-library guard (ADR-069): `const requireAuth = passport.authenticate('jwt')`
364
+ // used by name here — verified by the catalog, exactly like the inline form above.
365
+ if (mod.authGuards?.denyBindings?.has(arg.name)) {
366
+ return {
367
+ name: arg.name,
368
+ sourceFile: relFile,
369
+ sourceLine: arg.loc?.start.line ?? 0,
370
+ fn: null,
371
+ scan: AUTH_DENY_SCAN,
372
+ };
373
+ }
332
374
  // known but bodyless — node exists, microscope stays blind
333
375
  return {
334
376
  name: arg.name,