Skip to content

[Bug]: Hook transform cache key ignores export name, causing cross-mapping policy confusion #10152

@coygeek

Description

@coygeek

Summary

loadTransform() caches hook transform functions by modulePath only, but mappings support multiple exports from the same module. The first loaded export is reused for all later mappings, which can silently apply the wrong security policy (for example, safe vs. unsafe transform logic) across different hook routes.

Affected Code

File: src/gateway/hooks-mapping.ts (line 315)

async function loadTransform(transform: HookMappingTransformResolved): Promise<HookTransformFn> {
  const cached = transformCache.get(transform.modulePath);
  if (cached) {
    return cached;
  }
  const url = pathToFileURL(transform.modulePath).href;
  const mod = (await import(url)) as Record<string, unknown>;
  const fn = resolveTransformFn(mod, transform.exportName);
  transformCache.set(transform.modulePath, fn);
  return fn;
}

The cache key on .get() and .set() uses only transform.modulePath, but resolveTransformFn() correctly resolves via transform.exportName. The mismatch means the first export loaded from a module path is returned for all subsequent lookups of that path, regardless of which export was requested.

CVSS Assessment

Metric Value
Score 9.1 / 10.0
Severity Critical
Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L

CVSS v3.1 Calculator

Steps to Reproduce

  1. Configure two hook mappings that both use transform.module: ./hooks/transform.js but with different exports (e.g., export: safeTransform and export: strictTransform).
  2. Send a request that triggers mapping A first, forcing transformCache to store A's export function.
  3. Send a request that should trigger mapping B.
  4. Observe mapping B executing mapping A's transform behavior because cache lookup only uses modulePath.

Recommended Fix

Cache transforms by a composite key that includes both modulePath and exportName (e.g., ${modulePath}::${exportName ?? "default"}). Add regression tests proving that two mappings sharing one module but different exports execute independently.

References

  • CWE: CWE-706 - Use of Incorrectly-Resolved Name or Reference

🤖 Generated with Claude Code

Attack Surface

How is this reached?

  • Network (HTTP/WebSocket endpoint, API call)
  • Adjacent Network (same LAN, requires network proximity)
  • Local (local file, CLI argument, environment variable)
  • Physical (requires physical access to machine)

Authentication required?

  • None (unauthenticated/public access)
  • Low (any authenticated user)
  • High (admin/privileged user only)

Entry point: Hook HTTP routes handled through applyHookMappings() when mappings reuse one transform module with different export values.

Exploit Conditions

Complexity:

  • Low (no special conditions, works reliably)
  • High (requires race condition, specific config, or timing)

User interaction:

  • None (automatic, no victim action needed)
  • Required (victim must click, visit, or perform action)

Prerequisites:

  • Hook mappings include at least two routes referencing the same transform module path with different export functions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstaleMarked as stale due to inactivity

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions