ai-shield-core 0.1.0 → 0.2.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.
Files changed (68) hide show
  1. package/dist/audit/logger.d.ts.map +1 -1
  2. package/dist/audit/logger.js +13 -14
  3. package/dist/audit/types.js +1 -2
  4. package/dist/cache/lru.js +1 -5
  5. package/dist/canary/memory.d.ts +75 -0
  6. package/dist/canary/memory.d.ts.map +1 -0
  7. package/dist/canary/memory.js +194 -0
  8. package/dist/context/wrap-context.d.ts +105 -0
  9. package/dist/context/wrap-context.d.ts.map +1 -0
  10. package/dist/context/wrap-context.js +188 -0
  11. package/dist/cost/anomaly.js +1 -4
  12. package/dist/cost/pricing.d.ts.map +1 -1
  13. package/dist/cost/pricing.js +18 -19
  14. package/dist/cost/tracker.d.ts +19 -1
  15. package/dist/cost/tracker.d.ts.map +1 -1
  16. package/dist/cost/tracker.js +27 -10
  17. package/dist/index.d.ts +31 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +51 -37
  20. package/dist/policy/circuit-breaker.d.ts +70 -0
  21. package/dist/policy/circuit-breaker.d.ts.map +1 -0
  22. package/dist/policy/circuit-breaker.js +376 -0
  23. package/dist/policy/engine.js +1 -5
  24. package/dist/policy/tools.js +4 -8
  25. package/dist/scanner/canary.js +4 -8
  26. package/dist/scanner/chain.js +1 -5
  27. package/dist/scanner/heuristic.d.ts +13 -0
  28. package/dist/scanner/heuristic.d.ts.map +1 -1
  29. package/dist/scanner/heuristic.js +50 -7
  30. package/dist/scanner/ingestion.d.ts +116 -0
  31. package/dist/scanner/ingestion.d.ts.map +1 -0
  32. package/dist/scanner/ingestion.js +452 -0
  33. package/dist/scanner/pii.d.ts.map +1 -1
  34. package/dist/scanner/pii.js +24 -12
  35. package/dist/shield.d.ts.map +1 -1
  36. package/dist/shield.js +34 -26
  37. package/dist/types.d.ts +140 -2
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/types.js +1 -2
  40. package/package.json +4 -3
  41. package/src/audit/logger.ts +6 -1
  42. package/src/canary/memory.ts +259 -0
  43. package/src/context/wrap-context.ts +304 -0
  44. package/src/cost/pricing.ts +13 -9
  45. package/src/cost/tracker.ts +35 -1
  46. package/src/index.ts +82 -1
  47. package/src/policy/circuit-breaker.ts +449 -0
  48. package/src/scanner/heuristic.ts +49 -2
  49. package/src/scanner/ingestion.ts +550 -0
  50. package/src/scanner/pii.ts +21 -7
  51. package/src/shield.ts +15 -2
  52. package/src/types.ts +175 -2
  53. package/tsconfig.json +2 -1
  54. package/dist/audit/logger.js.map +0 -1
  55. package/dist/audit/types.js.map +0 -1
  56. package/dist/cache/lru.js.map +0 -1
  57. package/dist/cost/anomaly.js.map +0 -1
  58. package/dist/cost/pricing.js.map +0 -1
  59. package/dist/cost/tracker.js.map +0 -1
  60. package/dist/index.js.map +0 -1
  61. package/dist/policy/engine.js.map +0 -1
  62. package/dist/policy/tools.js.map +0 -1
  63. package/dist/scanner/canary.js.map +0 -1
  64. package/dist/scanner/chain.js.map +0 -1
  65. package/dist/scanner/heuristic.js.map +0 -1
  66. package/dist/scanner/pii.js.map +0 -1
  67. package/dist/shield.js.map +0 -1
  68. package/dist/types.js.map +0 -1
