@clear-capabilities/agentic-security-scanner 0.195.23 → 0.195.24

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.
@@ -1,4 +1,4 @@
1
- import { hashState, emptyState, addIdentity } from './field-identity.js';
1
+ import { hashState, emptyState, addIdentity, joinStates } from './field-identity.js';
2
2
  import {
3
3
  resolveExprIdentities, residualFlat, analyzeFunctionFieldIdentity,
4
4
  argumentContext, callInvocationId, calleeRecorder,
@@ -425,7 +425,8 @@ export function applyAtCallSite(summary, paramNames, callArgs) {
425
425
  // integration will pass a resolver backed by `scanner/src/ir/callgraph.js`
426
426
  // instead, without this function (or `resolveExprIdentities`) needing to
427
427
  // change at all.
428
- export function createCallSummaryResolver(cache, lookupCallee) {
428
+ export function createCallSummaryResolver(cache, lookupCallee, resolverOpts = {}) {
429
+ const seedEntryState = typeof resolverOpts.seedEntryState === 'function' ? resolverOpts.seedEntryState : null;
429
430
  return function resolveCallSummary(calleeExpr, callArgs, callerState, ctx) {
430
431
  const resolved = lookupCallee(calleeExpr);
431
432
  if (!resolved) return null;
@@ -435,7 +436,18 @@ export function createCallSummaryResolver(cache, lookupCallee) {
435
436
  // own header comment) — this call site forwards the caller's `ctx`
436
437
  // unmodified; the hazard cannot reappear here because the strip
437
438
  // happens one level down, not at each call site.
438
- const entryState = entryStateFromCall(fn.params, callArgs, callerState, ctx);
439
+ const boundState = entryStateFromCall(fn.params, callArgs, callerState, ctx);
440
+ // GA-03.1 (0.195.24): the callee's OWN source seeds are facts about
441
+ // expressions in its body (`req.body.card_number` is a source wherever
442
+ // `extractCard` is called from), so they hold under every entry context,
443
+ // not only the driver's top-level one. Join them onto the call-derived
444
+ // state. The cache key is the JOINED state, so a clean call site and the
445
+ // driver's own analysis of the callee share one key and one summary;
446
+ // DESIGN_GRAPH_BUILDER.md §3.6's rule stands: no seeded summary is ever
447
+ // stored under the empty-entry key. The bind hops below enumerate only
448
+ // the call-derived half (`boundState`): a seed is not an argument.
449
+ const ownSeeds = seedEntryState ? seedEntryState(fn) : null;
450
+ const entryState = ownSeeds ? joinStates(boundState, ownSeeds) : boundState;
439
451
  // §13.2's first half: the callee's own entry context, computed once so
440
452
  // both the bind hop below and the return-direction wrapper at the
441
453
  // bottom of this function can reference the exact same value.
@@ -454,7 +466,7 @@ export function createCallSummaryResolver(cache, lookupCallee) {
454
466
  // C4 would collide this binding's target with any caller-local
455
467
  // variable of the same name (Decision 5's bug class).
456
468
  if (ctx?.recordHop) {
457
- for (const [path, ids] of entryState) {
469
+ for (const [path, ids] of boundState) {
458
470
  for (const id of ids) {
459
471
  ctx.recordHop({
460
472
  kind: 'write-out', subKind: 'call-arg-bind',
@@ -508,6 +520,10 @@ export function createCallSummaryResolver(cache, lookupCallee) {
508
520
  ...(ctx.correlationVersion === 2 ? { correlationVersion: 2 } : null),
509
521
  }
510
522
  : { resolveCallSummary };
523
+ // GA-03.1 (0.195.24): the callee's after-node seeds are body facts
524
+ // too (source-seeding.js, SEED_KINDS); the hook is keyed by qid, so
525
+ // forwarding the caller's is exact.
526
+ if (ctx?.seedAfterNode) calleeCtx.seedAfterNode = ctx.seedAfterNode;
511
527
  const result = analyzeFunctionFieldIdentity(fn, es, calleeCtx);
512
528
  return summaryFromAnalysisResult(result);
513
529
  });
@@ -524,7 +540,7 @@ export function createCallSummaryResolver(cache, lookupCallee) {
524
540
  // under §2.2's rule (or, when there is no path-shaped argument, as
525
541
  // edge-forming in its own right) — never dropped either way.
526
542
  if (summary?.degradedReason && ctx?.recordHop) {
527
- for (const [, ids] of entryState) {
543
+ for (const [, ids] of boundState) {
528
544
  for (const id of ids) {
529
545
  ctx.recordHop({
530
546
  kind: 'production', subKind: 'call-resolved',