agent-sanitizer 2.55.0 → 2.56.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.
package/README.md CHANGED
@@ -164,7 +164,12 @@ buy you:
164
164
  3. Look-alike glyphs in tool inputs are folded to ASCII, so a Cyrillic `а` can't
165
165
  walk a command past a deny rule.
166
166
  4. Tool output has invisible characters and terminal escapes stripped, hidden
167
- HTML spliced out with a placeholder, and exfil-shaped URLs flagged.
167
+ HTML spliced out with a placeholder, and exfil-shaped URLs flagged. A hex
168
+ value one digest wide is exempt under a generic query or fragment parameter
169
+ name, because a commit or blob id in a link is ordinary — under a name that
170
+ already says credential it still flags, and a path segment never reaches the
171
+ exemption at all. `AGENT_SANITIZER_FLAG_DIGEST_VALUES=1` turns it off for a
172
+ consumer that reads such a value as a leak.
168
173
  5. With `AGENT_SANITIZER_SECRETS_ENABLED=1` set, secrets in tool output are
169
174
  redacted locally by `detect-secrets` — the engine ships with the plugin and
170
175
  provisions itself on first run, no further setup from you.
@@ -238,6 +238,24 @@ export function secretsEnabled(env = process.env) {
238
238
  return env[SECRETS_ENABLED_ENV] === "1";
239
239
  }
240
240
 
241
+ // Layer 3's digest exemption, which lets a URL carry an exact-digest-length hex
242
+ // value without being read as a payload. A consumer that treats a digest in a
243
+ // URL as exfil — a monitor reading tool output for a leaked commit or blob id —
244
+ // turns the exemption off here. `checkExfilUrl` takes the same option directly;
245
+ // this is the switch for the hook path, which composes its own seam options.
246
+ export const FLAG_DIGEST_VALUES_ENV = "AGENT_SANITIZER_FLAG_DIGEST_VALUES";
247
+
248
+ /**
249
+ * True when the operator opted into flagging digest-shaped URL values. `=== "1"`
250
+ * for the reason {@link secretsEnabled} gives: a typo fails toward the default,
251
+ * which is the exemption the precision rule prefers.
252
+ * @param {NodeJS.ProcessEnv | Record<string, string | undefined>} [env]
253
+ * @returns {boolean}
254
+ */
255
+ export function digestFlaggingEnabled(env = process.env) {
256
+ return env[FLAG_DIGEST_VALUES_ENV] === "1";
257
+ }
258
+
241
259
  /**
242
260
  * The env-bound redaction set: the UNION of the inference keys, the curated host
243
261
  * credentials, any credential-shaped var present in the environment, the
@@ -49,7 +49,7 @@ import { registerFaultPolicy, hookFaultOutcome } from "./lib/hook-fault.mjs";
49
49
  import { controlPlane, runJudgeCli } from "./lib/control-plane.mjs";
50
50
  import { bestEffortTrace, trace, TraceEvent } from "./lib/trace.mjs";
51
51
  import { hasEnvBoundSecret } from "./lib/secret-annotate.mjs";
52
- import { secretsEnabled } from "./lib/env-config.mjs";
52
+ import { digestFlaggingEnabled, secretsEnabled } from "./lib/env-config.mjs";
53
53
  import {
54
54
  persistReveal,
55
55
  persistSpan,
@@ -267,6 +267,10 @@ export async function sanitizeText(
267
267
  const seamOptions = {
268
268
  html,
269
269
  exfilScan: webIngress,
270
+ // Widens Layer 3 only, and only where the operator asked: without it the
271
+ // option the seam already accepts is unreachable from this entry, so a hook
272
+ // consumer that needs a digest in a URL flagged has no way to ask for it.
273
+ flagDigestValues: digestFlaggingEnabled(),
270
274
  sgrCarveOut: !webIngress,
271
275
  deadline,
272
276
  // Layer 4 — OPT-IN (secretsEnabled): with the knob unset the seam gets no
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-sanitizer",
3
- "version": "2.55.0",
3
+ "version": "2.56.0",
4
4
  "description": "Defend an agent against hidden-content injection: strip payload-capable invisible Unicode and ANSI, splice out human-invisible HTML, and flag data-exfil URLs in untrusted text before any model sees it.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -67,6 +67,14 @@ export function extraSecretVars(env?: Record<string, string | undefined>): strin
67
67
  * @returns {boolean}
68
68
  */
69
69
  export function secretsEnabled(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): boolean;
70
+ /**
71
+ * True when the operator opted into flagging digest-shaped URL values. `=== "1"`
72
+ * for the reason {@link secretsEnabled} gives: a typo fails toward the default,
73
+ * which is the exemption the precision rule prefers.
74
+ * @param {NodeJS.ProcessEnv | Record<string, string | undefined>} [env]
75
+ * @returns {boolean}
76
+ */
77
+ export function digestFlaggingEnabled(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): boolean;
70
78
  /**
71
79
  * The env-bound redaction set: the UNION of the inference keys, the curated host
72
80
  * credentials, any credential-shaped var present in the environment, the
@@ -79,3 +87,4 @@ export function secretsEnabled(env?: NodeJS.ProcessEnv | Record<string, string |
79
87
  */
80
88
  export function envBoundSecretVars(env?: Record<string, string | undefined>): string[];
81
89
  export const SECRETS_ENABLED_ENV: "AGENT_SANITIZER_SECRETS_ENABLED";
90
+ export const FLAG_DIGEST_VALUES_ENV: "AGENT_SANITIZER_FLAG_DIGEST_VALUES";