@mrciphersmith/keryx 0.3.20 → 0.3.21

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 +73 -16
  2. package/dist/core.js +37 -12
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9080,6 +9080,24 @@ var init_config = __esm(() => {
9080
9080
  });
9081
9081
 
9082
9082
  // src/security/detect/entropy.ts
9083
+ function isAdjacentLabel(content, start) {
9084
+ const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
9085
+ if (ADJACENT_LABEL.test(before)) {
9086
+ return true;
9087
+ }
9088
+ if (before.trim().length > 0) {
9089
+ return false;
9090
+ }
9091
+ const lineStart = content.lastIndexOf(`
9092
+ `, start - 1);
9093
+ if (lineStart < 0) {
9094
+ return false;
9095
+ }
9096
+ const prevLineStart = content.lastIndexOf(`
9097
+ `, lineStart - 1) + 1;
9098
+ const prevLine = content.slice(prevLineStart, lineStart);
9099
+ return CONTINUATION_LABEL_LINE_RE.test(prevLine);
9100
+ }
9083
9101
  function isHexBlob(value) {
9084
9102
  return HEX_BLOB.test(value) && /[a-f]/i.test(value);
9085
9103
  }
@@ -9279,12 +9297,19 @@ function looksSecretShapedIn(text) {
9279
9297
  return secretShapedCandidatesIn(text).length > 0;
9280
9298
  }
9281
9299
  function labelledPieceQualifies(piece) {
9282
- const base = bareShapeQualifies(piece);
9283
- if (base.qualifies) {
9284
- return { entropy: base.entropy };
9285
- }
9286
9300
  if (isAllowShapedValue(piece)) {
9287
- return { entropy: base.entropy };
9301
+ return { entropy: shannonEntropy(piece) };
9302
+ }
9303
+ if (isHexBlob(piece)) {
9304
+ return { entropy: shannonEntropy(piece) };
9305
+ }
9306
+ if (!/[0-9]/.test(piece) && !/[+=]/.test(piece)) {
9307
+ return null;
9308
+ }
9309
+ const withoutSeparators = piece.replace(/[-_.+]/g, "");
9310
+ const entropy = shannonEntropy(withoutSeparators.length > 0 ? withoutSeparators : piece);
9311
+ if (entropy >= 3.6) {
9312
+ return { entropy };
9288
9313
  }
9289
9314
  return null;
9290
9315
  }
