@mrciphersmith/keryx 0.3.18 → 0.3.20

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 (3) hide show
  1. package/dist/cli.js +701 -305
  2. package/dist/core.js +547 -90
  3. package/package.json +1 -1
package/dist/core.js CHANGED
@@ -4028,6 +4028,333 @@ var init_config2 = __esm(() => {
4028
4028
  ]);
4029
4029
  });
4030
4030
 
4031
+ // src/security/detect/entropy.ts
4032
+ function isHexBlob(value) {
4033
+ return HEX_BLOB.test(value) && /[a-f]/i.test(value);
4034
+ }
4035
+ function isAllowShapedValue(value) {
4036
+ return FULL_GIT_SHA_RE.test(value) || UUID_RE.test(value) || INTEGRITY_RE.test(value);
4037
+ }
4038
+ function lastLineOf(before) {
4039
+ const newline = before.lastIndexOf(`
4040
+ `);
4041
+ return newline < 0 ? before : before.slice(newline + 1);
4042
+ }
4043
+ function isPlainCasedWord(letters) {
4044
+ return /^[a-z]+$/.test(letters) || /^[A-Z]+$/.test(letters) || /^[A-Z][a-z]*$/.test(letters);
4045
+ }
4046
+ function isSingleTransitionTag(segment) {
4047
+ const letterThenDigit = /^([A-Za-z]+)([0-9]+)$/.exec(segment);
4048
+ if (letterThenDigit !== null) {
4049
+ const letters = letterThenDigit[1];
4050
+ return letters.length <= TAG_MAX_LETTER_RUN && isPlainCasedWord(letters);
4051
+ }
4052
+ const digitThenLetter = /^([0-9]+)([A-Za-z]+)$/.exec(segment);
4053
+ if (digitThenLetter !== null) {
4054
+ const letters = digitThenLetter[2];
4055
+ return letters.length <= TAG_MAX_LETTER_RUN && isPlainCasedWord(letters);
4056
+ }
4057
+ return false;
4058
+ }
4059
+ function isSlugHexTail(segment) {
4060
+ return SLUG_HEX_TAIL_RE.test(segment);
4061
+ }
4062
+ function isWordSlug(value) {
4063
+ const segments = value.split(/[-_]/);
4064
+ if (segments.length < 3) {
4065
+ return false;
4066
+ }
4067
+ let words = 0;
4068
+ for (let i = 0;i < segments.length; i += 1) {
4069
+ const segment = segments[i];
4070
+ const isDigits = /^[0-9]+$/.test(segment);
4071
+ if (isDigits) {
4072
+ continue;
4073
+ }
4074
+ if (isPlainCasedWord(segment)) {
4075
+ if (segment.length >= 3) {
4076
+ words += 1;
4077
+ }
4078
+ continue;
4079
+ }
4080
+ if (isSingleTransitionTag(segment)) {
4081
+ continue;
4082
+ }
4083
+ if (i === segments.length - 1 && isSlugHexTail(segment)) {
4084
+ continue;
4085
+ }
4086
+ return false;
4087
+ }
4088
+ return words >= 1;
4089
+ }
4090
+ function shannonEntropy(value) {
4091
+ const counts = new Map;
4092
+ for (const ch of value) {
4093
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
4094
+ }
4095
+ let entropy = 0;
4096
+ for (const count of counts.values()) {
4097
+ const p = count / value.length;
4098
+ entropy -= p * Math.log2(p);
4099
+ }
4100
+ return entropy;
4101
+ }
4102
+ function bareShapeQualifies(head) {
4103
+ if (!/[0-9]/.test(head) && !/[+=]/.test(head)) {
4104
+ return { qualifies: false, entropy: 0 };
4105
+ }
4106
+ if (isWordSlug(head)) {
4107
+ return { qualifies: false, entropy: 0 };
4108
+ }
4109
+ const entropy = shannonEntropy(head);
4110
+ const hexBlob = isHexBlob(head);
4111
+ if (entropy < 3.6 && !hexBlob) {
4112
+ return { qualifies: false, entropy };
4113
+ }
4114
+ return { qualifies: true, entropy };
4115
+ }
4116
+ function shapeQualifiesAsSecret(value) {
4117
+ if (isAllowShapedValue(value)) {
4118
+ return { qualifies: false, entropy: 0 };
4119
+ }
4120
+ return bareShapeQualifies(value);
4121
+ }
4122
+ function looksSecretShaped(value) {
4123
+ return shapeQualifiesAsSecret(value).qualifies;
4124
+ }
4125
+ function buildMatch(start, end, value, entropy) {
4126
+ const confidence = Math.max(0.4, Math.min(0.7, 0.4 + (entropy - 3.6) * 0.21));
4127
+ return {
4128
+ category: "secret",
4129
+ policyId: "secrets.high-entropy",
4130
+ severity: "medium",
4131
+ confidence: Number(confidence.toFixed(2)),
4132
+ start,
4133
+ end,
4134
+ value,
4135
+ mask: "entropy",
4136
+ remediation: "Verify this high-entropy value is not a live credential."
4137
+ };
4138
+ }
4139
+ function findUrlSpans(content) {
4140
+ const spans = [];
4141
+ URL_SPAN_RE.lastIndex = 0;
4142
+ let m;
4143
+ while ((m = URL_SPAN_RE.exec(content)) !== null) {
4144
+ let raw = m[0];
4145
+ while (raw.length > 0 && URL_TRAILING_PUNCTUATION_RE.test(raw)) {
4146
+ raw = raw.slice(0, -1);
4147
+ }
4148
+ if (raw.length > 0) {
4149
+ spans.push({ start: m.index, end: m.index + raw.length, raw });
4150
+ }
4151
+ if (m.index === URL_SPAN_RE.lastIndex) {
4152
+ URL_SPAN_RE.lastIndex += 1;
4153
+ }
4154
+ }
4155
+ return spans;
4156
+ }
4157
+ function urlComponentSpans(raw) {
4158
+ const spans = [];
4159
+ const schemeMatch = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.exec(raw);
4160
+ if (schemeMatch === null) {
4161
+ return spans;
4162
+ }
4163
+ let i = schemeMatch[0].length;
4164
+ while (i < raw.length && raw[i] !== "/" && raw[i] !== "?" && raw[i] !== "#") {
4165
+ i += 1;
4166
+ }
4167
+ let pathEnd = i;
4168
+ while (pathEnd < raw.length && raw[pathEnd] !== "?" && raw[pathEnd] !== "#") {
4169
+ pathEnd += 1;
4170
+ }
4171
+ let segStart = i;
4172
+ for (let j = i;j <= pathEnd; j += 1) {
4173
+ if (j === pathEnd || raw[j] === "/") {
4174
+ if (j > segStart) {
4175
+ spans.push({ start: segStart, end: j });
4176
+ }
4177
+ segStart = j + 1;
4178
+ }
4179
+ }
4180
+ let cursor = pathEnd;
4181
+ if (cursor < raw.length && raw[cursor] === "?") {
4182
+ let queryEnd = cursor + 1;
4183
+ while (queryEnd < raw.length && raw[queryEnd] !== "#") {
4184
+ queryEnd += 1;
4185
+ }
4186
+ let pairStart = cursor + 1;
4187
+ for (let j = cursor + 1;j <= queryEnd; j += 1) {
4188
+ if (j === queryEnd || raw[j] === "&") {
4189
+ const pair = raw.slice(pairStart, j);
4190
+ const eq = pair.indexOf("=");
4191
+ if (eq !== -1 && eq + 1 < pair.length) {
4192
+ spans.push({ start: pairStart + eq + 1, end: j });
4193
+ } else if (eq === -1 && pair.length > 0) {
4194
+ spans.push({ start: pairStart, end: j });
4195
+ }
4196
+ pairStart = j + 1;
4197
+ }
4198
+ }
4199
+ cursor = queryEnd;
4200
+ }
4201
+ if (cursor < raw.length && raw[cursor] === "#" && cursor + 1 < raw.length) {
4202
+ spans.push({ start: cursor + 1, end: raw.length });
4203
+ }
4204
+ return spans;
4205
+ }
4206
+ function secretShapedCandidatesIn(text) {
4207
+ const normalized = text.replace(/\+/g, " ");
4208
+ const out = [];
4209
+ TOKEN.lastIndex = 0;
4210
+ let m;
4211
+ while ((m = TOKEN.exec(normalized)) !== null) {
4212
+ const value = m[0];
4213
+ if (value.length < URL_COMPONENT_MIN_LENGTH) {
4214
+ continue;
4215
+ }
4216
+ const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
4217
+ const { qualifies, entropy } = shapeQualifiesAsSecret(head);
4218
+ if (!qualifies) {
4219
+ continue;
4220
+ }
4221
+ const start = m.index;
4222
+ const end = start + value.length;
4223
+ out.push({ start, end, value: text.slice(start, end), entropy });
4224
+ }
4225
+ return out;
4226
+ }
4227
+ function looksSecretShapedIn(text) {
4228
+ return secretShapedCandidatesIn(text).length > 0;
4229
+ }
4230
+ function labelledPieceQualifies(piece) {
4231
+ const base = bareShapeQualifies(piece);
4232
+ if (base.qualifies) {
4233
+ return { entropy: base.entropy };
4234
+ }
4235
+ if (isAllowShapedValue(piece)) {
4236
+ return { entropy: base.entropy };
4237
+ }
4238
+ return null;
4239
+ }
4240
+ function firstQualifyingSlashPiece(head) {
4241
+ let offset = 0;
4242
+ for (const piece of head.split("/")) {
4243
+ if (piece.length > 0) {
4244
+ const found = labelledPieceQualifies(piece);
4245
+ if (found !== null) {
4246
+ return { offset, length: piece.length, entropy: found.entropy };
4247
+ }
4248
+ }
4249
+ offset += piece.length + 1;
4250
+ }
4251
+ return null;
4252
+ }
4253
+ function detectEntropy(content) {
4254
+ const matches = [];
4255
+ const urlSpans = findUrlSpans(content);
4256
+ for (const url of urlSpans) {
4257
+ for (const comp of urlComponentSpans(url.raw)) {
4258
+ const compText = url.raw.slice(comp.start, comp.end);
4259
+ for (const cand of secretShapedCandidatesIn(compText)) {
4260
+ const absStart = url.start + comp.start + cand.start;
4261
+ const absEnd = url.start + comp.start + cand.end;
4262
+ matches.push(buildMatch(absStart, absEnd, cand.value, cand.entropy));
4263
+ }
4264
+ }
4265
+ }
4266
+ LABEL_ASSIGNMENT_RE.lastIndex = 0;
4267
+ let am;
4268
+ while ((am = LABEL_ASSIGNMENT_RE.exec(content)) !== null) {
4269
+ const value = am[1];
4270
+ const end = am.index + am[0].length;
4271
+ const start = end - value.length;
4272
+ if (urlSpans.some((u) => start < u.end && end > u.start)) {
4273
+ continue;
4274
+ }
4275
+ const { qualifies, entropy } = bareShapeQualifies(value);
4276
+ if (qualifies) {
4277
+ matches.push(buildMatch(start, end, value, entropy));
4278
+ }
4279
+ }
4280
+ TOKEN.lastIndex = 0;
4281
+ let m;
4282
+ while ((m = TOKEN.exec(content)) !== null) {
4283
+ const value = m[0];
4284
+ const start = m.index;
4285
+ const end = start + value.length;
4286
+ if (urlSpans.some((u) => start < u.end && end > u.start)) {
4287
+ continue;
4288
+ }
4289
+ if (matches.some((existing) => start < existing.end && end > existing.start)) {
4290
+ continue;
4291
+ }
4292
+ const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
4293
+ if (!ADJACENT_LABEL.test(before)) {
4294
+ continue;
4295
+ }
4296
+ const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
4297
+ if (head.includes("/")) {
4298
+ const piece = firstQualifyingSlashPiece(head);
4299
+ if (piece === null) {
4300
+ continue;
4301
+ }
4302
+ const pieceStart = start + piece.offset;
4303
+ const pieceEnd = pieceStart + piece.length;
4304
+ matches.push(buildMatch(pieceStart, pieceEnd, content.slice(pieceStart, pieceEnd), piece.entropy));
4305
+ continue;
4306
+ }
4307
+ const found = labelledPieceQualifies(head);
4308
+ if (found === null) {
4309
+ continue;
4310
+ }
4311
+ matches.push(buildMatch(start, end, value, found.entropy));
4312
+ }
4313
+ return matches.sort((a, b) => a.start - b.start);
4314
+ }
4315
+ var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), ADJACENT_LABEL, LABEL_ASSIGNMENT_RE, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB, FULL_GIT_SHA_RE, UUID_RE, INTEGRITY_RE, TAG_MAX_LETTER_RUN = 8, SLUG_HEX_TAIL_RE, URL_SPAN_RE, URL_TRAILING_PUNCTUATION_RE, URL_COMPONENT_MIN_LENGTH = 20;
4316
+ var init_entropy = __esm(() => {
4317
+ ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*["']?$`);
4318
+ LABEL_ASSIGNMENT_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${SENSITIVE_LABEL_WORDS})[A-Za-z0-9_-]*"?\\s*=\\s*"?([A-Za-z0-9+/_=-]{6,})`, "gi");
4319
+ TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
4320
+ TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
4321
+ HEX_BLOB = /^[0-9a-f]{24,}$/i;
4322
+ FULL_GIT_SHA_RE = /^[0-9a-f]{40}$/i;
4323
+ UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4324
+ INTEGRITY_RE = /^(?:sha256|sha512)-[A-Za-z0-9+/]+=*$/;
4325
+ SLUG_HEX_TAIL_RE = /^[0-9a-f]{10,16}$/i;
4326
+ URL_SPAN_RE = /\bhttps?:\/\/[^\s<>"'\\]+/g;
4327
+ URL_TRAILING_PUNCTUATION_RE = /[.,;:!?)\]}]$/;
4328
+ });
4329
+
4330
+ // src/security/entropy-gate.ts
4331
+ function ensureLoading() {
4332
+ if (loadStarted) {
4333
+ return;
4334
+ }
4335
+ loadStarted = true;
4336
+ loadSecurityConfig(process.cwd()).then((config) => {
4337
+ cachedEnabled = config.backends.entropy.enabled;
4338
+ }).catch(() => {});
4339
+ }
4340
+ function isEntropyBackendEnabled() {
4341
+ ensureLoading();
4342
+ return cachedEnabled;
4343
+ }
4344
+ function setEntropyBackendEnabledForTests(enabled) {
4345
+ loadStarted = true;
4346
+ cachedEnabled = enabled;
4347
+ }
4348
+ function resetEntropyGateForTests() {
4349
+ loadStarted = false;
4350
+ cachedEnabled = DEFAULT_SECURITY_CONFIG.backends.entropy.enabled;
4351
+ }
4352
+ var cachedEnabled, loadStarted = false;
4353
+ var init_entropy_gate = __esm(() => {
4354
+ init_config2();
4355
+ cachedEnabled = DEFAULT_SECURITY_CONFIG.backends.entropy.enabled;
4356
+ });
4357
+
4031
4358
  // src/security/redact.ts
4032
4359
  import path17 from "path";
4033
4360
  import { createHmac, randomBytes as randomBytes2 } from "crypto";
@@ -4097,7 +4424,12 @@ function redactSensitiveText(text) {
4097
4424
  if (text.length === 0) {
4098
4425
  return text;
4099
4426
  }
4100
- const matches = [...detectSecrets(text), ...detectPii(text), ...detectExfil(text)];
4427
+ const matches = [
4428
+ ...detectSecrets(text),
4429
+ ...detectPii(text),
4430
+ ...detectExfil(text),
4431
+ ...isEntropyBackendEnabled() ? detectEntropy(text) : []
4432
+ ];
4101
4433
  if (matches.length === 0) {
4102
4434
  return text;
4103
4435
  }
@@ -4134,6 +4466,8 @@ var init_redact = __esm(() => {
4134
4466
  init_secrets();
4135
4467
  init_pii();
4136
4468
  init_exfil();
4469
+ init_entropy();
4470
+ init_entropy_gate();
4137
4471
  });
4138
4472
 
4139
4473
  // src/security/output-validation.ts
@@ -4874,102 +5208,33 @@ var init_seam = __esm(() => {
4874
5208
  init_warn_once();
4875
5209
  });
4876
5210
 
4877
- // src/security/detect/entropy.ts
4878
- function lastLineOf(before) {
4879
- const newline = before.lastIndexOf(`
4880
- `);
4881
- return newline < 0 ? before : before.slice(newline + 1);
4882
- }
4883
- function isWordSlug(value) {
4884
- const segments = value.split(/[-_]/);
4885
- if (segments.length < 3) {
4886
- return false;
4887
- }
4888
- let words = 0;
4889
- for (const segment of segments) {
4890
- const isAlpha = /^[A-Za-z]+$/.test(segment);
4891
- const isDigits = /^[0-9]+$/.test(segment);
4892
- if (!isAlpha && !isDigits) {
4893
- return false;
4894
- }
4895
- if (isAlpha && segment.length >= 3) {
4896
- words += 1;
4897
- }
4898
- }
4899
- return words >= 2;
4900
- }
4901
- function shannonEntropy(value) {
4902
- const counts = new Map;
4903
- for (const ch of value) {
4904
- counts.set(ch, (counts.get(ch) ?? 0) + 1);
4905
- }
4906
- let entropy = 0;
4907
- for (const count of counts.values()) {
4908
- const p = count / value.length;
4909
- entropy -= p * Math.log2(p);
4910
- }
4911
- return entropy;
4912
- }
4913
- function detectEntropy(content) {
4914
- const matches = [];
4915
- TOKEN.lastIndex = 0;
4916
- let m;
4917
- while ((m = TOKEN.exec(content)) !== null) {
4918
- const value = m[0];
4919
- const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
4920
- if (!/[0-9]/.test(head) && !/[+=]/.test(head)) {
4921
- continue;
4922
- }
4923
- if (isWordSlug(head)) {
4924
- continue;
4925
- }
4926
- const entropy = shannonEntropy(head);
4927
- const hexBlob = HEX_BLOB.test(head);
4928
- if (entropy < 3.6 && !hexBlob) {
4929
- continue;
4930
- }
4931
- const before = lastLineOf(content.slice(Math.max(0, m.index - LABEL_WINDOW), m.index));
4932
- if (!SENSITIVE_LABEL.test(before)) {
4933
- continue;
4934
- }
4935
- const confidence = Math.max(0.4, Math.min(0.7, 0.4 + (entropy - 3.6) * 0.21));
4936
- matches.push({
4937
- category: "secret",
4938
- policyId: "secrets.high-entropy",
4939
- severity: "medium",
4940
- confidence: Number(confidence.toFixed(2)),
4941
- start: m.index,
4942
- end: m.index + value.length,
4943
- value,
4944
- mask: "secret",
4945
- remediation: "Verify this high-entropy value is not a live credential."
4946
- });
5211
+ // src/security/detect/injection.ts
5212
+ function foldForMatching(content) {
5213
+ let folded = "";
5214
+ for (let i = 0;i < content.length; i += 1) {
5215
+ const ch = content[i];
5216
+ folded += CONFUSABLES[ch] ?? ch;
4947
5217
  }
4948
- return matches;
5218
+ const nfkc = folded.normalize("NFKC");
5219
+ return nfkc.length === folded.length ? nfkc : folded;
4949
5220
  }
4950
- var SENSITIVE_LABEL, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB;
4951
- var init_entropy = __esm(() => {
4952
- SENSITIVE_LABEL = /(key|secret|token|password|passwd|api|credential|auth)/i;
4953
- TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
4954
- TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
4955
- HEX_BLOB = /^[0-9a-f]{24,}$/i;
4956
- });
4957
-
4958
- // src/security/detect/injection.ts
4959
5221
  function detectInjection(content) {
4960
5222
  const matches = [];
5223
+ const folded = foldForMatching(content);
4961
5224
  for (const pattern of PATTERNS) {
4962
5225
  pattern.regex.lastIndex = 0;
4963
5226
  let m;
4964
- while ((m = pattern.regex.exec(content)) !== null) {
5227
+ while ((m = pattern.regex.exec(folded)) !== null) {
5228
+ const start = Math.min(m.index, content.length);
5229
+ const end = Math.min(m.index + m[0].length, content.length);
4965
5230
  matches.push({
4966
5231
  category: "prompt-injection",
4967
5232
  policyId: pattern.policyId,
4968
5233
  severity: "low",
4969
5234
  confidence: pattern.confidence,
4970
- start: m.index,
4971
- end: m.index + m[0].length,
4972
- value: m[0],
5235
+ start,
5236
+ end,
5237
+ value: content.slice(start, end),
4973
5238
  remediation: "Treat external content as data, not instruction; require human review."
4974
5239
  });
4975
5240
  if (m.index === pattern.regex.lastIndex) {
@@ -4979,27 +5244,69 @@ function detectInjection(content) {
4979
5244
  }
4980
5245
  return matches;
4981
5246
  }
4982
- var PATTERNS;
5247
+ var CONFUSABLES, PATTERNS;
4983
5248
  var init_injection = __esm(() => {
5249
+ CONFUSABLES = {
5250
+ "\u0430": "a",
5251
+ "\u0410": "A",
5252
+ "\u0435": "e",
5253
+ "\u0415": "E",
5254
+ "\u043E": "o",
5255
+ "\u041E": "O",
5256
+ "\u0440": "p",
5257
+ "\u0420": "P",
5258
+ "\u0441": "c",
5259
+ "\u0421": "C",
5260
+ "\u0445": "x",
5261
+ "\u0425": "X",
5262
+ "\u0443": "y",
5263
+ "\u0423": "Y",
5264
+ "\u0455": "s",
5265
+ "\u0405": "S",
5266
+ "\u0456": "o",
5267
+ "\u0406": "O",
5268
+ "\u0458": "j",
5269
+ "\u0408": "J",
5270
+ "\u03B1": "a",
5271
+ "\u0391": "A",
5272
+ "\u03B2": "b",
5273
+ "\u0392": "B",
5274
+ "\u03B5": "e",
5275
+ "\u0395": "E",
5276
+ "\u03B9": "i",
5277
+ "\u0399": "I",
5278
+ "\u03BA": "k",
5279
+ "\u039A": "K",
5280
+ "\u03BF": "o",
5281
+ "\u039F": "O",
5282
+ "\u03C1": "p",
5283
+ "\u03A1": "P",
5284
+ "\u03C4": "t",
5285
+ "\u03A4": "T",
5286
+ "\u03C5": "u",
5287
+ "\u03A5": "Y",
5288
+ "\u03C7": "x",
5289
+ "\u03A7": "X"
5290
+ };
4984
5291
  PATTERNS = [
4985
5292
  {
4986
5293
  policyId: "prompt-injection.ignore-instructions",
4987
- regex: /\b(ignore|disregard|forget|override)\b[^.\n]{0,40}\b(all\s+)?(previous|prior|above|earlier|system|developer)\b[^.\n]{0,20}\b(instructions?|prompts?|rules?|context)\b/gi,
5294
+ regex: /\b(ignore|disregard|forget|override)\b[^.]{0,40}\b(all\s+)?(previous|prior|above|earlier|system|developer)\b[^.]{0,20}\b(instructions?|prompts?|rules?|context)\b/gi,
4988
5295
  confidence: 0.4
4989
5296
  },
4990
5297
  {
4991
5298
  policyId: "prompt-injection.reveal-context",
4992
- regex: /\b(reveal|show|print|expose|leak|dump|repeat)\b[^.\n]{0,30}\b(your\s+)?(system\s+prompt|instructions?|memory|secrets?|hidden\s+context|api\s*keys?)\b/gi,
5299
+ regex: /\b(reveal|show|print|expose|leak|dump|repeat)\b[^.]{0,30}\b(your\s+)?(system\s+prompt|instructions?|memory|secrets?|hidden\s+context|api\s*keys?)\b/gi,
4993
5300
  confidence: 0.45
4994
5301
  },
4995
5302
  {
4996
5303
  policyId: "prompt-injection.role-override",
4997
- regex: /\b(you\s+are\s+now|from\s+now\s+on|act\s+as|pretend\s+to\s+be)\b[^.\n]{0,40}\b(unrestricted|jailbroken|dan|admin|developer\s+mode|no\s+rules)\b/gi,
5304
+ regex: /\b(you\s+are\s+now|from\s+now\s+on|act\s+as|pretend\s+to\s+be)\b[^.]{0,40}\b(unrestricted|jailbroken|dan|admin|developer\s+mode|no\s+rules)\b/gi,
4998
5305
  confidence: 0.4
4999
5306
  },
5000
5307
  {
5001
5308
  policyId: "prompt-injection.priority-override",
5002
- regex: /\b(treat|follow|obey)\b[^.\n]{0,30}\b(the\s+)?(following|external|below)\b[^.\n]{0,20}\b(as\s+)?(instructions?|higher\s+priority|commands?)\b/gi,
5309
+ regex: /\b(treat|follow|obey)\b[^.]{0,30}\b(the\s+)?(following|external|below)\b[^.]{0,20}\b(as\s+)?(instructions?|higher\s+priority|commands?)\b/gi,
5003
5310
  confidence: 0.35
5004
5311
  }
5005
5312
  ];
@@ -6149,6 +6456,140 @@ var init_read_source = __esm(() => {
6149
6456
  init_contained_path();
6150
6457
  });
6151
6458
 
6459
+ // src/security/credential-shape.ts
6460
+ function isDeniedForMcpChild(name, value) {
6461
+ if (value !== undefined && CREDENTIAL_IN_VALUE_RE.test(value))
6462
+ return true;
6463
+ const upper = name.toUpperCase();
6464
+ if (MCP_ENV_ALLOW_EXACT.includes(upper))
6465
+ return false;
6466
+ const conditional = VALUE_DEPENDENT[upper];
6467
+ if (conditional !== undefined) {
6468
+ return value === undefined ? true : conditional(value);
6469
+ }
6470
+ if (EXTERNAL_ENV_DENY.some((denied) => denied.toUpperCase() === upper))
6471
+ return true;
6472
+ if (MCP_ENV_DENY.includes(upper))
6473
+ return true;
6474
+ if (EXTERNAL_ENV_PREFIX_SWEEPS.some((prefix) => upper.startsWith(prefix.toUpperCase())))
6475
+ return true;
6476
+ if (MCP_ENV_PREFIX_SWEEPS.some((prefix) => upper.startsWith(prefix)))
6477
+ return true;
6478
+ return SECRET_SEGMENT_RE.test(upper) || SECRET_SUBSTRING_RE.test(upper) || GLUED_SECRET_RE.test(upper) || CONNECTION_STRING_RE.test(upper);
6479
+ }
6480
+ var EXTERNAL_ENV_DENY, EXTERNAL_ENV_PREFIX_SWEEPS, MCP_ENV_ALLOW_EXACT, MCP_ENV_DENY, MCP_ENV_PREFIX_SWEEPS, SECRET_SEGMENTS, SECRET_SEGMENT_RE, SECRET_SUBSTRING_RE, GLUED_SECRET_RE, CONNECTION_STRING_RE, CREDENTIAL_IN_VALUE_RE, VALUE_DEPENDENT;
6481
+ var init_credential_shape = __esm(() => {
6482
+ EXTERNAL_ENV_DENY = [
6483
+ "ANTHROPIC_API_KEY",
6484
+ "ANTHROPIC_AUTH_TOKEN",
6485
+ "ANTHROPIC_BASE_URL",
6486
+ "ANTHROPIC_MODEL",
6487
+ "CLAUDE_CONFIG_DIR",
6488
+ "CODEX_HOME",
6489
+ "CLAUDECODE"
6490
+ ];
6491
+ EXTERNAL_ENV_PREFIX_SWEEPS = ["CLAUDE_CODE_", "KERYX_"];
6492
+ MCP_ENV_ALLOW_EXACT = ["PWD", "OLDPWD"];
6493
+ MCP_ENV_DENY = [
6494
+ "SSH_AUTH_SOCK",
6495
+ "SSH_AGENT_PID",
6496
+ "GPG_AGENT_INFO",
6497
+ "GNUPGHOME",
6498
+ "GNOME_KEYRING_CONTROL",
6499
+ "GNOME_KEYRING_PID",
6500
+ "KDE_FULL_SESSION",
6501
+ "GIT_ASKPASS",
6502
+ "SSH_ASKPASS",
6503
+ "SUDO_ASKPASS",
6504
+ "GIT_CREDENTIAL_HELPER",
6505
+ "GIT_SSH_COMMAND",
6506
+ "KUBECONFIG",
6507
+ "NETRC",
6508
+ "PGPASSFILE",
6509
+ "PGSERVICEFILE",
6510
+ "GOOGLE_APPLICATION_CREDENTIALS",
6511
+ "CLOUDSDK_CONFIG",
6512
+ "DOCKER_CONFIG",
6513
+ "AWS_SHARED_CREDENTIALS_FILE",
6514
+ "AWS_CONFIG_FILE",
6515
+ "BOTO_CONFIG",
6516
+ "AZURE_CONFIG_DIR",
6517
+ "CURLRC",
6518
+ "WGETRC",
6519
+ "NPMRC",
6520
+ "NPM_CONFIG_USERCONFIG",
6521
+ "PIP_CONFIG_FILE",
6522
+ "RCLONE_CONFIG",
6523
+ "MAVEN_SETTINGS",
6524
+ "GIT_CONFIG",
6525
+ "GIT_CONFIG_GLOBAL",
6526
+ "HGRCPATH",
6527
+ "ANSIBLE_VAULT_PASSWORD_FILE",
6528
+ "AZURE_CLIENT_CERTIFICATE_PATH",
6529
+ "ARM_CLIENT_CERTIFICATE_PATH",
6530
+ "VAULT_CLIENT_CERT",
6531
+ "VAULT_CLIENT_KEY",
6532
+ "SSL_CLIENT_CERT_PATH",
6533
+ "SSL_CLIENT_KEY_FILE",
6534
+ "IDENTITY_FILE",
6535
+ "SSH_IDENTITY_FILE",
6536
+ "GCP_SERVICE_ACCOUNT",
6537
+ "XAUTHORITY",
6538
+ "OPENAI_API_KEY",
6539
+ "GEMINI_API_KEY",
6540
+ "GOOGLE_API_KEY",
6541
+ "GROQ_API_KEY",
6542
+ "MISTRAL_API_KEY",
6543
+ "XAI_API_KEY",
6544
+ "DEEPSEEK_API_KEY",
6545
+ "OPENROUTER_API_KEY",
6546
+ "TOGETHER_API_KEY",
6547
+ "PERPLEXITY_API_KEY"
6548
+ ];
6549
+ MCP_ENV_PREFIX_SWEEPS = [
6550
+ "OP_SESSION",
6551
+ "BW_SESSION"
6552
+ ];
6553
+ SECRET_SEGMENTS = [
6554
+ "KEY",
6555
+ "KEYS",
6556
+ "TOKEN",
6557
+ "TOKENS",
6558
+ "SECRET",
6559
+ "SECRETS",
6560
+ "PASSWORD",
6561
+ "PASSWD",
6562
+ "PWD",
6563
+ "PASS",
6564
+ "PASSPHRASE",
6565
+ "PASSCODE",
6566
+ "PASSKEY",
6567
+ "CREDENTIAL",
6568
+ "CREDENTIALS",
6569
+ "CREDS",
6570
+ "AUTH",
6571
+ "AUTHORIZATION",
6572
+ "BEARER",
6573
+ "JWT",
6574
+ "COOKIE",
6575
+ "SIGNATURE",
6576
+ "APIKEY",
6577
+ "SECRETKEY",
6578
+ "ACCESSKEY",
6579
+ "AUTHTOKEN",
6580
+ "PAT",
6581
+ "WEBHOOK"
6582
+ ];
6583
+ SECRET_SEGMENT_RE = new RegExp(`(^|_)(${SECRET_SEGMENTS.join("|")})($|_)`);
6584
+ SECRET_SUBSTRING_RE = /(PASSWORD|PASSWD|PASSPHRASE|PASSCODE|SSHPASS|SECRET|CREDENTIAL|APIKEY|KEYFILE|PRIVKEY)/;
6585
+ GLUED_SECRET_RE = /(PRIVATE|SECRET|ACCESS|REFRESH|SESSION|CLIENT|API|APP)(KEY|TOKEN|SECRET)($|_)|DB(PASS|PWD)($|_)/;
6586
+ CONNECTION_STRING_RE = /(^|_)(DSN|CONNECTION_STRING|CONNECTIONSTRING)($|_)/;
6587
+ CREDENTIAL_IN_VALUE_RE = /:\/\/[^/\s@]*(:[^/\s@]*)?@/;
6588
+ VALUE_DEPENDENT = {
6589
+ DOCKER_HOST: (value) => !value.startsWith("unix://") && !value.startsWith("fd://")
6590
+ };
6591
+ });
6592
+
6152
6593
  // src/security/audit-harness/codex-toml.ts
6153
6594
  function stripComment(line) {
6154
6595
  let quoted = false;
@@ -18357,8 +18798,11 @@ var init_impact_evidence = __esm(() => {
18357
18798
  // src/security/service.ts
18358
18799
  var exports_service2 = {};
18359
18800
  __export(exports_service2, {
18801
+ EXTERNAL_ENV_DENY: () => EXTERNAL_ENV_DENY,
18802
+ EXTERNAL_ENV_PREFIX_SWEEPS: () => EXTERNAL_ENV_PREFIX_SWEEPS,
18360
18803
  addBaselineEntry: () => addBaselineEntry,
18361
18804
  analyze: () => analyze,
18805
+ appendIncident: () => appendIncident,
18362
18806
  appendLogRecord: () => appendLogRecord,
18363
18807
  applyAuditProposal: () => applyAuditProposal,
18364
18808
  auditGate: () => auditGate,
@@ -18366,13 +18810,20 @@ __export(exports_service2, {
18366
18810
  createImpactEvidenceProvider: () => createImpactEvidenceProvider,
18367
18811
  createSecurityService: () => createSecurityService,
18368
18812
  defaultBaselinePath: () => defaultBaselinePath,
18813
+ detectEntropy: () => detectEntropy,
18814
+ detectSecrets: () => detectSecrets,
18369
18815
  hostDeliveryStatus: () => hostDeliveryStatus,
18816
+ isDeniedForMcpChild: () => isDeniedForMcpChild,
18817
+ isEntropyBackendEnabled: () => isEntropyBackendEnabled,
18370
18818
  loadSecurityConfig: () => loadSecurityConfig,
18819
+ looksSecretShaped: () => looksSecretShaped,
18820
+ looksSecretShapedIn: () => looksSecretShapedIn,
18371
18821
  memoizeResolved: () => memoizeResolved,
18372
18822
  normalizeRequestFiles: () => normalizeRequestFiles,
18373
18823
  readLogRecords: () => readLogRecords,
18374
18824
  redactSensitiveText: () => redactSensitiveText,
18375
18825
  renderEvidenceBlock: () => renderEvidenceBlock,
18826
+ resetEntropyGateForTests: () => resetEntropyGateForTests,
18376
18827
  resolveImpactEvidenceConfig: () => resolveImpactEvidenceConfig,
18377
18828
  resolveImpactEvidenceConfigTrusted: () => resolveImpactEvidenceConfigTrusted,
18378
18829
  runGate: () => runGate,
@@ -18381,6 +18832,7 @@ __export(exports_service2, {
18381
18832
  runScan: () => runScan,
18382
18833
  runScanPath: () => runScanPath,
18383
18834
  scanContent: () => scanContent,
18835
+ setEntropyBackendEnabledForTests: () => setEntropyBackendEnabledForTests,
18384
18836
  sourceForFileRead: () => sourceForFileRead2,
18385
18837
  validateSerializedOutput: () => validateSerializedOutput,
18386
18838
  verifyConfigChecksum: () => verifyConfigChecksum
@@ -18626,6 +19078,11 @@ var init_service4 = __esm(() => {
18626
19078
  init_path_scan();
18627
19079
  init_read_source();
18628
19080
  init_redact();
19081
+ init_secrets();
19082
+ init_entropy();
19083
+ init_incidents();
19084
+ init_entropy_gate();
19085
+ init_credential_shape();
18629
19086
  init_audit_harness();
18630
19087
  init_config2();
18631
19088
  init_impact_evidence();