agent-inspect 6.29.0 → 6.29.1

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 (44) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/docs/CLI.md +19 -1
  4. package/docs/LIMITATIONS.md +1 -1
  5. package/docs/SAFE-TRACE-SHARING.md +1 -1
  6. package/docs/STANDARDS.md +1 -1
  7. package/package.json +1 -1
  8. package/packages/cli/dist/{chunk-D7PT6TXM.mjs → chunk-UK4HD5M3.mjs} +126 -26
  9. package/packages/cli/dist/chunk-UK4HD5M3.mjs.map +1 -0
  10. package/packages/cli/dist/index.cjs +196 -97
  11. package/packages/cli/dist/index.cjs.map +1 -1
  12. package/packages/cli/dist/index.mjs +63 -72
  13. package/packages/cli/dist/index.mjs.map +1 -1
  14. package/packages/cli/dist/{src-QONEBW2F.mjs → src-CMPIJGUE.mjs} +3 -3
  15. package/packages/cli/dist/{src-QONEBW2F.mjs.map → src-CMPIJGUE.mjs.map} +1 -1
  16. package/packages/core/dist/advanced.cjs +100 -24
  17. package/packages/core/dist/advanced.cjs.map +1 -1
  18. package/packages/core/dist/advanced.d.cts +11 -0
  19. package/packages/core/dist/advanced.d.ts +11 -0
  20. package/packages/core/dist/advanced.mjs +109 -33
  21. package/packages/core/dist/advanced.mjs.map +1 -1
  22. package/packages/core/dist/checks.cjs +225 -33
  23. package/packages/core/dist/checks.cjs.map +1 -1
  24. package/packages/core/dist/checks.mjs +3 -3
  25. package/packages/core/dist/{chunk-TNOPL75V.mjs → chunk-5RF7FYGL.mjs} +3 -3
  26. package/packages/core/dist/{chunk-TNOPL75V.mjs.map → chunk-5RF7FYGL.mjs.map} +1 -1
  27. package/packages/core/dist/{chunk-3B24355S.mjs → chunk-65LFRW2T.mjs} +3 -3
  28. package/packages/core/dist/{chunk-3B24355S.mjs.map → chunk-65LFRW2T.mjs.map} +1 -1
  29. package/packages/core/dist/{chunk-TIGBAVED.mjs → chunk-AQMJFKI6.mjs} +4 -4
  30. package/packages/core/dist/{chunk-TIGBAVED.mjs.map → chunk-AQMJFKI6.mjs.map} +1 -1
  31. package/packages/core/dist/{chunk-MVRZ7JBH.mjs → chunk-COZPMOUF.mjs} +3 -3
  32. package/packages/core/dist/{chunk-MVRZ7JBH.mjs.map → chunk-COZPMOUF.mjs.map} +1 -1
  33. package/packages/core/dist/{chunk-LGTPSHH2.mjs → chunk-GPGBKN7W.mjs} +228 -36
  34. package/packages/core/dist/chunk-GPGBKN7W.mjs.map +1 -0
  35. package/packages/core/dist/exporters.cjs +10 -5
  36. package/packages/core/dist/exporters.cjs.map +1 -1
  37. package/packages/core/dist/exporters.d.cts +12 -2
  38. package/packages/core/dist/exporters.d.ts +12 -2
  39. package/packages/core/dist/exporters.mjs +13 -8
  40. package/packages/core/dist/exporters.mjs.map +1 -1
  41. package/packages/core/dist/index.mjs +4 -4
  42. package/packages/core/dist/writers.mjs +1 -1
  43. package/packages/cli/dist/chunk-D7PT6TXM.mjs.map +0 -1
  44. package/packages/core/dist/chunk-LGTPSHH2.mjs.map +0 -1
@@ -165,8 +165,31 @@ var init_redactor = __esm({
165
165
  });
166
166
 
167
167
  // packages/core/src/safety/credential-value-patterns.ts