@@ -9323,9 +9348,9 @@ function detectEntropy(content) {
9323
9348
  if (urlSpans.some((u) => start < u.end && end > u.start)) {
9324
9349
  continue;
9325
9350
  }
9326
- const { qualifies, entropy } = bareShapeQualifies(value);
9327
- if (qualifies) {
9328
- matches.push(buildMatch(start, end, value, entropy));
9351
+ const found = labelledPieceQualifies(value);
9352
+ if (found !== null) {
9353
+ matches.push(buildMatch(start, end, value, found.entropy));
9329
9354
  }
9330
9355
  }
9331
9356
  TOKEN.lastIndex = 0;
@@ -9340,8 +9365,7 @@ function detectEntropy(content) {
9340
9365
  if (matches.some((existing) => start < existing.end && end > existing.start)) {
9341
9366
  continue;
9342
9367
  }
9343
- const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
9344
- if (!ADJACENT_LABEL.test(before)) {
9368
+ if (!isAdjacentLabel(content, start)) {
9345
9369
  continue;
9346
9370
  }
9347
9371
  const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
@@ -9363,9 +9387,10 @@ function detectEntropy(content) {
9363
9387
  }
9364
9388
  return matches.sort((a, b) => a.start - b.start);
9365
9389
  }
9366
- 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;
9390
+ var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), FILLER_WORDS = "is|was|set|to|the|value|now", ADJACENT_LABEL, CONTINUATION_LABEL_LINE_RE, 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;
9367
9391
  var init_entropy = __esm(() => {
9368
- ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*["']?$`);
9392
+ ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*` + `(?:\\s+(?:${caseless(FILLER_WORDS)})){0,2}` + `["']?\\s*[:=]?\\s*["']?$`);
9393
+ CONTINUATION_LABEL_LINE_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*\\\\\\s*$`);
9369
9394
  LABEL_ASSIGNMENT_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${SENSITIVE_LABEL_WORDS})[A-Za-z0-9_-]*"?\\s*=\\s*"?([A-Za-z0-9+/_=-]{6,})`, "gi");
9370
9395
  TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
9371
9396
  TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
@@ -18093,7 +18118,7 @@ var package_default;
18093
18118
  var init_package = __esm(() => {
18094
18119
  package_default = {
18095
18120
  name: "@mrciphersmith/keryx",
18096
- version: "0.3.20",
18121
+ version: "0.3.21",
18097
18122
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
18098
18123
  private: false,
18099
18124
  publishConfig: {
@@ -129152,6 +129177,37 @@ function createSystemWebWorkerRunner() {
129152
129177
 
129153
129178
  // src/harness/web/outbound-secret.ts
129154
129179
  init_service4();
129180
+ var HEX32_FRAGMENT = "[0-9a-f]{32}";
129181
+ var SHA40_FRAGMENT = "[0-9a-f]{40}";
129182
+ var GIST_HEX32_PATH_RE = new RegExp(`^/(?:[^/]+/)?(${HEX32_FRAGMENT})/?$`, "i");
129183
+ var GITHUB_COMMIT_OR_BLOB_PATH_RE = new RegExp(`/(?:commit|blob)/(${SHA40_FRAGMENT})(?:/|$)`, "i");
129184
+ var GITLAB_COMMIT_PATH_RE = new RegExp(`/-/commit/(${SHA40_FRAGMENT})(?:/|$)`, "i");
129185
+ function knownPublicIdentifierIn(url) {
129186
+ const host = url.hostname.toLowerCase();
129187
+ if (host === "gist.github.com") {
129188
+ return GIST_HEX32_PATH_RE.exec(url.pathname)?.[1] ?? null;
129189
+ }
129190
+ if (host === "github.com") {
129191
+ return GITHUB_COMMIT_OR_BLOB_PATH_RE.exec(url.pathname)?.[1] ?? null;
129192
+ }
129193
+ if (host === "gitlab.com") {
129194
+ return GITLAB_COMMIT_PATH_RE.exec(url.pathname)?.[1] ?? null;
129195
+ }
129196
+ return null;
129197
+ }
129198
+ function stripKnownPublicIdentifier(text) {
129199
+ let url;
129200
+ try {
129201
+ url = new URL(text);
129202
+ } catch {
129203
+ return text;
129204
+ }
129205
+ const identifier = knownPublicIdentifierIn(url);
129206
+ if (identifier === null) {
129207
+ return text;
129208
+ }
129209
+ return text.replace(identifier, "0".repeat(identifier.length));
129210
+ }
129155
129211
  function safeDecodeComponent(value) {
129156
129212
  try {
129157
129213
  return decodeURIComponent(value);
@@ -129195,13 +129251,14 @@ function anyComponentLooksSecret(text) {
129195
129251
  return false;
129196
129252
  }
129197
129253
  function containsOutboundSecret(text) {
129198
- if (detectSecrets(text).length > 0) {
129254
+ const scanText = stripKnownPublicIdentifier(text);
129255
+ if (detectSecrets(scanText).length > 0) {
129199
129256
  return true;
129200
129257
  }
129201
- if (isEntropyBackendEnabled() && detectEntropy(text).length > 0) {
129258
+ if (isEntropyBackendEnabled() && detectEntropy(scanText).length > 0) {
129202
129259
  return true;
129203
129260
  }
129204
- return anyComponentLooksSecret(text);
129261
+ return anyComponentLooksSecret(scanText);
129205
129262
  }
129206
129263
  async function recordOutboundSecretFinding(cwd, tool, subject) {
129207
129264
  try {
package/dist/core.js CHANGED
@@ -4029,6 +4029,24 @@ var init_config2 = __esm(() => {
4029
4029
  });
4030
4030
 
4031
4031
  // src/security/detect/entropy.ts
4032
+ function isAdjacentLabel(content, start) {
4033
+ const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
4034
+ if (ADJACENT_LABEL.test(before)) {
4035
+ return true;
4036
+ }
4037
+ if (before.trim().length > 0) {
4038
+ return false;
4039
+ }
4040
+ const lineStart = content.lastIndexOf(`
4041
+ `, start - 1);
4042
+ if (lineStart < 0) {
4043
+ return false;
4044
+ }
4045
+ const prevLineStart = content.lastIndexOf(`
4046
+ `, lineStart - 1) + 1;
4047
+ const prevLine = content.slice(prevLineStart, lineStart);
4048
+ return CONTINUATION_LABEL_LINE_RE.test(prevLine);
4049
+ }
4032
4050
  function isHexBlob(value) {
4033
4051
  return HEX_BLOB.test(value) && /[a-f]/i.test(value);
4034
4052
  }
@@ -4228,12 +4246,19 @@ function looksSecretShapedIn(text) {
4228
4246
  return secretShapedCandidatesIn(text).length > 0;
4229
4247
  }
4230
4248
  function labelledPieceQualifies(piece) {
4231
- const base = bareShapeQualifies(piece);
4232
- if (base.qualifies) {
4233
- return { entropy: base.entropy };
4234
- }
4235
4249
  if (isAllowShapedValue(piece)) {
4236
- return { entropy: base.entropy };
4250
+ return { entropy: shannonEntropy(piece) };
4251
+ }
4252
+ if (isHexBlob(piece)) {
4253
+ return { entropy: shannonEntropy(piece) };
4254
+ }
4255
+ if (!/[0-9]/.test(piece) && !/[+=]/.test(piece)) {
4256
+ return null;
4257
+ }
4258
+ const withoutSeparators = piece.replace(/[-_.+]/g, "");
4259
+ const entropy = shannonEntropy(withoutSeparators.length > 0 ? withoutSeparators : piece);
4260
+ if (entropy >= 3.6) {
4261
+ return { entropy };
4237
4262
  }
4238
4263
  return null;
4239
4264
  }
@@ -4272,9 +4297,9 @@ function detectEntropy(content) {
4272
4297
  if (urlSpans.some((u) => start < u.end && end > u.start)) {
4273
4298
  continue;
4274
4299
  }
4275
- const { qualifies, entropy } = bareShapeQualifies(value);
4276
- if (qualifies) {
4277
- matches.push(buildMatch(start, end, value, entropy));
4300
+ const found = labelledPieceQualifies(value);
4301
+ if (found !== null) {
4302
+ matches.push(buildMatch(start, end, value, found.entropy));
4278
4303
  }
4279
4304
  }
4280
4305
  TOKEN.lastIndex = 0;
@@ -4289,8 +4314,7 @@ function detectEntropy(content) {
4289
4314
  if (matches.some((existing) => start < existing.end && end > existing.start)) {
4290
4315
  continue;
4291
4316
  }
4292
- const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
4293
- if (!ADJACENT_LABEL.test(before)) {
4317
+ if (!isAdjacentLabel(content, start)) {
4294
4318
  continue;
4295
4319
  }
4296
4320
  const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
@@ -4312,9 +4336,10 @@ function detectEntropy(content) {
4312
4336
  }
4313
4337
  return matches.sort((a, b) => a.start - b.start);
4314
4338
  }
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;
4339
+ var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), FILLER_WORDS = "is|was|set|to|the|value|now", ADJACENT_LABEL, CONTINUATION_LABEL_LINE_RE, 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
4340
  var init_entropy = __esm(() => {
4317
- ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*["']?$`);
4341
+ ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*` + `(?:\\s+(?:${caseless(FILLER_WORDS)})){0,2}` + `["']?\\s*[:=]?\\s*["']?$`);
4342
+ CONTINUATION_LABEL_LINE_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*\\\\\\s*$`);
4318
4343
  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
4344
  TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
4320
4345
  TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.3.20",
3
+ "version": "0.3.21",
4
4
  "description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
5
5
  "private": false,
6
6
  "publishConfig": {