package/dist/shield.js CHANGED
@@ -1,18 +1,15 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AIShield = void 0;
4
- const chain_js_1 = require("./scanner/chain.js");
5
- const heuristic_js_1 = require("./scanner/heuristic.js");
6
- const pii_js_1 = require("./scanner/pii.js");
7
- const tools_js_1 = require("./policy/tools.js");
8
- const engine_js_1 = require("./policy/engine.js");
9
- const tracker_js_1 = require("./cost/tracker.js");
10
- const logger_js_1 = require("./audit/logger.js");
11
- const lru_js_1 = require("./cache/lru.js");
1
+ import { ScannerChain } from "./scanner/chain.js";
2
+ import { HeuristicScanner } from "./scanner/heuristic.js";
3
+ import { PIIScanner } from "./scanner/pii.js";
4
+ import { ToolPolicyScanner } from "./policy/tools.js";
5
+ import { PolicyEngine } from "./policy/engine.js";
6
+ import { CostTracker } from "./cost/tracker.js";
7
+ import { AuditLogger, ConsoleAuditStore } from "./audit/logger.js";
8
+ import { ScanLRUCache } from "./cache/lru.js";
12
9
  // ============================================================
13
10
  // AIShield — Main class, single entry point
14
11
  // ============================================================
15
- class AIShield {
12
+ export class AIShield {
16
13
  chain;
17
14
  policyEngine;
18
15
  costTracker;
@@ -21,19 +18,19 @@ class AIShield {
21
18
  config;
22
19
  constructor(config = {}) {
23
20
  this.config = config;
24
- this.policyEngine = new engine_js_1.PolicyEngine(config.preset ?? "public_website");
25
- this.chain = new chain_js_1.ScannerChain({ earlyExit: true });
21
+ this.policyEngine = new PolicyEngine(config.preset ?? "public_website");
22
+ this.chain = new ScannerChain({ earlyExit: true });
26
23
  // Build scanner chain based on config
27
24
  this.setupScanners(config);
28
25
  // Cost tracker (optional, needs Redis for distributed use)
29
26
  this.costTracker = config.cost?.enabled !== false && config.cost?.budgets
30
- ? new tracker_js_1.CostTracker(config.cost.budgets)
27
+ ? new CostTracker(config.cost.budgets)
31
28
  : null;
32
29
  // Audit logger (optional)
33
30
  this.auditLogger = this.setupAudit(config);
34
31
  // Scan cache (enabled when cache config is provided)
35
32
  this.scanCache = config.cache && config.cache.enabled !== false
36
- ? new lru_js_1.ScanLRUCache({
33
+ ? new ScanLRUCache({
37
34
  maxSize: config.cache.maxSize,
38
35
  ttlMs: config.cache.ttlMs,
39
36
  })
@@ -41,6 +38,15 @@ class AIShield {
41
38
  }
42
39
  /** Scan input text — the main API */
43
40
  async scan(input, context = {}) {
41
+ // Input-length guard — without this a user-supplied multi-MB prompt would
42
+ // trigger every regex scan on the full buffer, which is O(n) best-case
43
+ // but pathological under ReDoS-prone patterns. 256 KB handles every
44
+ // real chat/tool input we care about with plenty of headroom; override
45
+ // via AI_SHIELD_MAX_INPUT_BYTES when needed.
46
+ const maxInputBytes = Number(process.env.AI_SHIELD_MAX_INPUT_BYTES ?? 262_144);
47
+ if (input.length > maxInputBytes) {
48
+ input = input.slice(0, maxInputBytes);
49
+ }
44
50
  // Apply preset if not set in context
45
51
  if (!context.preset) {
46
52
  context.preset = this.config.preset ?? "public_website";
@@ -59,8 +65,11 @@ class AIShield {
59
65
  const cacheKey = this.buildCacheKey(input, context);
60
66
  this.scanCache.set(cacheKey, result);
61
67
  }
62
- // Log to audit if enabled
63
- if (this.auditLogger) {
68
+ // Log to audit if enabled — but never double-log the same input when
69
+ // a downstream caller re-scans cached content (result.meta.cached is set
70
+ // by the cache hit path above; here it is always false but we guard to
71
+ // be explicit and so subclasses extending scan() stay safe).
72
+ if (this.auditLogger && !result.meta.cached) {
64
73
  void this.auditLogger.log(input, result, context);
65
74
  }
66
75
  return result;
@@ -117,7 +126,7 @@ class AIShield {
117
126
  // 1. Heuristic injection scanner (always on unless explicitly disabled)
118
127
  if (config.injection?.enabled !== false) {
119
128
  const preset = this.policyEngine.getPreset();
120
- this.chain.add(new heuristic_js_1.HeuristicScanner({
129
+ this.chain.add(new HeuristicScanner({
121
130
  strictness: config.injection?.strictness ?? "medium",
122
131
  threshold: config.injection?.threshold ?? preset.injection.threshold,
123
132
  customPatterns: config.injection?.customPatterns?.map((pattern, i) => ({
@@ -131,7 +140,7 @@ class AIShield {
131
140
  }
132
141
  // 2. PII scanner
133
142
  if (config.pii?.enabled !== false) {
134
- this.chain.add(new pii_js_1.PIIScanner({
143
+ this.chain.add(new PIIScanner({
135
144
  action: config.pii?.action ?? this.policyEngine.getPIIAction(),
136
145
  locale: config.pii?.locale,
137
146
  types: config.pii?.types,
@@ -149,7 +158,7 @@ class AIShield {
149
158
  this.policyEngine.getMaxToolChainDepth(),
150
159
  },
151
160
  };
152
- this.chain.add(new tools_js_1.ToolPolicyScanner(toolPolicy, config.tools.manifestPins));
161
+ this.chain.add(new ToolPolicyScanner(toolPolicy, config.tools.manifestPins));
153
162
  }
154
163
  }
155
164
  setupAudit(config) {
@@ -158,27 +167,26 @@ class AIShield {
158
167
  let store;
159
168
  switch (config.audit?.store) {
160
169
  case "console":
161
- store = new logger_js_1.ConsoleAuditStore();
170
+ store = new ConsoleAuditStore();
162
171
  break;
163
172
  case "postgresql":
164
173
  // PostgreSQL store would be imported separately to keep core lightweight
165
174
  // For now, fall through to console
166
- store = new logger_js_1.ConsoleAuditStore();
175
+ store = new ConsoleAuditStore();
167
176
  break;
168
177
  case "memory":
169
178
  default:
170
179
  // If no store configured and audit not explicitly enabled, skip
171
180
  if (!config.audit?.store && config.audit?.enabled !== true)
172
181
  return null;
173
- store = new logger_js_1.ConsoleAuditStore();
182
+ store = new ConsoleAuditStore();
174
183
  break;
175
184
  }
176
- return new logger_js_1.AuditLogger({
185
+ return new AuditLogger({
177
186
  store,
178
187
  batchSize: config.audit?.batchSize,
179
188
  flushIntervalMs: config.audit?.flushIntervalMs,
180
189
  });
181
190
  }
182
191
  }
183
- exports.AIShield = AIShield;
184
192
  //# sourceMappingURL=shield.js.map
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type ScanDecision = "allow" | "warn" | "block";
2
- export type ViolationType = "prompt_injection" | "pii_detected" | "tool_denied" | "tool_rate_limit" | "budget_exceeded" | "content_policy" | "manifest_drift";
2
+ export type ViolationType = "prompt_injection" | "pii_detected" | "tool_denied" | "tool_rate_limit" | "budget_exceeded" | "content_policy" | "manifest_drift" | "ingested_injection" | "untrusted_instruction" | "memory_poisoning" | "circuit_breaker_open" | "blast_radius_exceeded";
3
3
  export interface Violation {
4
4
  type: ViolationType;
5
5
  scanner: string;
@@ -29,16 +29,154 @@ export interface Scanner {
29
29
  name: string;
30
30
  scan(input: string, context: ScanContext): Promise<ScannerResult>;
31
31
  }
32
+ /**
33
+ * Where a piece of content came from. Determines how aggressively the
34
+ * ingestion scanner treats instruction-like patterns.
35
+ *
36
+ * - `user` — Direct user message. Treat normally.
37
+ * - `rag` — Retrieved document chunk (vector store, knowledge base).
38
+ * Instructions here are almost always an indirect-injection
39
+ * attempt.
40
+ * - `tool-desc` — MCP tool description / OpenAI function schema / tool args
41
+ * that came from a remote MCP server. High-risk vector
42
+ * per Lakera 2026 advisory + OX Security MCP CVEs.
43
+ * - `memory` — Persisted memory entry (knowledge graph, session
44
+ * history, vector memory). Subject to persistence-poisoning.
45
+ * - `web` — Scraped web page / HTML. Hidden-instruction risk via
46
+ * HTML comments, CSS-hidden text, unicode tricks.
47
+ * - `agent-output`— Output from another agent flowing into this one
48
+ * (multi-agent contagion).
49
+ */
50
+ export type IngestionSource = "user" | "rag" | "tool-desc" | "memory" | "web" | "agent-output";
51
+ /**
52
+ * Privilege tier of a content segment. The toolkit treats instructions
53
+ * coming from `untrusted` sources differently from those in `system`
54
+ * or `trusted` segments. See `wrapContext()`.
55
+ */
56
+ export type TrustTier = "system" | "trusted" | "untrusted";
32
57
  export interface ScanContext {
33
58
  agentId?: string;
34
59
  sessionId?: string;
35
60
  userId?: string;
36
- userType?: "lead" | "agency" | "customer" | "internal";
61
+ userType?: string;
37
62
  locale?: string;
38
63
  preset?: PresetName;
39
64
  tools?: ToolCall[];
65
+ /**
66
+ * Provenance of the content being scanned. Defaults to `"user"` when
67
+ * unset. Set to `"rag"`/`"tool-desc"`/`"memory"`/`"web"`/`"agent-output"`
68
+ * to engage indirect-injection heuristics.
69
+ */
70
+ source?: IngestionSource;
71
+ /**
72
+ * Privilege tier of the content. Defaults inferred from `source`:
73
+ * `user` → untrusted, everything else → untrusted, `system` only when
74
+ * explicitly set via `wrapContext()`.
75
+ */
76
+ trustTier?: TrustTier;
40
77
  }
41
78
  export type PresetName = "public_website" | "internal_support" | "ops_agent";
79
+ /**
80
+ * A single content segment with provenance + trust tagging.
81
+ * Produced by `wrapContext()`, consumed by `assemblePrompt()` and
82
+ * any policy that needs to enforce tier boundaries.
83
+ */
84
+ export interface ContextSegment {
85
+ /** Source of the segment. */
86
+ source: IngestionSource;
87
+ /** Privilege tier. */
88
+ trust: TrustTier;
89
+ /** Raw text content. */
90
+ content: string;
91
+ /** Optional human-readable origin label (e.g. "wikipedia.org/wiki/X"). */
92
+ label?: string;
93
+ /** Optional SHA-256 content hash for poisoning detection. */
94
+ contentHash?: string;
95
+ }
96
+ export interface WrappedContext {
97
+ /** All segments in the order they were added. */
98
+ segments: ContextSegment[];
99
+ /** Per-segment scan results (filled by `scanWrappedContext()`). */
100
+ scanResults?: Array<{
101
+ segmentIndex: number;
102
+ decision: ScanDecision;
103
+ violations: Violation[];
104
+ }>;
105
+ /** Aggregate decision across all segments. */
106
+ decision?: ScanDecision;
107
+ }
108
+ /**
109
+ * A memory entry sealed with a content hash + sentinel canary.
110
+ * `verifyMemoryCanary()` re-derives the hash on read and flags
111
+ * silent mutation.
112
+ */
113
+ export interface MemoryCanaryEntry {
114
+ /** Stable identifier of the memory entry (e.g. "fact:12345"). */
115
+ id: string;
116
+ /** Original content at write time. */
117
+ content: string;
118
+ /** SHA-256(`${id}\0${content}\0${canaryToken}`). */
119
+ contentHash: string;
120
+ /** Random sentinel token. Used to bind verification to this write. */
121
+ canaryToken: string;
122
+ /** When the canary was minted. */
123
+ createdAt: Date;
124
+ /** Optional tenant scope so leaks across tenants are detectable. */
125
+ tenantId?: string;
126
+ }
127
+ export interface MemoryCanaryVerification {
128
+ valid: boolean;
129
+ reason?: "content_mutated" | "canary_missing" | "tenant_mismatch" | "hash_mismatch";
130
+ /** When invalid: the mutated content actually read. */
131
+ observed?: string;
132
+ }
133
+ export type CircuitState = "closed" | "open" | "half-open";
134
+ export interface CircuitBreakerConfig {
135
+ /** Logical tool name this breaker applies to. */
136
+ tool: string;
137
+ /** Optional sub-scope (per-agent, per-session, per-tenant). */
138
+ scope?: string;
139
+ /** Anomaly count that trips the breaker. Default: 5. */
140
+ failureThreshold?: number;
141
+ /** Rolling window in ms for the failure count. Default: 60_000. */
142
+ windowMs?: number;
143
+ /** How long the breaker stays open after tripping (ms). Default: 60_000. */
144
+ cooldownMs?: number;
145
+ /** Max calls per `windowMs`. Default: unlimited. */
146
+ maxCallsPerWindow?: number;
147
+ /** Max "destructive" calls per `windowMs`. Default: unlimited. */
148
+ maxWritesPerWindow?: number;
149
+ /** Optional human-in-the-loop confirmation hook. */
150
+ onDestructive?: (info: {
151
+ tool: string;
152
+ scope?: string;
153
+ context: ScanContext;
154
+ }) => boolean | Promise<boolean>;
155
+ /** Treat this tool as destructive (default: inferred via wildcard list). */
156
+ isDestructive?: boolean;
157
+ }
158
+ export interface CircuitBreakerDecision {
159
+ /** Whether the call may proceed. */
160
+ allowed: boolean;
161
+ /** Current state. */
162
+ state: CircuitState;
163
+ /** Reason if `allowed = false`. */
164
+ reason?: "circuit_open" | "rate_limit" | "blast_radius_exceeded" | "hitl_denied";
165
+ /** Detail for logs. */
166
+ message?: string;
167
+ /** Suggested retry-after in ms when state = open. */
168
+ retryAfterMs?: number;
169
+ }
170
+ /**
171
+ * Minimal counter store usable by the circuit breaker.
172
+ * Compatible subset of `ioredis` so existing
173
+ * `cost.tracker.RedisLike` deployments can be reused.
174
+ */
175
+ export interface CounterStoreLike {
176
+ get(key: string): Promise<string | null>;
177
+ incrbyfloat(key: string, increment: number): Promise<string>;
178
+ expire(key: string, seconds: number): Promise<number>;
179
+ }
42
180
  export type PIIType = "email" | "phone" | "iban" | "credit_card" | "german_tax_id" | "german_personal_id" | "german_social_security" | "ip_address" | "url_with_credentials";
43
181
  export type PIIAction = "block" | "mask" | "tokenize" | "allow";
44
182
  export interface PIIEntity {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -1,6 +1,5 @@
1
- "use strict";
2
1
  // ============================================================
3
2
  // AI Shield Core Types
4
3
  // ============================================================
5
- Object.defineProperty(exports, "__esModule", { value: true });
4
+ export {};
6
5
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "ai-shield-core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
+ "type": "module",
4
5
  "description": "LLM Security SDK — Prompt Injection Detection, PII Protection, Cost Control, Audit",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
@@ -11,8 +12,8 @@
11
12
  }
12
13
  },
13
14
  "scripts": {
14
- "build": "tsc",
15
- "typecheck": "tsc --noEmit"
15
+ "build": "tsc -b",
16
+ "typecheck": "tsc -b"
16
17
  },
17
18
  "dependencies": {},
18
19
  "peerDependencies": {
@@ -28,6 +28,11 @@ export class AuditLogger {
28
28
  this.flushTimer = setInterval(() => {
29
29
  void this.flush();
30
30
  }, flushMs);
31
+ // Allow the Node process to exit even if the timer is pending —
32
+ // clean shutdown should go through close(), not rely on the timer.
33
+ if (typeof this.flushTimer.unref === "function") {
34
+ this.flushTimer.unref();
35
+ }
31
36
  }
32
37
 
33
38
  /** Log a scan result */
@@ -48,7 +53,7 @@ export class AuditLogger {
48
53
  sessionId: context.sessionId,
49
54
  agentId: context.agentId,
50
55
  userIdHash: context.userId
51
- ? createHash("sha256").update(context.userId).digest("hex").substring(0, 16)
56
+ ? createHash("sha256").update(context.userId).digest("hex").substring(0, 32)
52
57
  : undefined,
53
58
  requestType: context.tools?.length ? "tool_call" : "chat",
54
59
  inputHash: createHash("sha256").update(input).digest("hex"),
@@ -0,0 +1,259 @@
1
+ import { randomBytes, createHash, timingSafeEqual } from "node:crypto";
2
+ import type {
3
+ MemoryCanaryEntry,
4
+ MemoryCanaryVerification,
5
+ } from "../types.js";
6
+
7
+ // ============================================================
8
+ // Memory Canary — Persistence-Poisoning Detection
9
+ //
10
+ // Existing canary tokens defend the *system prompt*: inject a sentinel
11
+ // + check the response for leakage. That works for prompt-extraction
12
+ // attacks but does nothing for the broader class of *persistence
13
+ // poisoning*: an attacker mutates a stored memory entry, knowledge-graph
14
+ // fact, or RAG document so that the next retrieval steers the model.
15
+ //
16
+ // MemoryCanary seals each write with:
17
+ // 1. A random sentinel token bound to the entry (`canaryToken`).
18
+ // 2. A SHA-256 hash over `id || content || canaryToken` so any silent
19
+ // mutation of `content` between write and read is detectable.
20
+ // 3. Optional `tenantId` binding so a cross-tenant leak surfaces as
21
+ // an explicit `tenant_mismatch` reason.
22
+ //
23
+ // Storage of the canary metadata is the caller's responsibility — the
24
+ // returned `MemoryCanaryEntry` is JSON-serialisable and meant to be
25
+ // persisted alongside the memory entry (separate column / sidecar key).
26
+ //
27
+ // The library does not store secrets and does not need a key. The
28
+ // security property is integrity (mutation detection), not
29
+ // confidentiality.
30
+ // ============================================================
31
+
32
+ /** Minimum allowed canary-token length in bytes (= 16 hex chars). */
33
+ const MIN_TOKEN_BYTES = 8;
34
+ /** Default canary-token length: 16 random bytes -> 32 hex chars. */
35
+ const DEFAULT_TOKEN_BYTES = 16;
36
+
37
+ export interface MintMemoryCanaryOptions {
38
+ /**
39
+ * Override token byte length. Minimum 8 (16 hex chars). Default 16.
40
+ * Longer = stronger guessing resistance, but the hex string lives in
41
+ * sidecar storage so the overhead is negligible.
42
+ */
43
+ tokenBytes?: number;
44
+ }
45
+
46
+ /**
47
+ * Mint a canary for a memory entry. Call at write time, persist the
48
+ * returned `MemoryCanaryEntry` alongside (or in place of) the raw entry.
49
+ *
50
+ * @param id Stable identifier of the memory entry.
51
+ * @param content The content being stored.
52
+ * @param tenantId Optional tenant scope.
53
+ */
54
+ export function mintMemoryCanary(
55
+ id: string,
56
+ content: string,
57
+ tenantId?: string,
58
+ options: MintMemoryCanaryOptions = {},
59
+ ): MemoryCanaryEntry {
60
+ if (typeof id !== "string" || id.length === 0) {
61
+ throw new TypeError("mintMemoryCanary: 'id' must be a non-empty string");
62
+ }
63
+ if (typeof content !== "string") {
64
+ throw new TypeError("mintMemoryCanary: 'content' must be a string");
65
+ }
66
+ const tokenBytes = Math.max(
67
+ MIN_TOKEN_BYTES,
68
+ options.tokenBytes ?? DEFAULT_TOKEN_BYTES,
69
+ );
70
+ const canaryToken = randomBytes(tokenBytes).toString("hex");
71
+ const contentHash = computeHash(id, content, canaryToken, tenantId);
72
+
73
+ return {
74
+ id,
75
+ content,
76
+ contentHash,
77
+ canaryToken,
78
+ createdAt: new Date(),
79
+ tenantId,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Verify a previously minted canary against the content read back from
85
+ * storage. Returns `valid: true` only if both the content matches what
86
+ * was sealed and the tenant binding (if any) matches.
87
+ *
88
+ * Use case 1 — mutation detection:
89
+ * ```ts
90
+ * const entry = mintMemoryCanary("fact:42", "Sky is blue.");
91
+ * await db.write({ ...entry });
92
+ *
93
+ * // ... later ...
94
+ * const stored = await db.read("fact:42");
95
+ * const ver = verifyMemoryCanary(stored, stored.content);
96
+ * if (!ver.valid) {
97
+ * logger.security("Memory poisoning suspected", { id, reason: ver.reason });
98
+ * }
99
+ * ```
100
+ *
101
+ * Use case 2 — cross-tenant leak detection:
102
+ * ```ts
103
+ * // tenant A reads what should be a tenant-B-only entry
104
+ * const ver = verifyMemoryCanary(entry, entry.content, { tenantId: "tenant-A" });
105
+ * // ver.reason === "tenant_mismatch"
106
+ * ```
107
+ */
108
+ export function verifyMemoryCanary(
109
+ entry: MemoryCanaryEntry,
110
+ observedContent: string,
111
+ options: { tenantId?: string } = {},
112
+ ): MemoryCanaryVerification {
113
+ if (!entry || typeof entry !== "object") {
114
+ return { valid: false, reason: "canary_missing" };
115
+ }
116
+ if (
117
+ typeof entry.canaryToken !== "string" ||
118
+ entry.canaryToken.length < MIN_TOKEN_BYTES * 2
119
+ ) {
120
+ return { valid: false, reason: "canary_missing" };
121
+ }
122
+ if (typeof observedContent !== "string") {
123
+ return { valid: false, reason: "content_mutated" };
124
+ }
125
+ // Tenant binding is fail-closed. If the entry was minted with a
126
+ // tenantId, the caller MUST supply the same tenantId. A caller that
127
+ // omits `options.tenantId` against a tenant-bound entry surfaces as
128
+ // a leak rather than a silent pass (Critic C2 from round 1 review).
129
+ if (entry.tenantId !== undefined) {
130
+ if (
131
+ options.tenantId === undefined ||
132
+ options.tenantId !== entry.tenantId
133
+ ) {
134
+ return {
135
+ valid: false,
136
+ reason: "tenant_mismatch",
137
+ observed: observedContent,
138
+ };
139
+ }
140
+ } else if (
141
+ options.tenantId !== undefined &&
142
+ entry.tenantId !== options.tenantId
143
+ ) {
144
+ // Caller passed a tenantId but the entry has none — also a mismatch.
145
+ return {
146
+ valid: false,
147
+ reason: "tenant_mismatch",
148
+ observed: observedContent,
149
+ };
150
+ }
151
+
152
+ const expectedHash = computeHash(
153
+ entry.id,
154
+ observedContent,
155
+ entry.canaryToken,
156
+ entry.tenantId,
157
+ );
158
+ if (!hashesEqual(expectedHash, entry.contentHash)) {
159
+ return {
160
+ valid: false,
161
+ reason:
162
+ observedContent === entry.content ? "hash_mismatch" : "content_mutated",
163
+ observed: observedContent,
164
+ };
165
+ }
166
+ return { valid: true };
167
+ }
168
+
169
+ /**
170
+ * Re-mint a canary after legitimate content edit. Returns a new
171
+ * sealed entry that supersedes the old one. The old `canaryToken`
172
+ * is rotated so a replay of the previous hash is also invalidated.
173
+ */
174
+ export function rotateMemoryCanary(
175
+ prev: MemoryCanaryEntry,
176
+ newContent: string,
177
+ ): MemoryCanaryEntry {
178
+ return mintMemoryCanary(prev.id, newContent, prev.tenantId);
179
+ }
180
+
181
+ /**
182
+ * Inject a *sentinel* memory entry — a decoy fact whose mutation would
183
+ * indicate the store was tampered with. Pair with `findSentinelMutations()`
184
+ * in a periodic sweep over the memory store.
185
+ *
186
+ * Returns a `MemoryCanaryEntry` with deterministic content that callers
187
+ * can recognise. The content includes the canary token so even content
188
+ * inspection (not just hash compare) catches mutation.
189
+ */
190
+ export function buildSentinelEntry(
191
+ scope: string,
192
+ tenantId?: string,
193
+ ): MemoryCanaryEntry {
194
+ // ID combines timestamp (for ordering) + random suffix (so enumeration
195
+ // by approximate-time guessing is infeasible — Critic M2 round 1).
196
+ const idSuffix = randomBytes(4).toString("hex");
197
+ const id = `ai-shield-sentinel:${scope}:${Date.now().toString(36)}-${idSuffix}`;
198
+ const nonce = randomBytes(8).toString("hex");
199
+ const content = `[AI-Shield sentinel — do not modify] scope=${scope} nonce=${nonce}`;
200
+ return mintMemoryCanary(id, content, tenantId);
201
+ }
202
+
203
+ /**
204
+ * Bulk verify a set of stored entries against their canaries. Returns
205
+ * the IDs that failed verification, with the reason.
206
+ */
207
+ export function bulkVerify(
208
+ entries: Array<{
209
+ canary: MemoryCanaryEntry;
210
+ observedContent: string;
211
+ expectedTenantId?: string;
212
+ }>,
213
+ ): Array<{ id: string; reason: NonNullable<MemoryCanaryVerification["reason"]> }> {
214
+ const failures: Array<{
215
+ id: string;
216
+ reason: NonNullable<MemoryCanaryVerification["reason"]>;
217
+ }> = [];
218
+ for (const e of entries) {
219
+ const v = verifyMemoryCanary(e.canary, e.observedContent, {
220
+ tenantId: e.expectedTenantId,
221
+ });
222
+ if (!v.valid && v.reason) {
223
+ failures.push({ id: e.canary.id, reason: v.reason });
224
+ }
225
+ }
226
+ return failures;
227
+ }
228
+
229
+ // --- Internal helpers ---
230
+
231
+ function computeHash(
232
+ id: string,
233
+ content: string,
234
+ token: string,
235
+ tenantId?: string,
236
+ ): string {
237
+ return createHash("sha256")
238
+ .update(id)
239
+ .update("\0")
240
+ .update(content)
241
+ .update("\0")
242
+ .update(token)
243
+ .update("\0")
244
+ .update(tenantId ?? "")
245
+ .digest("hex");
246
+ }
247
+
248
+ /**
249
+ * Constant-time hex string compare. Falls back to a non-timing-safe
250
+ * direct compare only when lengths differ (which is itself the leak
251
+ * we want surfaced as a `false`, so this is fine).
252
+ */
253
+ function hashesEqual(a: string, b: string): boolean {
254
+ if (typeof a !== "string" || typeof b !== "string") return false;
255
+ if (a.length !== b.length) return false;
256
+ // Safe — both buffers have identical length, both come from
257
+ // hex(SHA-256), no untrusted input.
258
+ return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
259
+ }