168
+ function stringContainsHighConfidenceCredential(value) {
169
+ for (const pattern of HIGH_CONFIDENCE_CREDENTIAL_PATTERNS) {
170
+ pattern.lastIndex = 0;
171
+ if (pattern.test(value)) {
172
+ pattern.lastIndex = 0;
173
+ return true;
174
+ }
175
+ }
176
+ return false;
177
+ }
178
+ var KEY_VALUE_SECRET_PATTERN, HIGH_CONFIDENCE_CREDENTIAL_PATTERNS;
168
179
  var init_credential_value_patterns = __esm({
169
180
  "packages/core/src/safety/credential-value-patterns.ts"() {
181
+ KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i;
182
+ HIGH_CONFIDENCE_CREDENTIAL_PATTERNS = [
183
+ /^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
184
+ /\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
185
+ /\b[a-z0-9_.-]+=[^;\s]+(?:;\s*[a-z0-9_.-]+=[^;\s]+)+/i,
186
+ /\beyJ[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\b/,
187
+ /\b(?:sk-(?:proj-)?[a-zA-Z0-9_-]{16,}|sk-ant-[a-zA-Z0-9_-]{16,}|AIza[0-9A-Za-z_-]{20,})\b/,
188
+ /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\b/,
189
+ /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
190
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*-----END [A-Z ]*PRIVATE KEY-----/,
191
+ KEY_VALUE_SECRET_PATTERN
192
+ ];
170
193
  }
171
194
  });
172
195
 
@@ -6972,9 +6995,16 @@ var init_control_rules = __esm({
6972
6995
  }
6973
6996
  });
6974
6997
 
6998
+ // packages/core/src/checks/canonical-equality.ts
6999
+ var init_canonical_equality = __esm({
7000
+ "packages/core/src/checks/canonical-equality.ts"() {
7001
+ }
7002
+ });
7003
+
6975
7004
  // packages/core/src/checks/tool-arguments.ts
6976
7005
  var init_tool_arguments = __esm({
6977
7006
  "packages/core/src/checks/tool-arguments.ts"() {
7007
+ init_canonical_equality();
6978
7008
  }
6979
7009
  });
6980
7010
 
@@ -6989,6 +7019,7 @@ var init_retry_safety = __esm({
6989
7019
  // packages/core/src/checks/recovery-operations.ts
6990
7020
  var init_recovery_operations = __esm({
6991
7021
  "packages/core/src/checks/recovery-operations.ts"() {
7022
+ init_canonical_equality();
6992
7023
  init_logical_events();
6993
7024
  init_tool_arguments();
6994
7025
  init_retry_safety();
@@ -7196,6 +7227,38 @@ var init_contract = __esm({
7196
7227
  });
7197
7228
 
7198
7229
  // packages/core/src/evidence/contract-binding.ts
7230
+ function contractContainsUnsafeExpectedValues(value) {
7231
+ return scanForExpectedFields(value, 0);
7232
+ }
7233
+ function scanForExpectedFields(value, depth) {
7234
+ if (depth > 32) return false;
7235
+ if (Array.isArray(value)) {
7236
+ return value.some((item) => scanForExpectedFields(item, depth + 1));
7237
+ }
7238
+ if (value === null || typeof value !== "object") return false;
7239
+ const record = value;
7240
+ for (const [key, child] of Object.entries(record)) {
7241
+ if (key === "expected" || key === "oneOf") {
7242
+ if (valueTreeContainsCredential(child, depth + 1)) return true;
7243
+ continue;
7244
+ }
7245
+ if (scanForExpectedFields(child, depth + 1)) return true;
7246
+ }
7247
+ return false;
7248
+ }
7249
+ function valueTreeContainsCredential(value, depth) {
7250
+ if (depth > 32) return false;
7251
+ if (typeof value === "string") {
7252
+ return stringContainsHighConfidenceCredential(value);
7253
+ }
7254
+ if (Array.isArray(value)) {
7255
+ return value.some((item) => valueTreeContainsCredential(item, depth + 1));
7256
+ }
7257
+ if (value === null || typeof value !== "object") return false;
7258
+ return Object.values(value).some(
7259
+ (child) => valueTreeContainsCredential(child, depth + 1)
7260
+ );
7261
+ }
7199
7262
  function serializeCanonicalJson(value) {
7200
7263
  return `${JSON.stringify(stableValue(value), null, 2)}
7201
7264
  `;
@@ -7329,6 +7392,32 @@ function buildEvidenceContractPackage(input3) {
7329
7392
  if (unsupported.length > 0) {
7330
7393
  ruleIds = sortedUniqueStrings([...ruleIds, ...unsupported]);
7331
7394
  }
7395
+ if (contractContainsUnsafeExpectedValues(resolved)) {
7396
+ return {
7397
+ binding: {
7398
+ status: "unavailable",
7399
+ source: input3.source,
7400
+ ...input3.contractId !== void 0 ? { contractId: input3.contractId } : {},
7401
+ ...input3.contractVersion !== void 0 ? { contractVersion: input3.contractVersion } : {},
7402
+ canonicalizationVersion: EVIDENCE_CONTRACT_CANONICALIZATION_VERSION,
7403
+ engineVersion: input3.engineVersion,
7404
+ ruleIds,
7405
+ ...unsupported.length > 0 ? { unsupportedRuleIds: unsupported } : {},
7406
+ note: input3.note ?? EVIDENCE_CONTRACT_UNSAFE_NOTE
7407
+ },
7408
+ checkBinding: {
7409
+ canonicalizationVersion: EVIDENCE_CONTRACT_CANONICALIZATION_VERSION,
7410
+ engineVersion: input3.engineVersion,
7411
+ evaluatedRuleIds: ruleIds,
7412
+ bindingStatus: "unavailable",
7413
+ ...unsupported.length > 0 ? { unsupportedRuleIds: unsupported } : {},
7414
+ origin: {
7415
+ source: input3.source,
7416
+ ...input3.preset?.name ? { preset: input3.preset.name } : {}
7417
+ }
7418
+ }
7419
+ };
7420
+ }
7332
7421
  const document = {
7333
7422
  canonicalizationVersion: EVIDENCE_CONTRACT_CANONICALIZATION_VERSION,
7334
7423
  kind,
@@ -7468,16 +7557,18 @@ function verifyEvidenceContractBinding(parts) {
7468
7557
  }
7469
7558
  return issues;
7470
7559
  }
7471
- var EVIDENCE_CONTRACT_CANONICALIZATION_VERSION, EVIDENCE_CONTRACT_PARTIAL_NOTE, EVIDENCE_CONTRACT_UNAVAILABLE_NOTE, EVIDENCE_CONTRACT_ASSURANCE_NOTE;
7560
+ var EVIDENCE_CONTRACT_CANONICALIZATION_VERSION, EVIDENCE_CONTRACT_PARTIAL_NOTE, EVIDENCE_CONTRACT_UNAVAILABLE_NOTE, EVIDENCE_CONTRACT_ASSURANCE_NOTE, EVIDENCE_CONTRACT_UNSAFE_NOTE;
7472
7561
  var init_contract_binding = __esm({
7473
7562
  "packages/core/src/evidence/contract-binding.ts"() {
7474
7563
  init_contract();
7564
+ init_credential_value_patterns();
7475
7565
  init_hash();
7476
7566
  init_types4();
7477
7567
  EVIDENCE_CONTRACT_CANONICALIZATION_VERSION = "1";
7478
7568
  EVIDENCE_CONTRACT_PARTIAL_NOTE = "Custom or programmatic rules are not serializable; Evidence records rule IDs only and does not claim complete reviewer replay.";
7479
7569
  EVIDENCE_CONTRACT_UNAVAILABLE_NOTE = "No resolved TraceContract or check preset was packaged with this Evidence artifact.";
7480
7570
  EVIDENCE_CONTRACT_ASSURANCE_NOTE = "Contract binding verifies packaged digest integrity only; it does not prove producer identity, trusted time, source completeness, semantic truth, or external acceptance.";
7571
+ EVIDENCE_CONTRACT_UNSAFE_NOTE = "Resolved TraceContract contains high-confidence credential-like expected values; Evidence refuses complete packaging rather than silently redacting evaluated expectations.";
7481
7572
  }
7482
7573
  });
7483
7574
 
@@ -7826,7 +7917,15 @@ function buildEmbeddedPayload(input3) {
7826
7917
  redactionProfile: input3.redactionProfile,
7827
7918
  verificationPolicy: input3.verificationPolicy
7828
7919
  },
7829
- checkSummary: input3.checkSummary
7920
+ checkSummary: input3.checkSummary,
7921
+ ...input3.contractBinding !== void 0 ? {
7922
+ contract: {
7923
+ status: input3.contractBinding.status,
7924
+ ...input3.contractBinding.sha256 !== void 0 ? { sha256: input3.contractBinding.sha256 } : {},
7925
+ ...input3.contractBinding.ruleCount !== void 0 ? { ruleCount: input3.contractBinding.ruleCount } : {},
7926
+ ...input3.contractBinding.note !== void 0 ? { note: input3.contractBinding.note } : {}
7927
+ }
7928
+ } : {}
7830
7929
  };
7831
7930
  }
7832
7931
  function buildEvidenceHtmlShell(input3) {
@@ -7924,6 +8023,7 @@ ${nav}
7924
8023
  <p>Artifact status: <span class="badge ${statusClass(input3.assessmentStatus)}">${escapeHtml(input3.assessmentStatus)}</span>
7925
8024
  ${input3.sourceStatus !== void 0 ? ` \xB7 Source status: <span class="badge ${statusClass(input3.sourceStatus)}">${escapeHtml(input3.sourceStatus)}</span>` : ""}</p>
7926
8025
  <p>Profile: <code>${escapeHtml(input3.redactionProfile)}</code> \xB7 Verification: <code>${escapeHtml(input3.verificationPolicy)}</code></p>
8026
+ ${input3.contractBinding !== void 0 ? `<p>Contract binding: <code>${escapeHtml(input3.contractBinding.status)}</code>${input3.contractBinding.ruleCount !== void 0 ? ` \xB7 Rules: <code>${escapeHtml(String(input3.contractBinding.ruleCount))}</code>` : ""}${input3.contractBinding.sha256 !== void 0 ? ` \xB7 Digest: <code>${escapeHtml(input3.contractBinding.sha256.slice(0, 16))}\u2026</code>` : ""}</p>${input3.contractBinding.note !== void 0 && input3.contractBinding.note.trim() !== "" ? `<p class="muted">${escapeHtml(input3.contractBinding.note)}</p>` : ""}` : ""}
7927
8027
  <p>Generator: <code>${escapeHtml(input3.generatorName)}@${escapeHtml(input3.generatorVersion)}</code>
7928
8028
  ${input3.createdAt ? ` \xB7 Created: <code>${escapeHtml(input3.createdAt)}</code>` : ""}</p>
7929
8029
  <h3>Runs</h3>
@@ -9015,29 +9115,6 @@ function buildEvidenceCiPackage(input3) {
9015
9115
  schemaVersions.add(version2);
9016
9116
  }
9017
9117
  const createdAt = input3.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
9018
- const evidenceHtml = buildEvidenceHtmlShell({
9019
- title: "AgentInspect evidence",
9020
- runIds: input3.runIds,
9021
- assessmentStatus: input3.assessmentStatus,
9022
- sourceStatus: input3.sourceStatus,
9023
- redactionProfile: input3.redactionProfile,
9024
- verificationPolicy: input3.redactionProfile,
9025
- generatorName: "agent-inspect",
9026
- generatorVersion: input3.generatorVersion,
9027
- createdAt,
9028
- summaryText: input3.summaryText,
9029
- checkSummary: {
9030
- aggregateStatus: input3.assessmentStatus,
9031
- runs: input3.runIds.map((runId) => ({
9032
- runId,
9033
- status: input3.assessmentStatus,
9034
- sourceStatus: input3.sourceStatus,
9035
- errors: input3.assessmentStatus === "UNSAFE" || input3.assessmentStatus === "UNKNOWN" ? 1 : 0,
9036
- warnings: input3.assessmentStatus === "SAFE WITH WARNINGS" ? 1 : 0,
9037
- findings: 0
9038
- }))
9039
- }
9040
- });
9041
9118
  let checkResultsJson = input3.checkResultsJson;
9042
9119
  let contractBinding;
9043
9120
  let contractFileContent;
@@ -9060,6 +9137,37 @@ function buildEvidenceCiPackage(input3) {
9060
9137
  bindCheckResultsToContract(parsed, packaged2.checkBinding)
9061
9138
  );
9062
9139
  }
9140
+ const evidenceHtml = buildEvidenceHtmlShell({
9141
+ title: "AgentInspect evidence",
9142
+ runIds: input3.runIds,
9143
+ assessmentStatus: input3.assessmentStatus,
9144
+ sourceStatus: input3.sourceStatus,
9145
+ redactionProfile: input3.redactionProfile,
9146
+ verificationPolicy: input3.redactionProfile,
9147
+ generatorName: "agent-inspect",
9148
+ generatorVersion: input3.generatorVersion,
9149
+ createdAt,
9150
+ summaryText: input3.summaryText,
9151
+ checkSummary: {
9152
+ aggregateStatus: input3.assessmentStatus,
9153
+ runs: input3.runIds.map((runId) => ({
9154
+ runId,
9155
+ status: input3.assessmentStatus,
9156
+ sourceStatus: input3.sourceStatus,
9157
+ errors: input3.assessmentStatus === "UNSAFE" || input3.assessmentStatus === "UNKNOWN" ? 1 : 0,
9158
+ warnings: input3.assessmentStatus === "SAFE WITH WARNINGS" ? 1 : 0,
9159
+ findings: 0
9160
+ }))
9161
+ },
9162
+ ...contractBinding !== void 0 ? {
9163
+ contractBinding: {
9164
+ status: contractBinding.status,
9165
+ ...contractBinding.sha256 !== void 0 ? { sha256: contractBinding.sha256 } : {},
9166
+ ...contractBinding.note !== void 0 ? { note: contractBinding.note } : {},
9167
+ ruleCount: contractBinding.ruleIds.length
9168
+ }
9169
+ } : {}
9170
+ });
9063
9171
  const packaged = [
9064
9172
  { path: EVIDENCE_HTML_FILENAME, content: evidenceHtml },
9065
9173
  { path: "check-results.json", content: checkResultsJson },
@@ -13648,7 +13756,7 @@ var init_src = __esm({
13648
13756
  });
13649
13757
 
13650
13758
  // package.json
13651
- var version = "6.29.0";
13759
+ var version = "6.29.1";
13652
13760
 
13653
13761
  // packages/cli/src/list.ts
13654
13762
  init_advanced();
@@ -17641,13 +17749,13 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
17641
17749
 
17642
17750
  // packages/redact/src/key-value-secret.ts
17643
17751
  var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
17644
- var KEY_VALUE_SECRET_PATTERN = new RegExp(
17752
+ var KEY_VALUE_SECRET_PATTERN2 = new RegExp(
17645
17753
  KEY_VALUE_SECRET_PATTERN_SOURCE,
17646
17754
  "i"
17647
17755
  );
17648
17756
  function valueContainsKeyValueSecret(value) {
17649
- KEY_VALUE_SECRET_PATTERN.lastIndex = 0;
17650
- return KEY_VALUE_SECRET_PATTERN.test(value);
17757
+ KEY_VALUE_SECRET_PATTERN2.lastIndex = 0;
17758
+ return KEY_VALUE_SECRET_PATTERN2.test(value);
17651
17759
  }
17652
17760
 
17653
17761
  // packages/redact/src/index.ts
@@ -18116,15 +18224,14 @@ var REDACTION_POLICY_LIMITS = {
18116
18224
  maxPatterns: 32,
18117
18225
  maxPatternLength: 128,
18118
18226
  maxPatternIdLength: 64,
18119
- maxTypedQuantifier: 64
18227
+ /** Maximum policy file size before parse (bytes). */
18228
+ maxPolicyFileBytes: 64 * 1024
18120
18229
  };
18121
- var SAFE_TYPED_PATTERN = /^[A-Za-z0-9_@./:=<>\-[\]()|+*?{},\\\s^$]+$/;
18230
+ var ALLOWED_TOP_LEVEL = /* @__PURE__ */ new Set(["version", "extraKeys", "patterns"]);
18231
+ var ALLOWED_PATTERN_FIELDS = /* @__PURE__ */ new Set(["id", "type", "value", "severity"]);
18122
18232
  function isRecord17(value) {
18123
18233
  return value !== null && typeof value === "object" && !Array.isArray(value);
18124
18234
  }
18125
- function escapeRegExp(value) {
18126
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18127
- }
18128
18235
  function rejectRemotePolicyPath(policyPath) {
18129
18236
  const trimmed = policyPath.trim();
18130
18237
  if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) {
@@ -18153,43 +18260,23 @@ function validateExtraKey(key, index) {
18153
18260
  }
18154
18261
  return key;
18155
18262
  }
18156
- function assertBoundedTypedPattern(pattern, label) {
18157
- if (pattern.length === 0) throw new Error(`${label} must be non-empty.`);
18158
- if (pattern.length > REDACTION_POLICY_LIMITS.maxPatternLength) {
18159
- throw new Error(
18160
- `${label} exceeds max length ${REDACTION_POLICY_LIMITS.maxPatternLength}.`
18161
- );
18162
- }
18163
- if (!SAFE_TYPED_PATTERN.test(pattern)) {
18164
- throw new Error(
18165
- `${label} contains unsupported characters for bounded typed patterns.`
18166
- );
18167
- }
18168
- if (/\(\?/.test(pattern) || /\\[1-9]/.test(pattern)) {
18169
- throw new Error(
18170
- `${label} rejects lookaround, backreferences, and nested quantifiers.`
18171
- );
18172
- }
18173
- if (/\([^)]*[+*{][^)]*\)[+*{]/.test(pattern) || /\[[^\]]*[+*{][^\]]*\][+*{]/.test(pattern)) {
18174
- throw new Error(`${label} rejects nested quantifiers.`);
18175
- }
18176
- if (/\.[*+]/.test(pattern) || /[*+]\{/.test(pattern)) {
18177
- throw new Error(`${label} rejects unbounded .* / .+ style quantifiers.`);
18178
- }
18179
- for (const match of pattern.matchAll(/\{(\d+)(?:,(\d*))?\}/g)) {
18180
- const min = Number(match[1]);
18181
- const maxRaw = match[2];
18182
- const max = maxRaw === void 0 || maxRaw === "" ? min : Number(maxRaw);
18183
- if (!Number.isFinite(min) || !Number.isFinite(max) || min > REDACTION_POLICY_LIMITS.maxTypedQuantifier || max > REDACTION_POLICY_LIMITS.maxTypedQuantifier) {
18184
- throw new Error(
18185
- `${label} quantifiers must be <= ${REDACTION_POLICY_LIMITS.maxTypedQuantifier}.`
18186
- );
18263
+ function rejectUnknownFields(record, allowed, label) {
18264
+ for (const key of Object.keys(record)) {
18265
+ if (!allowed.has(key)) {
18266
+ throw new Error(`${label} rejects unknown field "${key}".`);
18187
18267
  }
18188
18268
  }
18189
18269
  }
18190
18270
  function compilePatternDetector(input3, index) {
18191
18271
  const label = `patterns[${index}]`;
18192
18272
  if (!isRecord17(input3)) throw new Error(`${label} must be an object.`);
18273
+ const typeEarly = input3.type;
18274
+ if (typeEarly === "typed") {
18275
+ throw new Error(
18276
+ `${label}.type "typed" is no longer supported (removed in 6.29.1). Use type "literal" or "prefix" with a fixed value; arbitrary regex patterns are not accepted.`
18277
+ );
18278
+ }
18279
+ rejectUnknownFields(input3, ALLOWED_PATTERN_FIELDS, label);
18193
18280
  const idRaw = input3.id;
18194
18281
  if (typeof idRaw !== "string" || idRaw.trim() === "") {
18195
18282
  throw new Error(`${label}.id must be a non-empty string.`);
@@ -18204,48 +18291,35 @@ function compilePatternDetector(input3, index) {
18204
18291
  throw new Error(`${label}.id must be an identifier-like detector id.`);
18205
18292
  }
18206
18293
  const type = input3.type;
18207
- if (type !== "literal" && type !== "prefix" && type !== "typed") {
18208
- throw new Error(`${label}.type must be literal, prefix, or typed.`);
18294
+ if (type !== "literal" && type !== "prefix") {
18295
+ throw new Error(`${label}.type must be literal or prefix.`);
18296
+ }
18297
+ if (input3.pattern !== void 0) {
18298
+ throw new Error(
18299
+ `${label}.pattern is not accepted; use .value with type literal or prefix.`
18300
+ );
18209
18301
  }
18210
18302
  const severity = parseSeverity(input3.severity, label);
18211
- let source;
18212
- if (type === "typed") {
18213
- if (typeof input3.pattern !== "string") {
18214
- throw new Error(`${label}.pattern must be a string for typed patterns.`);
18215
- }
18216
- assertBoundedTypedPattern(input3.pattern, `${label}.pattern`);
18217
- source = input3.pattern;
18218
- } else {
18219
- if (typeof input3.value !== "string") {
18220
- throw new Error(`${label}.value must be a string for ${type} patterns.`);
18221
- }
18222
- if (input3.value.length === 0) {
18223
- throw new Error(`${label}.value must be non-empty.`);
18224
- }
18225
- if (input3.value.length > REDACTION_POLICY_LIMITS.maxPatternLength) {
18226
- throw new Error(
18227
- `${label}.value exceeds max length ${REDACTION_POLICY_LIMITS.maxPatternLength}.`
18228
- );
18229
- }
18230
- const escaped = escapeRegExp(input3.value);
18231
- source = type === "prefix" ? `^${escaped}` : escaped;
18303
+ if (typeof input3.value !== "string") {
18304
+ throw new Error(`${label}.value must be a string for ${type} patterns.`);
18232
18305
  }
18233
- let regex;
18234
- try {
18235
- regex = new RegExp(source);
18236
- } catch (error) {
18306
+ if (input3.value.length === 0) {
18307
+ throw new Error(`${label}.value must be non-empty.`);
18308
+ }
18309
+ if (input3.value.length > REDACTION_POLICY_LIMITS.maxPatternLength) {
18237
18310
  throw new Error(
18238
- `${label} failed to compile: ${error instanceof Error ? error.message : String(error)}`
18311
+ `${label}.value exceeds max length ${REDACTION_POLICY_LIMITS.maxPatternLength}.`
18239
18312
  );
18240
18313
  }
18314
+ const needle = input3.value;
18315
+ const match = type === "prefix" ? (haystack) => haystack.startsWith(needle) : (haystack) => haystack.includes(needle);
18241
18316
  return {
18242
18317
  id: `policy.${id}`,
18243
18318
  severity,
18244
18319
  matchKind: "custom",
18245
18320
  detect({ value }) {
18246
18321
  if (typeof value !== "string") return [];
18247
- regex.lastIndex = 0;
18248
- return regex.test(value) ? [{ action: "replace", severity, matchKind: "custom" }] : [];
18322
+ return match(value) ? [{ action: "replace", severity, matchKind: "custom" }] : [];
18249
18323
  }
18250
18324
  };
18251
18325
  }
@@ -18253,6 +18327,7 @@ function compileRedactionPolicy(raw, policyPath) {
18253
18327
  if (!isRecord17(raw)) {
18254
18328
  throw new Error(`Redaction policy must be a JSON object (${policyPath}).`);
18255
18329
  }
18330
+ rejectUnknownFields(raw, ALLOWED_TOP_LEVEL, "Redaction policy");
18256
18331
  const diagnostics = [];
18257
18332
  if (raw.version !== void 0 && raw.version !== 1) {
18258
18333
  throw new Error(`Unsupported redaction policy version (supported: 1).`);
@@ -18316,14 +18391,38 @@ function compileRedactionPolicy(raw, policyPath) {
18316
18391
  async function loadRedactionPolicy(policyPath) {
18317
18392
  rejectRemotePolicyPath(policyPath);
18318
18393
  const resolved = path33__default.default.resolve(policyPath);
18319
- let text;
18394
+ let fileStat;
18395
+ try {
18396
+ fileStat = await promises.stat(resolved);
18397
+ } catch (error) {
18398
+ throw new Error(
18399
+ `Failed to read --policy file "${resolved}": ${error instanceof Error ? error.message : String(error)}`
18400
+ );
18401
+ }
18402
+ if (!fileStat.isFile()) {
18403
+ throw new Error(
18404
+ `--policy must be a regular local JSON file (not a directory or special file): "${resolved}".`
18405
+ );
18406
+ }
18407
+ if (fileStat.size > REDACTION_POLICY_LIMITS.maxPolicyFileBytes) {
18408
+ throw new Error(
18409
+ `--policy file exceeds max size ${REDACTION_POLICY_LIMITS.maxPolicyFileBytes} bytes.`
18410
+ );
18411
+ }
18412
+ let buffer;
18320
18413
  try {
18321
- text = await promises.readFile(resolved, "utf-8");
18414
+ buffer = await promises.readFile(resolved);
18322
18415
  } catch (error) {
18323
18416
  throw new Error(
18324
18417
  `Failed to read --policy file "${resolved}": ${error instanceof Error ? error.message : String(error)}`
18325
18418
  );
18326
18419
  }
18420
+ if (buffer.byteLength > REDACTION_POLICY_LIMITS.maxPolicyFileBytes) {
18421
+ throw new Error(
18422
+ `--policy file exceeds max size ${REDACTION_POLICY_LIMITS.maxPolicyFileBytes} bytes.`
18423
+ );
18424
+ }
18425
+ const text = buffer.toString("utf-8");
18327
18426
  let parsed;
18328
18427
  try {
18329
18428
  parsed = JSON.parse(text);