@moda-ai/cli 1.55.12 → 1.55.14

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.
@@ -10304,7 +10304,7 @@ var require_typescript = __commonJS((exports, module) => {
10304
10304
  identity: () => identity2,
10305
10305
  identitySourceMapConsumer: () => identitySourceMapConsumer,
10306
10306
  ignoreSourceNewlines: () => ignoreSourceNewlines,
10307
- ignoredPaths: () => ignoredPaths,
10307
+ ignoredPaths: () => ignoredPaths2,
10308
10308
  importFromModuleSpecifier: () => importFromModuleSpecifier,
10309
10309
  importSyntaxAffectsModuleResolution: () => importSyntaxAffectsModuleResolution,
10310
10310
  indexOfAnyCharCode: () => indexOfAnyCharCode,
@@ -16689,7 +16689,7 @@ ${lanes.join(`
16689
16689
  function getFileWatcherEventKind(oldTime, newTime) {
16690
16690
  return oldTime === 0 ? 0 : newTime === 0 ? 2 : 1;
16691
16691
  }
16692
- var ignoredPaths = ["/node_modules/.", "/.git", "/.#"];
16692
+ var ignoredPaths2 = ["/node_modules/.", "/.git", "/.#"];
16693
16693
  var curSysLog = noop;
16694
16694
  function sysLog(s) {
16695
16695
  return curSysLog(s);
@@ -16907,7 +16907,7 @@ ${lanes.join(`
16907
16907
  }
16908
16908
  }
16909
16909
  function isIgnoredPath(path, options) {
16910
- return some(ignoredPaths, (searchPath) => isInPath(path, searchPath)) || isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames2, getCurrentDirectory);
16910
+ return some(ignoredPaths2, (searchPath) => isInPath(path, searchPath)) || isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames2, getCurrentDirectory);
16911
16911
  }
16912
16912
  function isInPath(path, searchPath) {
16913
16913
  if (path.includes(searchPath))
@@ -31305,7 +31305,7 @@ ${lanes.join(`
31305
31305
  }
31306
31306
  }
31307
31307
  function containsIgnoredPath(path) {
31308
- return some(ignoredPaths, (p2) => path.includes(p2));
31308
+ return some(ignoredPaths2, (p2) => path.includes(p2));
31309
31309
  }
31310
31310
  function getContainingNodeArray(node) {
31311
31311
  if (!node.parent)
@@ -75258,11 +75258,11 @@ ${lanes.join(`
75258
75258
  const resolved = resolveStructuredTypeMembers(type);
75259
75259
  return !!(resolved.callSignatures.length || resolved.constructSignatures.length || resolved.members.get("bind") && isTypeSubtypeOf(type, globalFunctionType));
75260
75260
  }
75261
- function getTypeFacts(type, mask2) {
75262
- return getTypeFactsWorker(type, mask2) & mask2;
75261
+ function getTypeFacts(type, mask22) {
75262
+ return getTypeFactsWorker(type, mask22) & mask22;
75263
75263
  }
75264
- function hasTypeFacts(type, mask2) {
75265
- return getTypeFacts(type, mask2) !== 0;
75264
+ function hasTypeFacts(type, mask22) {
75265
+ return getTypeFacts(type, mask22) !== 0;
75266
75266
  }
75267
75267
  function getTypeFactsWorker(type, callerOnlyNeeds) {
75268
75268
  if (type.flags & (2097152 | 465829888)) {
@@ -110604,14 +110604,14 @@ ${lanes.join(`
110604
110604
  return factory2.createModifiersFromModifierFlags(newFlags);
110605
110605
  }
110606
110606
  function ensureModifierFlags(node) {
110607
- let mask2 = 131071 ^ (1 | 1024 | 16);
110607
+ let mask22 = 131071 ^ (1 | 1024 | 16);
110608
110608
  let additions = needsDeclare && !isAlwaysType(node) ? 128 : 0;
110609
110609
  const parentIsFile = node.parent.kind === 308;
110610
110610
  if (!parentIsFile || isBundledEmit && parentIsFile && isExternalModule(node.parent)) {
110611
- mask2 ^= 128;
110611
+ mask22 ^= 128;
110612
110612
  additions = 0;
110613
110613
  }
110614
- return maskModifierFlags(node, mask2, additions);
110614
+ return maskModifierFlags(node, mask22, additions);
110615
110615
  }
110616
110616
  function transformHeritageClauses(nodes) {
110617
110617
  return factory2.createNodeArray(filter(map(nodes, (clause) => factory2.updateHeritageClause(clause, visitNodes2(factory2.createNodeArray(filter(clause.types, (t) => {
@@ -121243,7 +121243,7 @@ ${lanes.join(`
121243
121243
  if (endsWith(path, "/node_modules/.staging")) {
121244
121244
  return removeSuffix(path, "/.staging");
121245
121245
  }
121246
- return some(ignoredPaths, (searchPath) => path.includes(searchPath)) ? undefined : path;
121246
+ return some(ignoredPaths2, (searchPath) => path.includes(searchPath)) ? undefined : path;
121247
121247
  }
121248
121248
  function perceivedOsRootLengthForWatching(pathComponents2, length2) {
121249
121249
  if (length2 <= 1)
@@ -163646,20 +163646,20 @@ ${options.prefix}` : `
163646
163646
  return rulesMapCache;
163647
163647
  }
163648
163648
  function getRuleActionExclusion(ruleAction) {
163649
- let mask2 = 0;
163649
+ let mask22 = 0;
163650
163650
  if (ruleAction & 1) {
163651
- mask2 |= 28;
163651
+ mask22 |= 28;
163652
163652
  }
163653
163653
  if (ruleAction & 2) {
163654
- mask2 |= 96;
163654
+ mask22 |= 96;
163655
163655
  }
163656
163656
  if (ruleAction & 28) {
163657
- mask2 |= 28;
163657
+ mask22 |= 28;
163658
163658
  }
163659
163659
  if (ruleAction & 96) {
163660
- mask2 |= 96;
163660
+ mask22 |= 96;
163661
163661
  }
163662
- return mask2;
163662
+ return mask22;
163663
163663
  }
163664
163664
  function createRulesMap(rules) {
163665
163665
  const map2 = buildMap(rules);
@@ -163704,7 +163704,7 @@ ${options.prefix}` : `
163704
163704
  return row * mapRowLength + column;
163705
163705
  }
163706
163706
  var maskBitSize = 5;
163707
- var mask = 31;
163707
+ var mask2 = 31;
163708
163708
  var mapRowLength = 166 + 1;
163709
163709
  var RulesPosition = ((RulesPosition2) => {
163710
163710
  RulesPosition2[RulesPosition2["StopRulesSpecific"] = 0] = "StopRulesSpecific";
@@ -163724,15 +163724,15 @@ ${options.prefix}` : `
163724
163724
  function getInsertionIndex(indexBitmap, maskPosition) {
163725
163725
  let index = 0;
163726
163726
  for (let pos = 0;pos <= maskPosition; pos += maskBitSize) {
163727
- index += indexBitmap & mask;
163727
+ index += indexBitmap & mask2;
163728
163728
  indexBitmap >>= maskBitSize;
163729
163729
  }
163730
163730
  return index;
163731
163731
  }
163732
163732
  function increaseInsertionIndex(indexBitmap, maskPosition) {
163733
- const value = (indexBitmap >> maskPosition & mask) + 1;
163734
- Debug.assert((value & mask) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
163735
- return indexBitmap & ~(mask << maskPosition) | value << maskPosition;
163733
+ const value = (indexBitmap >> maskPosition & mask2) + 1;
163734
+ Debug.assert((value & mask2) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
163735
+ return indexBitmap & ~(mask2 << maskPosition) | value << maskPosition;
163736
163736
  }
163737
163737
  function createTextRangeWithKind(pos, end, kind) {
163738
163738
  const textRangeWithKind = { pos, end, kind };
@@ -166304,7 +166304,7 @@ ${options.prefix}` : `
166304
166304
  identity: () => identity2,
166305
166305
  identitySourceMapConsumer: () => identitySourceMapConsumer,
166306
166306
  ignoreSourceNewlines: () => ignoreSourceNewlines,
166307
- ignoredPaths: () => ignoredPaths,
166307
+ ignoredPaths: () => ignoredPaths2,
166308
166308
  importFromModuleSpecifier: () => importFromModuleSpecifier,
166309
166309
  importSyntaxAffectsModuleResolution: () => importSyntaxAffectsModuleResolution,
166310
166310
  indexOfAnyCharCode: () => indexOfAnyCharCode,
@@ -177901,7 +177901,7 @@ import {
177901
177901
  lstatSync as lstatSync10,
177902
177902
  mkdtempSync as mkdtempSync2,
177903
177903
  mkdirSync as mkdirSync6,
177904
- readFileSync as readFileSync17,
177904
+ readFileSync as readFileSync16,
177905
177905
  readdirSync as readdirSync4,
177906
177906
  writeFileSync as writeFileSync6,
177907
177907
  rmSync as rmSync4
@@ -180097,7 +180097,7 @@ async function remoteAnalyzeFetch(url, apiKey, init, timeoutMs) {
180097
180097
 
180098
180098
  // src/harness-register/register.ts
180099
180099
  import * as fs from "node:fs";
180100
- import { mkdtempSync, readFileSync as readFileSync7, rmSync as rmSync3 } from "node:fs";
180100
+ import { mkdtempSync, readFileSync as readFileSync6, rmSync as rmSync3 } from "node:fs";
180101
180101
  import { Readable as Readable2 } from "node:stream";
180102
180102
  import { tmpdir } from "node:os";
180103
180103
  import { basename as basename4, join as join8, resolve as resolve4 } from "node:path";
@@ -180105,7 +180105,7 @@ import { basename as basename4, join as join8, resolve as resolve4 } from "node:
180105
180105
  // src/harness-register/collect-adapter.ts
180106
180106
  import { createHash as createHash4 } from "node:crypto";
180107
180107
  import { execFileSync } from "node:child_process";
180108
- import { copyFileSync, createReadStream, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, realpathSync as realpathSync4, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
180108
+ import { copyFileSync, createReadStream, existsSync as existsSync6, mkdirSync as mkdirSync4, realpathSync as realpathSync4, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
180109
180109
  import { dirname as dirname5, join as join7 } from "node:path";
180110
180110
 
180111
180111
  // ../../packages/harness-bundle/src/contract.ts
@@ -185388,15 +185388,16 @@ var CACHE_FILE_PATTERNS = [
185388
185388
  [/^\.yarn-integrity$/, "yarn_integrity"],
185389
185389
  [/^install-state\.gz$/, "yarn_install_state"]
185390
185390
  ];
185391
- var ENV_EXAMPLE_SUFFIXES = new Set(["example", "sample", "template", "dist", "defaults.example", "tpl"]);
185391
+ var ENV_TEMPLATE_WORDS = new Set(["example", "examples", "sample", "template", "dist", "tpl", "default", "defaults"]);
185392
185392
  function isEnvExampleName(name) {
185393
185393
  const lower = name.toLowerCase();
185394
- for (const prefix of [".env.", ".dev.vars.", "env."]) {
185395
- if (lower.startsWith(prefix) && ENV_EXAMPLE_SUFFIXES.has(lower.slice(prefix.length)))
185396
- return true;
185397
- }
185398
- return /^(example|sample|template)\.env$/.test(lower) || /\.env\.(example|sample|template)$/.test(lower);
185394
+ const base = /^(\.env|\.dev\.vars|env)\./.exec(lower);
185395
+ if (base)
185396
+ return lower.slice(base[0].length).split(".").some((part) => ENV_TEMPLATE_WORDS.has(part));
185397
+ const named = /^(.+)\.env$/.exec(lower);
185398
+ return named !== null && named[1].split(".").some((part) => ENV_TEMPLATE_WORDS.has(part));
185399
185399
  }
185400
+ var ENV_VALUE_FILE_RULES = new Set(["dotenv", "direnv", "wrangler_dev_vars"]);
185400
185401
  function secretPathRule(path) {
185401
185402
  const name = basenameOf(path);
185402
185403
  const lower = name.toLowerCase();
@@ -185417,7 +185418,7 @@ function secretPathRule(path) {
185417
185418
  return "key_material_file";
185418
185419
  if (/^id_(rsa|dsa|ecdsa|ed25519)/.test(lower))
185419
185420
  return "ssh_key";
185420
- if (lower === "credentials.json" || lower === "credentials" || lower === "credentials.yml" || lower === "credentials.yaml") {
185421
+ if (/^credentials(\.(json|ya?ml|toml|conf|cfg|ini|txt|env|properties|xml|csv))?$/.test(lower) || /\.credentials$/.test(lower)) {
185421
185422
  return "credentials_file";
185422
185423
  }
185423
185424
  if (/(^|\/)\.aws\/(credentials|config)$/.test(lowerPath))
@@ -185453,34 +185454,58 @@ function secretPathRule(path) {
185453
185454
  return "secrets_file";
185454
185455
  return null;
185455
185456
  }
185456
- function registryAuthRule(path, text) {
185457
+ var PROVIDER_MARKER = /^(?:sk-ant-(?:api|admin|oat)\d{2}-|sk-or-v1-|sk-(?:proj|svcacct|admin)-|sk-|(?:sk|rk)_(?:live|test)_|gh[pousr]_|github_pat_|glpat-|xox[abposr]-|whsec_|gsk_|hf_|xai-|npm_|pypi-|do[opr]_v1_|shp(?:at|ca|pa|ss)_|moda_(?:sk|ct)_)/;
185458
+ function registryAuthFindings(path, text) {
185457
185459
  const name = basenameOf(path).toLowerCase();
185458
185460
  const literal = (value) => value.trim() !== "" && !/^["']?\$\{?[A-Za-z_]/.test(value.trim());
185459
- if (name === ".npmrc") {
185460
- for (const match of text.matchAll(/(?:_authToken|_auth|_password)\s*=\s*(.*)/g)) {
185461
- if (literal(match[1] ?? ""))
185462
- return "npmrc_auth_token";
185463
- }
185464
- }
185465
- if (name === ".yarnrc.yml" || name === ".yarnrc") {
185466
- for (const match of text.matchAll(/npmAuth(?:Token|Ident)\s*:\s*(.*)/g)) {
185467
- if (literal(match[1] ?? ""))
185468
- return "yarnrc_auth_token";
185469
- }
185470
- }
185471
- if (name === ".pypirc") {
185472
- for (const match of text.matchAll(/^\s*password\s*[=:]\s*(.*)$/gm)) {
185473
- if (literal(match[1] ?? ""))
185474
- return "pypirc_password";
185461
+ const out = [];
185462
+ const scan = (pattern, rule2, check, yaml = false) => {
185463
+ for (const match of text.matchAll(pattern)) {
185464
+ if (!check(match[1] ?? ""))
185465
+ continue;
185466
+ const [from] = match.indices[1];
185467
+ let raw = match[1];
185468
+ if (yaml) {
185469
+ const quoted = /^\s*(?:"(?:[^"\\\n]|\\.)*"|'(?:[^'\n]|'')*')/.exec(raw);
185470
+ raw = quoted ? quoted[0] : raw.replace(/\s+#.*$/, "");
185471
+ }
185472
+ const lead = raw.length - raw.replace(/^[\s"']+/, "").length;
185473
+ const value = raw.replace(/[\s"']+$/, "");
185474
+ if (value.length <= lead)
185475
+ continue;
185476
+ out.push({ rule: rule2, line: 0, start: from + lead, end: from + value.length });
185475
185477
  }
185476
- }
185478
+ };
185479
+ if (name === ".npmrc")
185480
+ scan(/(?:_authToken|_auth|_password)\s*=\s*(.*)/dg, "npmrc_auth_token", literal);
185481
+ if (name === ".yarnrc.yml" || name === ".yarnrc")
185482
+ scan(/npmAuth(?:Token|Ident)\s*:\s*(.*)/dg, "yarnrc_auth_token", literal, name === ".yarnrc.yml");
185483
+ if (name === ".pypirc")
185484
+ scan(/^\s*password\s*[=:]\s*(.*)$/dgm, "pypirc_password", literal);
185477
185485
  if (name === "pip.conf" || name === "pip.ini" || name === "poetry.toml" || name === "bunfig.toml" || name === ".condarc") {
185478
- if (/https?:\/\/[^\s/:@]+:[^\s/@$]+@/.test(text))
185479
- return "registry_url_credentials";
185486
+ scan(/https?:\/\/[^\s/:@]+:([^\s/?#]+)@[^\s/?#@]+/dg, "registry_url_credentials", (value) => !value.startsWith("$"));
185487
+ }
185488
+ if (name === "settings.xml")
185489
+ scan(/<password>(?!\s*\$\{)([^<]+)<\/password>/dg, "maven_settings_password", () => true);
185490
+ out.sort((a, b) => a.start - b.start);
185491
+ let line = 1;
185492
+ let cursor = 0;
185493
+ for (const finding of out) {
185494
+ for (;cursor < finding.start; cursor += 1)
185495
+ if (text.charCodeAt(cursor) === 10)
185496
+ line += 1;
185497
+ finding.line = line;
185480
185498
  }
185481
- if (name === "settings.xml" && /<password>(?!\s*\$\{)[^<]+<\/password>/.test(text))
185482
- return "maven_settings_password";
185483
- return null;
185499
+ return out;
185500
+ }
185501
+ function registryAuthRule(path, text) {
185502
+ return registryAuthFindings(path, text)[0]?.rule ?? null;
185503
+ }
185504
+ var LOG_DIRS = new Set(["logs", ".logs"]);
185505
+ function runtimeLogRule(path, status) {
185506
+ if (status === "tracked")
185507
+ return null;
185508
+ return /\.log(\.\d+)?$/i.test(basenameOf(path)) ? { reason: "regenerable_output", rule: "runtime_log" } : null;
185484
185509
  }
185485
185510
  function dirRule(dirPath, status) {
185486
185511
  const name = basenameOf(dirPath);
@@ -185491,6 +185516,8 @@ function dirRule(dirPath, status) {
185491
185516
  }
185492
185517
  if (status === "tracked")
185493
185518
  return null;
185519
+ if (LOG_DIRS.has(name))
185520
+ return { reason: "regenerable_output", rule: "runtime_log" };
185494
185521
  if (DEPENDENCY_DIRS.has(name) || DEPENDENCY_DIR_SUFFIXES.some((s) => hasSuffix(dirPath, s))) {
185495
185522
  return { reason: "dependency_install", rule: name };
185496
185523
  }
@@ -186222,12 +186249,21 @@ class EnvCollector {
186222
186249
  }
186223
186250
  }
186224
186251
  scanDotenv(path, lines) {
186252
+ let open = null;
186225
186253
  lines.forEach((line, index) => {
186254
+ if (open) {
186255
+ if (closesQuote(line, open, 0))
186256
+ open = null;
186257
+ return;
186258
+ }
186226
186259
  const m = line.match(new RegExp(`^\\s*(?:export\\s+)?(${NAME})\\s*=\\s*(.*)$`));
186227
186260
  if (!m)
186228
186261
  return;
186229
- const value = stripInlineValue(m[2] ?? "");
186230
- this.add(m[1], `${path}:${index + 1}`, value !== "");
186262
+ this.add(m[1], `${path}:${index + 1}`, false);
186263
+ const value = m[2] ?? "";
186264
+ const quote = value[0];
186265
+ if ((quote === '"' || quote === "'" || quote === "`") && !closesQuote(value, quote, 1))
186266
+ open = quote;
186231
186267
  });
186232
186268
  }
186233
186269
  scanDestructuring(path, line, lineNo) {
@@ -186468,6 +186504,15 @@ function shellExpandable(line) {
186468
186504
  }
186469
186505
  return out;
186470
186506
  }
186507
+ function closesQuote(text, quote, from) {
186508
+ for (let i = from;i < text.length; i += 1) {
186509
+ if (text[i] === "\\" && quote !== "'")
186510
+ i += 1;
186511
+ else if (text[i] === quote)
186512
+ return true;
186513
+ }
186514
+ return false;
186515
+ }
186471
186516
  function stripInlineValue(raw) {
186472
186517
  let value = raw.trim();
186473
186518
  if (/^["']/.test(value)) {
@@ -186587,6 +186632,24 @@ function lsFiles(root, args) {
186587
186632
  throw new Error(`git ls-files ${args.join(" ")} failed in ${root}`);
186588
186633
  return result.stdout.split("\x00").filter((entry) => entry !== "");
186589
186634
  }
186635
+ function ignoredPaths(root) {
186636
+ const result = git(root, ["status", "--porcelain=v1", "-z", "--ignored=matching", "--untracked-files=all"]);
186637
+ if (!result.ok)
186638
+ throw new Error(`git status --ignored failed in ${root}`);
186639
+ const out = [];
186640
+ const fields = result.stdout.split("\x00");
186641
+ for (let i = 0;i < fields.length; i += 1) {
186642
+ const field = fields[i];
186643
+ if (field.length < 4)
186644
+ continue;
186645
+ const code = field.slice(0, 2);
186646
+ if (code === "!!")
186647
+ out.push(field.slice(3));
186648
+ else if (code[0] === "R" || code[0] === "C")
186649
+ i += 1;
186650
+ }
186651
+ return out;
186652
+ }
186590
186653
  function gitMetadata(root) {
186591
186654
  const commit = git(root, ["rev-parse", "--verify", "-q", "HEAD"]);
186592
186655
  const branch = git(root, ["symbolic-ref", "--short", "-q", "HEAD"]);
@@ -186877,7 +186940,6 @@ function shannonEntropy(value) {
186877
186940
  return entropy;
186878
186941
  }
186879
186942
  var notPlaceholder = (value) => !looksLikePlaceholder(value);
186880
- var LOCAL_HOSTS = /^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\]|::1|host\.docker\.internal|[a-z0-9_-]+)(:\d+)?$/i;
186881
186943
  var WEAK_PASSWORDS = new Set([
186882
186944
  "password",
186883
186945
  "pass",
@@ -186903,9 +186965,20 @@ var WEAK_PASSWORDS = new Set([
186903
186965
  var SECRET_RULES = [
186904
186966
  {
186905
186967
  name: "private_key_block",
186906
- pattern: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:(?:\r?\n|(?:\\r)?\\n)[A-Za-z][A-Za-z0-9-]*:[^\r\n\\]*)*(?:\\n|\\r|\s)+([A-Za-z0-9+/=]{32,})/g,
186968
+ pattern: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:(?:\r?\n|(?:\\r)?\\n)[A-Za-z][A-Za-z0-9-]*:[^\r\n\\]*)*(?:\\n|\\r|\s)+([A-Za-z0-9+/=]{32,8192})/g,
186907
186969
  group: 1,
186908
- accept: notPlaceholder
186970
+ accept: notPlaceholder,
186971
+ withholdFile: true
186972
+ },
186973
+ {
186974
+ name: "private_key_block",
186975
+ pattern: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----([^-]{1,8192}?)-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g,
186976
+ group: 1,
186977
+ accept: (body) => {
186978
+ const compact = body.replace(/\\[rn]|\s/g, "");
186979
+ return compact.length >= 16 && /^[A-Za-z0-9+/=:,-]+$/.test(compact) && !/x{4,}|X{4,}|\.\.\./.test(compact);
186980
+ },
186981
+ withholdFile: true
186909
186982
  },
186910
186983
  {
186911
186984
  name: "aws_access_key_id",
@@ -186919,57 +186992,60 @@ var SECRET_RULES = [
186919
186992
  group: 1,
186920
186993
  accept: (value) => !/EXAMPLE/.test(value) && notPlaceholder(value)
186921
186994
  },
186922
- { name: "anthropic_api_key", pattern: /\bsk-ant-(?:api|admin|oat)\d{2}-([A-Za-z0-9_-]{32,})/g, group: 1, accept: (v) => randomLooking(v, 32) },
186923
- { name: "openrouter_api_key", pattern: /\bsk-or-v1-[a-f0-9]{48,}\b/g, accept: notPlaceholder },
186995
+ { name: "anthropic_api_key", pattern: /\bsk-ant-(?:api|admin|oat)\d{2}-([A-Za-z0-9_-]{32,512})(?![A-Za-z0-9_-])/g, group: 1, accept: (v) => randomLooking(v, 32) },
186996
+ { name: "openrouter_api_key", pattern: /\bsk-or-v1-[a-f0-9]{48,512}\b/g, accept: notPlaceholder },
186924
186997
  {
186925
186998
  name: "openai_api_key",
186926
- pattern: /\bsk-(?!ant-|or-)(?:proj-|svcacct-|admin-|None-)?([A-Za-z0-9_-]{32,})/g,
186999
+ pattern: /\bsk-(?!ant-|or-)(?:proj-|svcacct-|admin-|None-)?([A-Za-z0-9_-]{32,512})(?![A-Za-z0-9_-])/g,
186927
187000
  group: 1,
186928
187001
  accept: (value) => randomLooking(value, 32)
186929
187002
  },
186930
- { name: "groq_api_key", pattern: /\bgsk_[A-Za-z0-9]{40,}\b/g, accept: notPlaceholder },
187003
+ { name: "groq_api_key", pattern: /\bgsk_[A-Za-z0-9]{40,512}\b/g, accept: notPlaceholder },
186931
187004
  { name: "google_api_key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g, accept: notPlaceholder },
186932
- { name: "huggingface_token", pattern: /\bhf_[A-Za-z0-9]{30,}\b/g, accept: notPlaceholder },
186933
- { name: "xai_api_key", pattern: /\bxai-[A-Za-z0-9]{40,}\b/g, accept: notPlaceholder },
186934
- { name: "github_token", pattern: /\b(?:gh[pousr]_([A-Za-z0-9]{36,})|github_pat_([A-Za-z0-9_]{60,}))\b/g, accept: (_v, m) => randomLooking(m[1] ?? m[2] ?? "", 36) },
186935
- { name: "gitlab_token", pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g, accept: notPlaceholder },
186936
- { name: "slack_token", pattern: /\bxox[abposr]-[A-Za-z0-9-]{10,}/g, accept: notPlaceholder },
187005
+ { name: "huggingface_token", pattern: /\bhf_[A-Za-z0-9]{30,512}\b/g, accept: notPlaceholder },
187006
+ { name: "xai_api_key", pattern: /\bxai-[A-Za-z0-9]{40,512}\b/g, accept: notPlaceholder },
187007
+ { name: "github_token", pattern: /\b(?:gh[pousr]_([A-Za-z0-9]{36,512})|github_pat_([A-Za-z0-9_]{60,512}))\b/g, accept: (_v, m) => randomLooking(m[1] ?? m[2] ?? "", 36) },
187008
+ { name: "gitlab_token", pattern: /\bglpat-[A-Za-z0-9_-]{20,512}\b/g, accept: notPlaceholder },
187009
+ { name: "slack_token", pattern: /\bxox[abposr]-[A-Za-z0-9-]{10,512}(?![A-Za-z0-9-])/g, accept: notPlaceholder },
186937
187010
  {
186938
187011
  name: "slack_webhook",
186939
- pattern: /https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{16,}/g,
187012
+ pattern: /https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{16,512}/g,
186940
187013
  accept: notPlaceholder
186941
187014
  },
186942
- { name: "stripe_secret_key", pattern: /\b(?:sk|rk)_(?:live|test)_([A-Za-z0-9]{20,})\b/g, group: 1, accept: (v) => randomLooking(v, 20) },
186943
- { name: "stripe_webhook_secret", pattern: /\bwhsec_[A-Za-z0-9]{24,}\b/g, accept: notPlaceholder },
187015
+ { name: "stripe_secret_key", pattern: /\b(?:sk|rk)_(?:live|test)_([A-Za-z0-9]{20,512})\b/g, group: 1, accept: (v) => randomLooking(v, 20) },
187016
+ { name: "stripe_webhook_secret", pattern: /\bwhsec_[A-Za-z0-9]{24,512}\b/g, accept: notPlaceholder },
186944
187017
  { name: "sendgrid_api_key", pattern: /\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/g, accept: notPlaceholder },
186945
187018
  { name: "npm_token", pattern: /\bnpm_[A-Za-z0-9]{36}\b/g, accept: notPlaceholder },
186946
- { name: "pypi_token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,}/g, accept: notPlaceholder },
187019
+ { name: "pypi_token", pattern: /\bpypi-AgEIcHlwaS5vcmc[A-Za-z0-9_-]{50,512}(?![A-Za-z0-9_-])/g, accept: notPlaceholder },
186947
187020
  { name: "digitalocean_token", pattern: /\bdo[opr]_v1_[a-f0-9]{64}\b/g, accept: notPlaceholder },
186948
187021
  { name: "shopify_token", pattern: /\bshp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}\b/g, accept: notPlaceholder },
186949
187022
  { name: "databricks_token", pattern: /\bdapi[a-f0-9]{32}\b/g, accept: notPlaceholder },
186950
- { name: "moda_api_key", pattern: /\bmoda_(?:sk|ct)_([A-Za-z0-9_-]{32,})/g, group: 1, accept: (v) => randomLooking(v, 32) },
187023
+ { name: "moda_api_key", pattern: /\bmoda_(?:sk|ct)_([A-Za-z0-9_-]{32,512})(?![A-Za-z0-9_-])/g, group: 1, accept: (v) => randomLooking(v, 32) },
186951
187024
  {
186952
187025
  name: "jwt",
186953
- pattern: /\b(eyJ[A-Za-z0-9_-]{10,})\.eyJ[A-Za-z0-9_-]{10,}\.([A-Za-z0-9_-]{16,})/g,
187026
+ pattern: /\b(eyJ[A-Za-z0-9_-]{10,1024})\.eyJ[A-Za-z0-9_-]{10,65536}\.([A-Za-z0-9_-]{16,1024})(?![A-Za-z0-9_-])/g,
186954
187027
  accept: (_v, m) => isJwtHeader(m[1] ?? "") && !KNOWN_SAMPLE_JWT_SIGNATURES.has(m[2] ?? "") && notPlaceholder(m[2] ?? "")
186955
187028
  },
187029
+ {
187030
+ name: "bearer_token",
187031
+ pattern: /\bBearer[ \t]+([A-Za-z0-9._~+/=-]{20,65536})(?![A-Za-z0-9._~+/=-])/g,
187032
+ group: 1,
187033
+ accept: (value) => !looksLikePlaceholder(value) && /[0-9]/.test(value) && /[A-Za-z]/.test(value)
187034
+ },
186956
187035
  {
186957
187036
  name: "url_embedded_credentials",
186958
- pattern: /\b(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?|mssql|sqlserver|clickhouse|https?):\/\/([^:\s/@'"]+):([^@\s/'"]{3,})@([^/\s'"?#]+)/g,
187037
+ pattern: /\b(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?|mssql|sqlserver|clickhouse|https?):\/\/([^:\s/@'"]{1,256}):([^\s/?#'"]{3,65536})@([^/\s'"?#@]+)/g,
186959
187038
  group: 2,
186960
- accept: (password, match) => {
186961
- const host = match[3] ?? "";
187039
+ accept: (password) => {
186962
187040
  if (WEAK_PASSWORDS.has(password.toLowerCase()))
186963
187041
  return false;
186964
- if (looksLikePlaceholder(password) || /^[$%:]/.test(password))
186965
- return false;
186966
- return !LOCAL_HOSTS.test(host);
187042
+ return !looksLikePlaceholder(password) && !/^[$%:{]/.test(password);
186967
187043
  }
186968
187044
  },
186969
187045
  {
186970
187046
  name: "generic_secret_assignment",
186971
187047
  heuristic: true,
186972
- pattern: /\b([A-Za-z][A-Za-z0-9_-]*)["']?\s*[:=]\s*(["'`])([A-Za-z0-9+/=_\-.~]{16,})\2/g,
187048
+ pattern: /\b([A-Za-z][A-Za-z0-9_-]{0,127})["']?[ \t]*[:=][ \t]*(["'`])([A-Za-z0-9+/=_\-.~]{16,})\2/g,
186973
187049
  group: 3,
186974
187050
  accept: (value, match) => isSecretName(match[1] ?? "") && isSecretLikeValue(value)
186975
187051
  },
@@ -186977,7 +187053,7 @@ var SECRET_RULES = [
186977
187053
  name: "generic_secret_assignment",
186978
187054
  heuristic: true,
186979
187055
  configOnly: true,
186980
- pattern: /^\s*(?:export\s+)?([A-Za-z][A-Za-z0-9_-]*)\s*[:=]\s*([A-Za-z0-9+/=_\-.~]{16,})\s*(?:#.*)?$/gm,
187056
+ pattern: /^[ \t]*(?:export[ \t]+)?([A-Za-z][A-Za-z0-9_-]{0,127})[ \t]*[:=][ \t]*([A-Za-z0-9+/=_\-.~]{16,512})[ \t]*(?:#.*)?$/gm,
186981
187057
  group: 2,
186982
187058
  accept: (value, match) => isSecretName(match[1] ?? "") && isSecretLikeValue(value)
186983
187059
  }
@@ -187035,6 +187111,842 @@ function scanForSecrets(text, opts) {
187035
187111
  }
187036
187112
  return null;
187037
187113
  }
187114
+ var INDEXED = new Map;
187115
+ function withIndices(pattern) {
187116
+ let indexed = INDEXED.get(pattern);
187117
+ if (!indexed) {
187118
+ indexed = new RegExp(pattern.source, `${pattern.flags.replace("d", "")}d`);
187119
+ INDEXED.set(pattern, indexed);
187120
+ }
187121
+ return indexed;
187122
+ }
187123
+ function findSecretSpans(text, opts) {
187124
+ const spans = [];
187125
+ for (const rule2 of SECRET_RULES) {
187126
+ if (rule2.heuristic && !opts.heuristics)
187127
+ continue;
187128
+ if (rule2.configOnly && !opts.config)
187129
+ continue;
187130
+ const pattern = withIndices(rule2.pattern);
187131
+ pattern.lastIndex = 0;
187132
+ for (const match of text.matchAll(pattern)) {
187133
+ const group = rule2.group ?? 0;
187134
+ const value = match[group] ?? "";
187135
+ if (rule2.accept && !rule2.accept(value, match))
187136
+ continue;
187137
+ const range = match.indices?.[group] ?? [match.index ?? 0, (match.index ?? 0) + match[0].length];
187138
+ spans.push({ rule: rule2.name, start: range[0], end: range[1], withholdFile: rule2.withholdFile === true });
187139
+ }
187140
+ }
187141
+ return spans;
187142
+ }
187143
+
187144
+ // ../../packages/harness-bundle/src/collect/redact.ts
187145
+ var CONFIG_EXT = /\.(ya?ml|toml|json|jsonc|json5|properties|ini|cfg|conf|env|tfvars|hcl|cnf|nix)$/i;
187146
+ var CONFIG_NAMES = /^(\.npmrc|\.yarnrc|\.yarnrc\.yml|\.pypirc|pip\.conf|pip\.ini|\.condarc|\.netrc|\.envrc|\.dev\.vars.*|\.env.*|[^/]*\.env|dockerfile.*|containerfile.*|.*\.dockerfile|procfile|settings\.xml)$/i;
187147
+ function isConfigLike(path) {
187148
+ const name = basenameOf(path);
187149
+ return CONFIG_EXT.test(name) || CONFIG_NAMES.test(name);
187150
+ }
187151
+ var CREDENTIAL_WORDS = new Set([
187152
+ "token",
187153
+ "secret",
187154
+ "password",
187155
+ "passwd",
187156
+ "pwd",
187157
+ "passphrase",
187158
+ "credential",
187159
+ "credentials",
187160
+ "dsn",
187161
+ "apikey"
187162
+ ]);
187163
+ var NOT_CREDENTIAL_WORDS = new Set([
187164
+ "public",
187165
+ "publishable",
187166
+ "anon",
187167
+ "primary",
187168
+ "foreign",
187169
+ "sort",
187170
+ "partition",
187171
+ "cache",
187172
+ "idempotency",
187173
+ "dedup",
187174
+ "lookup",
187175
+ "index",
187176
+ "hash",
187177
+ "column",
187178
+ "cursor",
187179
+ "row",
187180
+ "composite",
187181
+ "unique",
187182
+ "map",
187183
+ "dict"
187184
+ ]);
187185
+ var KEY_QUALIFIERS = new Set([
187186
+ "api",
187187
+ "access",
187188
+ "secret",
187189
+ "private",
187190
+ "signing",
187191
+ "encryption",
187192
+ "enc",
187193
+ "master",
187194
+ "service",
187195
+ "auth",
187196
+ "client",
187197
+ "license",
187198
+ "app",
187199
+ "webhook",
187200
+ "session",
187201
+ "jwt",
187202
+ "hmac",
187203
+ "admin",
187204
+ "bearer",
187205
+ "account",
187206
+ "subscription",
187207
+ "crypto",
187208
+ "cipher",
187209
+ "root",
187210
+ "role",
187211
+ "write",
187212
+ "read",
187213
+ "deploy",
187214
+ "gateway",
187215
+ "aes",
187216
+ "creds",
187217
+ "credential",
187218
+ "credentials",
187219
+ "pat",
187220
+ "sas",
187221
+ "shared"
187222
+ ]);
187223
+ var IDENTIFIER_KEY_WORDS = new Set([
187224
+ "s3",
187225
+ "r2",
187226
+ "gcs",
187227
+ "blob",
187228
+ "object",
187229
+ "output",
187230
+ "path",
187231
+ "plugin",
187232
+ "file",
187233
+ "prompt",
187234
+ "config",
187235
+ "json",
187236
+ "user",
187237
+ "storage",
187238
+ "routing",
187239
+ "shard",
187240
+ "message",
187241
+ "event",
187242
+ "bucket",
187243
+ "record",
187244
+ "i18n",
187245
+ "translation",
187246
+ "metric",
187247
+ "label",
187248
+ "group",
187249
+ "state",
187250
+ "query",
187251
+ "field",
187252
+ "attribute",
187253
+ "property",
187254
+ "tag",
187255
+ "header",
187256
+ "item",
187257
+ "table",
187258
+ "range",
187259
+ "prefix",
187260
+ "ssh",
187261
+ "gpg",
187262
+ "pgp",
187263
+ "idem",
187264
+ "sort",
187265
+ "partition",
187266
+ "cache",
187267
+ "lookup",
187268
+ "index",
187269
+ "hash",
187270
+ "column",
187271
+ "row",
187272
+ "map",
187273
+ "dict",
187274
+ "composite",
187275
+ "unique"
187276
+ ]);
187277
+ function isCredentialName(rawName) {
187278
+ const name = rawName.split(/[./:\\]/).filter(Boolean).pop() ?? rawName;
187279
+ const words = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").split(/[_\-\s]+/).filter(Boolean).map((w) => w.toLowerCase());
187280
+ const last = words[words.length - 1];
187281
+ const previous = words[words.length - 2];
187282
+ if (!last || words.slice(0, -1).some((w) => NOT_CREDENTIAL_WORDS.has(w)))
187283
+ return false;
187284
+ if (CREDENTIAL_WORDS.has(last))
187285
+ return true;
187286
+ if (/^_auth$/i.test(name) || last === "ident" && previous === "auth")
187287
+ return true;
187288
+ if (last !== "key" || !previous)
187289
+ return false;
187290
+ if (KEY_QUALIFIERS.has(previous))
187291
+ return true;
187292
+ return /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)+$/.test(name) && !IDENTIFIER_KEY_WORDS.has(previous);
187293
+ }
187294
+ function isCredentialValue(raw) {
187295
+ const value = raw.trim();
187296
+ if (value.length < 4 || /[\s\p{C}]/u.test(value))
187297
+ return false;
187298
+ if (/^(true|false|null|none|nil|undefined|yes|no|on|off|~)$/i.test(value))
187299
+ return false;
187300
+ if (/^\$\{?[A-Za-z_]/.test(value) || /\$\{\{|\{\{|%\(|^\$\(/.test(value))
187301
+ return false;
187302
+ if (/^(?:os\.environ(?:\.get)?|os\.getenv|process\.env|import\.meta\.env|Deno\.env\.get|System\.getenv|getenv|ENV)(?:\(.*\)|\[.*\]|\.[A-Za-z_]\w*)$/.test(value))
187303
+ return false;
187304
+ if (/^(env|secret|vault|op|arn|ssm|awssecretsmanager|azurekeyvault|sm|gcpsm|projects)[:(/]/i.test(value) || /^@Microsoft\.KeyVault/i.test(value))
187305
+ return false;
187306
+ if (/^<.*>$/.test(value) || /^#\//.test(value))
187307
+ return false;
187308
+ if (/^(\/|\.\.?\/|~\/|[A-Za-z]:\\)/.test(value))
187309
+ return false;
187310
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value) && !/^[a-z][a-z0-9+.-]*:\/\/[^/@\s]+@/i.test(value))
187311
+ return false;
187312
+ if (/^[-+]?\d*\.?\d+(e[-+]?\d+)?$/i.test(value))
187313
+ return false;
187314
+ if (/^[\^~<>=v]*\d+(\.(\d+|x|\*)){0,3}([-+.][\w.-]+)?$/i.test(value))
187315
+ return false;
187316
+ if (/^(workspace|npm|file|link|portal|patch|catalog|git\+\w+|github|gitlab):/i.test(value) || /^(latest|next|canary|beta|\*)$/i.test(value))
187317
+ return false;
187318
+ if (WEAK_PASSWORDS.has(value.toLowerCase()))
187319
+ return false;
187320
+ if (/^(.)\1+$/.test(value) || sequentialRuns(value) >= 2)
187321
+ return false;
187322
+ if (/x{4,}|X{4,}|\*{3,}|\.\.\.|(^|[-_.])your[-_.]|changeme|change[-_]me|placeholder|example|replace[-_]?me|redacted|dummy|(^|[-_.])(fake|mock|sample|test|demo)([-_.]|$)/i.test(value))
187323
+ return false;
187324
+ const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((re) => re.test(value)).length;
187325
+ return /[0-9]/.test(value) || value.length >= 20 && classes >= 3;
187326
+ }
187327
+ var PREFIX = String.raw`[ \t]*(?:-[ \t]+)?(?:export[ \t]+|ENV[ \t]+|ARG[ \t]+)?`;
187328
+ var EQUALS_LINE = new RegExp(String.raw`^(${PREFIX})(["']?)([A-Za-z_@/][\w.\-/:@]*)\2[ \t]*=[ \t]*(.*)$`, "gm");
187329
+ var COLON_LINE = new RegExp(String.raw`^(${PREFIX})(["']?)([A-Za-z_@][\w.\-/@]*)\2[ \t]*:(?:[ \t]+|(?=["']))(.*)$`, "gm");
187330
+ function closingQuote(text, open, lineEnd) {
187331
+ const quote = text[open];
187332
+ for (let i = open + 1;i < lineEnd; i += 1) {
187333
+ if (text[i] === "\\" && quote === '"') {
187334
+ i += 1;
187335
+ continue;
187336
+ }
187337
+ if (quote === "'" && text[i] === quote && text[i + 1] === quote) {
187338
+ i += 1;
187339
+ continue;
187340
+ }
187341
+ if (text[i] === quote)
187342
+ return i;
187343
+ }
187344
+ return -1;
187345
+ }
187346
+ function isDockerfile(path) {
187347
+ const name = basenameOf(path);
187348
+ return /^(dockerfile|containerfile)/i.test(name) || /\.dockerfile$/i.test(name);
187349
+ }
187350
+ function valueEnds(path, envFile) {
187351
+ const chars = isDockerfile(path) ? "\\s" : envFile ? "\\s;" : "\\s,;)\\]}";
187352
+ return { terminator: new RegExp(`[${chars}]`), segmentEnd: new RegExp(`[${chars}"'\`]`) };
187353
+ }
187354
+ function dockerfileEnvSpans(source, detected) {
187355
+ const directives = /^(?:[ \t]*#[ \t]*[A-Za-z]+[ \t]*=[^\n]*\n)*/.exec(source)[0];
187356
+ const backtick = /^[ \t]*#[ \t]*escape[ \t]*=[ \t]*`[ \t]*\r?$/im.test(directives);
187357
+ const text = backtick ? source.replace(/[`\\]/g, (ch) => ch === "`" ? "\\" : "`") : source;
187358
+ const spans = [];
187359
+ const known = [...detected].sort((a, b) => a.start - b.start);
187360
+ let nextKnown = 0;
187361
+ let knownEnd = -1;
187362
+ let covered = -1;
187363
+ const skipIgnored = (from) => {
187364
+ let q = from;
187365
+ while (q < text.length) {
187366
+ const lineEnd = text.indexOf(`
187367
+ `, q) === -1 ? text.length : text.indexOf(`
187368
+ `, q);
187369
+ if (!/^[ \t]*(?:#.*)?\r?$/.test(text.slice(q, lineEnd)) || lineEnd === text.length)
187370
+ break;
187371
+ q = lineEnd + 1;
187372
+ }
187373
+ return q;
187374
+ };
187375
+ for (const m of text.matchAll(/^[ \t]*(ENV|ARG)[ \t]+/gim)) {
187376
+ if (m.index < covered)
187377
+ continue;
187378
+ let p2 = m.index + m[0].length;
187379
+ let instructionEnd = text.indexOf(`
187380
+ `, p2);
187381
+ while (instructionEnd !== -1) {
187382
+ let slash = instructionEnd - (text[instructionEnd - 1] === "\r" ? 1 : 0);
187383
+ const end = slash;
187384
+ while (slash > p2 && text[slash - 1] === "\\")
187385
+ slash -= 1;
187386
+ if ((end - slash) % 2 === 0)
187387
+ break;
187388
+ instructionEnd = text.indexOf(`
187389
+ `, skipIgnored(instructionEnd + 1));
187390
+ }
187391
+ if (instructionEnd === -1)
187392
+ instructionEnd = text.length;
187393
+ covered = instructionEnd;
187394
+ let first = true;
187395
+ for (;; ) {
187396
+ for (;; ) {
187397
+ if (text[p2] === " " || text[p2] === "\t" || text[p2] === "\r")
187398
+ p2 += 1;
187399
+ else if (text.startsWith("\\\n", p2))
187400
+ p2 = skipIgnored(p2 + 2);
187401
+ else if (text.startsWith(`\\\r
187402
+ `, p2))
187403
+ p2 = skipIgnored(p2 + 3);
187404
+ else
187405
+ break;
187406
+ }
187407
+ const rest = text.slice(p2, Math.min(p2 + 256, instructionEnd));
187408
+ let name = /^(["']?)([A-Za-z_][\w.-]*)\1=/.exec(rest);
187409
+ const legacy = !name && first && m[1].toUpperCase() === "ENV";
187410
+ if (legacy)
187411
+ name = /^(["']?)([A-Za-z_][\w.-]*)\1[ \t]+/.exec(rest);
187412
+ if (!name)
187413
+ break;
187414
+ first = false;
187415
+ p2 += name[0].length;
187416
+ const valueStart = p2;
187417
+ const parts = [];
187418
+ const addPart = (start, end) => {
187419
+ let from = start;
187420
+ for (const continuation of text.slice(start, end).matchAll(/\\\r?\n/g)) {
187421
+ const stop = start + continuation.index;
187422
+ if (stop > from)
187423
+ parts.push({ start: from, end: stop, rule: "credential_assignment" });
187424
+ from = stop + continuation[0].length;
187425
+ }
187426
+ if (end > from)
187427
+ parts.push({ start: from, end, rule: "credential_assignment" });
187428
+ };
187429
+ let literal = "";
187430
+ while (p2 < instructionEnd && (legacy || !/\s/.test(text[p2]))) {
187431
+ const q = text[p2];
187432
+ if (q === '"' || q === "'") {
187433
+ const close = closingQuote(text, p2, instructionEnd);
187434
+ const end = close === -1 ? instructionEnd : close;
187435
+ addPart(p2 + 1, end);
187436
+ literal += text.slice(p2 + 1, end);
187437
+ p2 = close === -1 ? instructionEnd : close + 1;
187438
+ } else {
187439
+ let e = p2;
187440
+ while (e < instructionEnd && !(legacy ? /["']/ : /[\s"']/).test(text[e])) {
187441
+ if (text[e] === "\\" && e + 1 < instructionEnd)
187442
+ e += text.startsWith(`\\\r
187443
+ `, e) ? 3 : 2;
187444
+ else
187445
+ e += 1;
187446
+ }
187447
+ addPart(p2, e);
187448
+ literal += text.slice(p2, e);
187449
+ p2 = e;
187450
+ }
187451
+ }
187452
+ while (nextKnown < known.length && known[nextKnown].start < valueStart) {
187453
+ knownEnd = Math.max(knownEnd, known[nextKnown++].end);
187454
+ }
187455
+ const hasSecret = knownEnd > valueStart || nextKnown < known.length && known[nextKnown].start < p2;
187456
+ const value = literal.replace(/\\\r?\n/g, "").replace(/\\(.)/g, "$1").trim();
187457
+ if (isCredentialName(name[2]) && (hasSecret || value.split(/[ \t]+/).some(isCredentialValue)))
187458
+ for (const part of parts)
187459
+ spans.push(part);
187460
+ if (legacy)
187461
+ break;
187462
+ }
187463
+ }
187464
+ return spans;
187465
+ }
187466
+ function assignmentSpans(text, everyValue, path, ends) {
187467
+ const toml = /\.toml$/i.test(path);
187468
+ const spans = [];
187469
+ const seenLines = new Set;
187470
+ for (const pattern of [EQUALS_LINE, COLON_LINE]) {
187471
+ pattern.lastIndex = 0;
187472
+ for (const m of text.matchAll(pattern)) {
187473
+ const lineStart = m.index ?? 0;
187474
+ if (seenLines.has(lineStart))
187475
+ continue;
187476
+ seenLines.add(lineStart);
187477
+ const name = m[3];
187478
+ const rest = m[4];
187479
+ const restStart = lineStart + m[0].length - rest.length;
187480
+ let value = valueBounds(rest, everyValue, toml) ?? multilineQuoted(text, restStart, toml);
187481
+ if (!value && everyValue && rest.trim() !== "")
187482
+ value = { start: 0, end: rest.replace(/[ \t]+$/, "").length };
187483
+ if (!value)
187484
+ continue;
187485
+ const extra = [];
187486
+ if (value.start > 0) {
187487
+ const lineEnd = text.indexOf(`
187488
+ `, restStart + value.end) === -1 ? text.length : text.indexOf(`
187489
+ `, restStart + value.end);
187490
+ let p2 = restStart + value.end + value.start;
187491
+ while (p2 < lineEnd && !ends.terminator.test(text[p2])) {
187492
+ const q = text[p2];
187493
+ if (q === '"' || q === "'" || q === "`") {
187494
+ const close = closingQuote(text, p2, lineEnd);
187495
+ const contentEnd = close === -1 ? lineEnd : close;
187496
+ if (contentEnd > p2 + 1)
187497
+ extra.push({ start: p2 + 1, end: contentEnd, rule: "" });
187498
+ p2 = close === -1 ? lineEnd : close + 1;
187499
+ } else {
187500
+ let e = p2;
187501
+ while (e < lineEnd && !ends.segmentEnd.test(text[e]))
187502
+ e += 1;
187503
+ extra.push({ start: p2, end: e, rule: "" });
187504
+ p2 = e;
187505
+ }
187506
+ }
187507
+ if (everyValue && p2 < lineEnd) {
187508
+ const line = text.slice(p2, lineEnd);
187509
+ const comment = line.search(/[ \t]+#/);
187510
+ const body = (comment === -1 ? line : line.slice(0, comment)).replace(/[ \t\r]+$/, "");
187511
+ const lead = body.length - body.replace(/^[\s,;)\]}]+/, "").length;
187512
+ if (body.length > lead)
187513
+ extra.push({ start: p2 + lead, end: p2 + body.length, rule: "" });
187514
+ }
187515
+ }
187516
+ const joined = extra.length > 0;
187517
+ const rule2 = everyValue ? "env_value" : "credential_assignment";
187518
+ const literal = rest.slice(value.start, Math.min(value.end, rest.length)) + (value.end > rest.length ? text.slice(restStart + rest.length, restStart + value.end) : "");
187519
+ if (!everyValue && /\.(hcl|tfvars)$/i.test(path) && value.start === 0 && /^[A-Za-z_]\w*(?:\.|\[|\()/.test(literal))
187520
+ continue;
187521
+ if (everyValue ? literal.trim() === "" : !(isCredentialName(name) && (joined || isCredentialValue(literal))))
187522
+ continue;
187523
+ spans.push({ start: restStart + value.start, end: restStart + value.end, rule: rule2 });
187524
+ for (const span of extra)
187525
+ spans.push({ ...span, rule: rule2 });
187526
+ }
187527
+ }
187528
+ return spans;
187529
+ }
187530
+ function unparsedEnvLines(text, covered) {
187531
+ const out = [];
187532
+ const assignment = new RegExp(String.raw`^${PREFIX}(["']?)[A-Za-z_@/][\w.\-/:@]*\1[ \t]*[=:]`);
187533
+ const spans = [...covered].sort((a, b) => a.start - b.start);
187534
+ let next = 0;
187535
+ let reach = -1;
187536
+ let offset = 0;
187537
+ for (const line of text.split(`
187538
+ `)) {
187539
+ const start = offset;
187540
+ const end = start + Math.max(line.length, 1);
187541
+ offset += line.length + 1;
187542
+ while (next < spans.length && spans[next].start < end)
187543
+ reach = Math.max(reach, spans[next++].end);
187544
+ if (reach > start)
187545
+ continue;
187546
+ const trimmed = line.trim();
187547
+ if (trimmed === "" || trimmed.startsWith("#") || assignment.test(line))
187548
+ continue;
187549
+ const lead = line.length - line.trimStart().length;
187550
+ out.push({ start: start + lead, end: start + line.replace(/[ \t\r]+$/, "").length, rule: "env_value" });
187551
+ }
187552
+ return out;
187553
+ }
187554
+ function tripleQuoted(text, from) {
187555
+ const delim = text.slice(from, from + 3);
187556
+ if (delim !== '"""' && delim !== "'''")
187557
+ return null;
187558
+ for (let i = from + 3;i < text.length; i += 1) {
187559
+ if (text[i] === "\\" && delim === '"""') {
187560
+ i += 1;
187561
+ continue;
187562
+ }
187563
+ if (text.startsWith(delim, i)) {
187564
+ let close = i;
187565
+ while (close - i < 2 && text[close + 3] === delim[0])
187566
+ close += 1;
187567
+ return close > from + 3 ? { start: 3, end: close - from } : null;
187568
+ }
187569
+ }
187570
+ return null;
187571
+ }
187572
+ function multilineQuoted(text, restStart, toml) {
187573
+ const quote = text[restStart];
187574
+ if (quote !== '"' && quote !== "'" && quote !== "`")
187575
+ return null;
187576
+ if (toml && quote !== "`" && text.startsWith(quote.repeat(3), restStart))
187577
+ return tripleQuoted(text, restStart);
187578
+ for (let i = restStart + 1;i < text.length; i += 1) {
187579
+ if (text[i] === "\\" && quote !== "'") {
187580
+ i += 1;
187581
+ continue;
187582
+ }
187583
+ if (quote === "'" && text[i] === quote && text[i + 1] === quote) {
187584
+ i += 1;
187585
+ continue;
187586
+ }
187587
+ if (text[i] === quote)
187588
+ return i > restStart + 1 ? { start: 1, end: i - restStart } : null;
187589
+ }
187590
+ return null;
187591
+ }
187592
+ function valueBounds(rest, envFile, toml) {
187593
+ if (rest === "" || /^[{[|>]/.test(rest))
187594
+ return null;
187595
+ const quote = rest[0];
187596
+ if (quote === '"' || quote === "'" || quote === "`") {
187597
+ if (toml && quote !== "`" && rest.startsWith(quote.repeat(3)))
187598
+ return tripleQuoted(rest, 0);
187599
+ for (let i = 1;i < rest.length; i += 1) {
187600
+ if (rest[i] === "\\" && quote === '"') {
187601
+ i += 1;
187602
+ continue;
187603
+ }
187604
+ if (quote === "'" && rest[i] === quote && rest[i + 1] === quote) {
187605
+ i += 1;
187606
+ continue;
187607
+ }
187608
+ if (rest[i] === quote)
187609
+ return i > 1 ? { start: 1, end: i } : null;
187610
+ }
187611
+ return null;
187612
+ }
187613
+ let end = rest.length;
187614
+ const comment = rest.search(envFile ? /[ \t]+#/ : /[ \t]+[#;]/);
187615
+ if (comment !== -1)
187616
+ end = comment;
187617
+ const trimmed = rest.slice(0, end).replace(envFile ? /[ \t]+$/ : /[ \t,]+$/, "");
187618
+ return trimmed === "" ? null : { start: 0, end: trimmed.length };
187619
+ }
187620
+ function xmlSpans(text) {
187621
+ const spans = [];
187622
+ for (const m of text.matchAll(/<(password|passphrase|privateKey|secret|token|apiKey|accessKey|secretKey)>([^<]+)<\/\1>/dgi)) {
187623
+ const value = m[2].trim();
187624
+ if (value === "" || /^\$\{/.test(value))
187625
+ continue;
187626
+ const [start, end] = m.indices[2];
187627
+ spans.push({ start, end, rule: "credential_assignment" });
187628
+ }
187629
+ return spans;
187630
+ }
187631
+ function inlinePairSpans(text, path) {
187632
+ const cComments = /\.json5$/i.test(path);
187633
+ const toml = /\.toml$/i.test(path);
187634
+ const yaml = /\.ya?ml$/i.test(path);
187635
+ const spans = [];
187636
+ const tomlKey = /[ \t]*(["']?)([A-Za-z_@][\w.\-/@]{0,127})\1[ \t]*=[ \t]*("""|''')/y;
187637
+ const pair = /[ \t]*(["']?)([A-Za-z_@][\w.\-/@]{0,127})\1[ \t]*[:=][ \t]*(?:"((?:[^"\\\n]|\\.)*)"|'((?:[^'\n]|'')*)'|([^,{}[\]\s"'][^,{}\n]*))/dy;
187638
+ let depth = 0;
187639
+ let quote = "";
187640
+ let previous = `
187641
+ `;
187642
+ let expectKey = false;
187643
+ for (let i = 0;i < text.length; i += 1) {
187644
+ const ch = text[i];
187645
+ if (quote) {
187646
+ if (ch === "\\" && quote === '"')
187647
+ i += 1;
187648
+ else if (quote === "'" && ch === "'" && text[i + 1] === "'")
187649
+ i += 1;
187650
+ else if (ch === quote || ch === `
187651
+ ` && !yaml)
187652
+ quote = "";
187653
+ continue;
187654
+ }
187655
+ if (ch === " " || ch === "\t" || ch === "\r")
187656
+ continue;
187657
+ if (ch === `
187658
+ `) {
187659
+ previous = ch;
187660
+ continue;
187661
+ }
187662
+ if (cComments && ch === "/" && (text[i + 1] === "/" || text[i + 1] === "*") && /[\s{,[]/.test(text[i - 1] ?? `
187663
+ `)) {
187664
+ const close = text[i + 1] === "*" ? text.indexOf("*/", i + 2) : text.indexOf(`
187665
+ `, i);
187666
+ i = close === -1 ? text.length : text[i + 1] === "*" ? close + 1 : close - 1;
187667
+ continue;
187668
+ }
187669
+ if (ch === "#" && /\s/.test(text[i - 1] ?? `
187670
+ `)) {
187671
+ const lineEnd = text.indexOf(`
187672
+ `, i);
187673
+ i = (lineEnd === -1 ? text.length : lineEnd) - 1;
187674
+ continue;
187675
+ }
187676
+ if (expectKey) {
187677
+ expectKey = false;
187678
+ if (toml) {
187679
+ tomlKey.lastIndex = i;
187680
+ const k = tomlKey.exec(text);
187681
+ if (k) {
187682
+ const open = k.index + k[0].length - 3;
187683
+ const bounds = tripleQuoted(text, open);
187684
+ if (!bounds) {
187685
+ const close = text.indexOf(k[3], open + 3);
187686
+ i = close === -1 ? text.length - 1 : close + 2;
187687
+ } else {
187688
+ const from = open + bounds.start;
187689
+ const to = open + bounds.end;
187690
+ if (isCredentialName(k[2]) && text.slice(from, to).trim().split(/\s+/).some(isCredentialValue)) {
187691
+ spans.push({ start: from, end: to, rule: "credential_assignment" });
187692
+ }
187693
+ i = to + 2;
187694
+ while (text[i + 1] === k[3][0])
187695
+ i += 1;
187696
+ }
187697
+ previous = text[i];
187698
+ continue;
187699
+ }
187700
+ }
187701
+ pair.lastIndex = i;
187702
+ const m = pair.exec(text);
187703
+ if (m) {
187704
+ const group = m[3] !== undefined ? 3 : m[4] !== undefined ? 4 : 5;
187705
+ const value = m[group];
187706
+ if (isCredentialName(m[2]) && isCredentialValue(value)) {
187707
+ const [from] = m.indices[group];
187708
+ spans.push({ start: from, end: from + value.replace(/[ \t]+$/, "").length, rule: "credential_assignment" });
187709
+ }
187710
+ i = m.index + m[0].length - 1;
187711
+ previous = text[i];
187712
+ continue;
187713
+ }
187714
+ }
187715
+ if ((ch === '"' || ch === "'") && /[\n:=,{[(]/.test(previous)) {
187716
+ if (toml && text.startsWith(ch.repeat(3), i)) {
187717
+ const close = tripleQuoted(text, i);
187718
+ const end = close === null ? text.indexOf(ch.repeat(3), i + 3) : i + close.end;
187719
+ i = end === -1 ? text.length : end + 2;
187720
+ while (text[i + 1] === ch)
187721
+ i += 1;
187722
+ previous = ch;
187723
+ continue;
187724
+ }
187725
+ quote = ch;
187726
+ previous = ch;
187727
+ continue;
187728
+ }
187729
+ previous = ch;
187730
+ if (ch === "{")
187731
+ depth += 1;
187732
+ else if (ch === "}" && depth > 0)
187733
+ depth -= 1;
187734
+ if ((ch === "{" || ch === ",") && depth > 0)
187735
+ expectKey = true;
187736
+ }
187737
+ return spans;
187738
+ }
187739
+ function jsonSpans(text) {
187740
+ const spans = [];
187741
+ for (const m of text.matchAll(/"([^"\\\n]{1,128})"\s*:\s*"((?:[^"\\\n]|\\.)*)"/dg)) {
187742
+ if (!isCredentialName(m[1]) || !isCredentialValue(m[2]))
187743
+ continue;
187744
+ const [start, end] = m.indices[2];
187745
+ spans.push({ start, end, rule: "credential_assignment" });
187746
+ }
187747
+ return spans;
187748
+ }
187749
+ var GENERIC_RULES = new Set(["generic_secret_assignment", "credential_assignment", "env_value", "url_embedded_credentials", "bearer_token"]);
187750
+ var SEPARATOR2 = /[-_./:@+=,;]/;
187751
+ function mask(value) {
187752
+ const marker = PROVIDER_MARKER.exec(value)?.[0] ?? "";
187753
+ const keep = marker.length <= value.length - 8 ? marker : "";
187754
+ const body = value.slice(keep.length);
187755
+ const hasContent = /[\p{L}\p{N}]/u.test(body);
187756
+ let out = "";
187757
+ for (let i = 0;i < body.length; i += 1) {
187758
+ const ch = body[i];
187759
+ if (ch === "\\" && i + 1 < body.length && body[i + 1] !== `
187760
+ ` && body[i + 1] !== "\r") {
187761
+ out += "xx";
187762
+ i += 1;
187763
+ continue;
187764
+ }
187765
+ if (ch === `
187766
+ ` || ch === "\r")
187767
+ out += ch;
187768
+ else if (hasContent && SEPARATOR2.test(ch))
187769
+ out += ch;
187770
+ else
187771
+ out += "x";
187772
+ }
187773
+ return keep + out;
187774
+ }
187775
+ function propertiesContinuations(text, spans) {
187776
+ const out = [];
187777
+ const visited = new Set;
187778
+ const lineEndAt = (i) => text.indexOf(`
187779
+ `, i) === -1 ? text.length : text.indexOf(`
187780
+ `, i);
187781
+ const continuation = (from, end) => {
187782
+ let contentEnd = end;
187783
+ if (text[contentEnd - 1] === "\r")
187784
+ contentEnd -= 1;
187785
+ let run = contentEnd;
187786
+ while (run > from && text[run - 1] === "\\")
187787
+ run -= 1;
187788
+ return (contentEnd - run) % 2 === 1 ? contentEnd - 1 : -1;
187789
+ };
187790
+ const comments = new Set;
187791
+ let continued = false;
187792
+ for (let from = 0;from < text.length; ) {
187793
+ const end = lineEndAt(from);
187794
+ const comment = !continued && /^[ \t\f]*[#!]/.test(text.slice(from, end));
187795
+ if (comment)
187796
+ comments.add(end);
187797
+ continued = !comment && continuation(from, end) !== -1;
187798
+ from = end + 1;
187799
+ }
187800
+ for (const span of spans) {
187801
+ let end = lineEndAt(span.start);
187802
+ if (comments.has(end))
187803
+ continue;
187804
+ let slash = continuation(span.start, end);
187805
+ if (slash === -1)
187806
+ continue;
187807
+ if (span.end > slash)
187808
+ span.end = slash;
187809
+ while (slash !== -1 && end < text.length && !visited.has(end)) {
187810
+ visited.add(end);
187811
+ const from = end + 1;
187812
+ end = lineEndAt(from);
187813
+ let start = from;
187814
+ while (start < end && (text[start] === " " || text[start] === "\t"))
187815
+ start += 1;
187816
+ slash = continuation(start, end);
187817
+ const stop = slash === -1 ? text[end - 1] === "\r" ? end - 1 : end : slash;
187818
+ if (stop > start)
187819
+ out.push({ start, end: stop, rule: span.rule });
187820
+ }
187821
+ }
187822
+ return out;
187823
+ }
187824
+ function yamlValueSpans(text, spans, everyValue) {
187825
+ if (spans.length === 0) {
187826
+ let candidate = false;
187827
+ COLON_LINE.lastIndex = 0;
187828
+ for (const match of text.matchAll(COLON_LINE)) {
187829
+ if (!isCredentialName(match[3]))
187830
+ continue;
187831
+ const rest = match[4].replace(/\r$/, "");
187832
+ const lineEnd = match.index + match[0].length;
187833
+ const nextLine = /^\n([ \t]*)\S/.exec(text.slice(lineEnd, lineEnd + 512));
187834
+ const indent = /^[ \t]*/.exec(match[0])[0].length;
187835
+ if (/^[&!]/.test(rest) || /(?:^|[ \t])[|>][-+1-9]*[ \t]*(?:#.*)?$/.test(rest) || /^"(?:[^"\\]|\\.)*$/.test(rest) || /^'(?:[^']|'')*$/.test(rest) || rest.trim() !== "" && !/^["'{[]/.test(rest) && nextLine !== null && nextLine[1].length > indent) {
187836
+ candidate = true;
187837
+ break;
187838
+ }
187839
+ }
187840
+ if (!candidate)
187841
+ return [];
187842
+ } else if (!/[|>&!]/.test(text) && !/[\r\n][^\r\n]/.test(text))
187843
+ return [];
187844
+ const out = [];
187845
+ const sorted = [...spans].sort((a, b) => a.start - b.start);
187846
+ let next = 0;
187847
+ let reach = -1;
187848
+ let reachRule = "";
187849
+ for (const doc of $parseAllDocuments(text, { keepSourceTokens: true, prettyErrors: false })) {
187850
+ if (doc.errors.length > 0)
187851
+ throw new Error("YAML value bounds are unavailable");
187852
+ $visit(doc, (_key, node, path) => {
187853
+ if (!$isScalar(node) || !node.srcToken || !("source" in node.srcToken) || !node.range)
187854
+ return;
187855
+ const block = node.srcToken.type === "block-scalar";
187856
+ const quoted = node.type === "QUOTE_SINGLE" || node.type === "QUOTE_DOUBLE";
187857
+ const raw = node.srcToken.source;
187858
+ if (!block && !node.anchor && !node.tag && !/[\r\n]/.test(raw))
187859
+ return;
187860
+ const source = quoted ? raw.slice(1, -1) : raw;
187861
+ const to = node.range[1] - (quoted ? 1 : 0);
187862
+ const from = to - source.length;
187863
+ const parent = path[path.length - 1];
187864
+ const name = $isPair(parent) && $isScalar(parent.key) ? String(parent.key.value) : "";
187865
+ while (next < sorted.length && sorted[next].start < from) {
187866
+ if (sorted[next].end > reach)
187867
+ [reach, reachRule] = [sorted[next].end, sorted[next].rule];
187868
+ next += 1;
187869
+ }
187870
+ const inside = reach > from ? reachRule : next < sorted.length && sorted[next].start < to ? sorted[next].rule : "";
187871
+ const credential = isCredentialName(name) && String(node.value).trim().split(/[ \t\r\n]+/).some(isCredentialValue);
187872
+ if (!inside && !everyValue && !credential)
187873
+ return;
187874
+ const rule2 = inside || (everyValue ? "env_value" : "credential_assignment");
187875
+ for (const line of source.matchAll(/[^\r\n]+/g)) {
187876
+ const lead = /^[ \t]*/.exec(line[0])[0].length;
187877
+ if (lead < line[0].length)
187878
+ out.push({ start: from + line.index + lead, end: from + line.index + line[0].length, rule: rule2 });
187879
+ }
187880
+ });
187881
+ }
187882
+ return out;
187883
+ }
187884
+ function redactText(path, text, categories, opts) {
187885
+ const heuristics = !categories.includes("lockfile") && !/\.min\.(js|css)$|\.map$/.test(path);
187886
+ const config = isConfigLike(path);
187887
+ const found = findSecretSpans(text, { heuristics, config: !categories.includes("source") });
187888
+ const withhold = found.find((s) => s.withholdFile);
187889
+ if (withhold)
187890
+ return { text, redactions: [], ranges: [], withholdRule: withhold.rule };
187891
+ const spans = found.map(({ start, end, rule: rule2 }) => ({ start, end, rule: rule2 }));
187892
+ const lockfile = categories.includes("lockfile");
187893
+ if (opts.envFile) {
187894
+ spans.push(...assignmentSpans(text, true, path, valueEnds(path, true)));
187895
+ spans.push(...unparsedEnvLines(text, spans));
187896
+ } else if (config && !lockfile && !isDockerfile(path))
187897
+ spans.push(...assignmentSpans(text, false, path, valueEnds(path, false)));
187898
+ if (isDockerfile(path))
187899
+ for (const span of dockerfileEnvSpans(text, spans))
187900
+ spans.push(span);
187901
+ if (config && !lockfile && /\.json[c5]?$/i.test(path))
187902
+ spans.push(...jsonSpans(text));
187903
+ if (config && !lockfile && !opts.envFile && !/\.jsonc?$/i.test(path))
187904
+ spans.push(...inlinePairSpans(text, path));
187905
+ if (!lockfile && /\.xml$/i.test(path))
187906
+ spans.push(...xmlSpans(text));
187907
+ if (/\.properties$/i.test(path))
187908
+ spans.push(...propertiesContinuations(text, spans));
187909
+ if (/\.ya?ml$/i.test(path)) {
187910
+ try {
187911
+ spans.push(...yamlValueSpans(text, spans, opts.envFile));
187912
+ } catch {
187913
+ return { text, redactions: [], ranges: [], withholdRule: "credential_assignment" };
187914
+ }
187915
+ }
187916
+ if (spans.length === 0)
187917
+ return { text, redactions: [], ranges: [] };
187918
+ spans.sort((a, b) => a.start - b.start || b.end - a.end);
187919
+ const merged = [];
187920
+ for (const span of spans) {
187921
+ if (span.end <= span.start)
187922
+ continue;
187923
+ const last = merged[merged.length - 1];
187924
+ if (last && span.start < last.end) {
187925
+ last.end = Math.max(last.end, span.end);
187926
+ if (GENERIC_RULES.has(last.rule) && !GENERIC_RULES.has(span.rule))
187927
+ last.rule = span.rule;
187928
+ } else {
187929
+ merged.push({ ...span });
187930
+ }
187931
+ }
187932
+ let out = "";
187933
+ let cursor = 0;
187934
+ let lineNo = 1;
187935
+ let lineCursor = 0;
187936
+ const redactions = [];
187937
+ for (const span of merged) {
187938
+ for (let i = lineCursor;i < span.start; i += 1)
187939
+ if (text.charCodeAt(i) === 10)
187940
+ lineNo += 1;
187941
+ lineCursor = span.start;
187942
+ const value = text.slice(span.start, span.end);
187943
+ out += text.slice(cursor, span.start) + mask(value);
187944
+ cursor = span.end;
187945
+ redactions.push({ line: lineNo, rule: span.rule, length: value.length });
187946
+ }
187947
+ out += text.slice(cursor);
187948
+ return { text: out, redactions, ranges: merged.map((span) => [span.start, span.end]) };
187949
+ }
187038
187950
 
187039
187951
  // ../../packages/harness-bundle/src/collect/toolchain.ts
187040
187952
  function isToolchainFile(path) {
@@ -187735,6 +188647,7 @@ class Collector {
187735
188647
  texts = new Map;
187736
188648
  env = new EnvCollector;
187737
188649
  parentCache = new Map;
188650
+ redactions = [];
187738
188651
  listedArtifacts = new Map;
187739
188652
  constructor(repoRoot, opts) {
187740
188653
  this.opts = opts;
@@ -187793,7 +188706,7 @@ class Collector {
187793
188706
  const withPrefix = (p2) => prefix === "" ? p2 : `${prefix}/${p2}`;
187794
188707
  const tracked = [...new Set(lsFiles(dir, ["--cached"]))].sort().map(withPrefix);
187795
188708
  const untracked = lsFiles(dir, ["--others", "--exclude-standard"]).sort().map(withPrefix);
187796
- const ignored = lsFiles(dir, ["--others", "--ignored", "--exclude-standard", "--directory", "--no-empty-directory"]).sort().map(withPrefix);
188709
+ const ignored = ignoredPaths(dir).sort().map(withPrefix);
187797
188710
  const ignoredDirs = ignored.filter((p2) => p2.endsWith("/"));
187798
188711
  const ignoredRuleFiles = lsFiles(dir, ["--others", "--ignored", "--exclude-standard", "--", ":(glob)**/.modaignore"]).map(withPrefix).filter((p2) => !ignoredDirs.some((d) => p2.startsWith(d))).filter((p2) => {
187799
188712
  const excluded = this.ancestorMatch(p2, "ignored");
@@ -187962,13 +188875,14 @@ class Collector {
187962
188875
  this.addToAggregate(ancestor.dir, ancestor.rule, path, isDir);
187963
188876
  return;
187964
188877
  }
188878
+ const collectAll = this.opts.collectIgnored !== false;
187965
188879
  if (isDir) {
187966
188880
  const rule3 = this.dirMatch(path, "ignored");
187967
188881
  if (rule3) {
187968
188882
  this.recordDirOrFile(path, rule3);
187969
188883
  return;
187970
188884
  }
187971
- if (this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, true)) {
188885
+ if (collectAll || this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, true)) {
187972
188886
  this.walk(path, "ignored", null);
187973
188887
  return;
187974
188888
  }
@@ -187988,7 +188902,7 @@ class Collector {
187988
188902
  this.excludeFile(path, "user_exclude", "modaignore", this.sizeOf(path));
187989
188903
  return;
187990
188904
  }
187991
- if (this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, false)) {
188905
+ if (collectAll || this.includeIgnoredRules.size > 0 && this.includeIgnoredRules.matchesWithAncestors(path, false)) {
187992
188906
  this.consider(path, "ignored");
187993
188907
  return;
187994
188908
  }
@@ -188074,8 +188988,9 @@ class Collector {
188074
188988
  }
188075
188989
  return;
188076
188990
  }
188077
- const pathRule = fileRule(path);
188078
- if (pathRule) {
188991
+ const pathRule = fileRule(path) ?? runtimeLogRule(path, status);
188992
+ const envValueFile = pathRule?.reason === "secret_path" && ENV_VALUE_FILE_RULES.has(pathRule.rule) && status === "tracked";
188993
+ if (pathRule && !envValueFile) {
188079
188994
  this.excludeFile(path, pathRule.reason, pathRule.rule, st.size);
188080
188995
  return;
188081
188996
  }
@@ -188106,20 +189021,48 @@ class Collector {
188106
189021
  const text = encoding === "text" ? buf.toString("utf8") : undefined;
188107
189022
  const categoryText = text !== undefined && /^(package\.json|pyproject\.toml|Cargo\.toml)$/.test(basenameOf(path)) ? text : undefined;
188108
189023
  const categories = categorize({ path, encoding, head: text?.slice(0, 1024), text: categoryText });
188109
- const finding = contentSecretFinding(path, buf, text, categories);
188110
- if (finding) {
188111
- this.excludeFile(path, finding.reason, finding.rule, buf.length);
188112
- return;
189024
+ if (envValueFile && !categories.includes("env_example"))
189025
+ categories.push("env_example");
189026
+ const envFile = envValueFile || categories.includes("env_example");
189027
+ let shipped = buf;
189028
+ if (text !== undefined) {
189029
+ const redacted = redactText(path, text, categories, { envFile });
189030
+ if (redacted.withholdRule) {
189031
+ this.excludeFile(path, "secret_content", redacted.withholdRule, buf.length);
189032
+ return;
189033
+ }
189034
+ const credential = redacted.redactions.find((r) => r.rule !== "env_value");
189035
+ if (credential && status !== "tracked" && !categories.includes("env_example")) {
189036
+ this.excludeFile(path, "secret_content", credential.rule, buf.length);
189037
+ return;
189038
+ }
189039
+ const unmasked = registryAuthFindings(path, text).find((finding) => !rangeCovered(redacted.ranges, finding.start, finding.end));
189040
+ if (unmasked) {
189041
+ this.excludeFile(path, "secret_path", unmasked.rule, buf.length);
189042
+ return;
189043
+ }
189044
+ if (redacted.redactions.length > 0) {
189045
+ shipped = Buffer.from(redacted.text, "utf8");
189046
+ for (const r of redacted.redactions)
189047
+ this.redactions.push({ path, ...r });
189048
+ }
189049
+ } else {
189050
+ const finding = contentSecretFinding(path, buf, text, categories);
189051
+ if (finding) {
189052
+ this.excludeFile(path, finding.reason, finding.rule, buf.length);
189053
+ return;
189054
+ }
188113
189055
  }
188114
189056
  this.included.set(path, {
188115
189057
  path,
188116
- bytes: buf.length,
188117
- sha256: sha256(buf),
189058
+ bytes: shipped.length,
189059
+ sha256: sha256(shipped),
188118
189060
  executable: (st.mode & 73) !== 0,
188119
189061
  encoding,
188120
- categories,
189062
+ categories: [...categories].sort(),
188121
189063
  git: status,
188122
- absolutePath: abs
189064
+ absolutePath: abs,
189065
+ ...shipped !== buf ? { redaction: { envFile }, sourceSha256: sha256(buf) } : {}
188123
189066
  });
188124
189067
  if (text !== undefined) {
188125
189068
  if (isToolchainFile(path))
@@ -188304,7 +189247,15 @@ class Collector {
188304
189247
  ].sort((a, b) => compare(a.path, b.path) || compare(a.reason, b.reason));
188305
189248
  const textOf = (path) => this.texts.get(path);
188306
189249
  const toolchain = detectToolchain(files.map((f) => f.path), textOf, harnessRel);
188307
- const manifestFiles = files.map(({ absolutePath: _absolutePath, linkTargetOnDisk: _linkTargetOnDisk, ...file }) => file);
189250
+ const manifestFiles = files.map(({
189251
+ absolutePath: _absolutePath,
189252
+ linkTargetOnDisk: _linkTargetOnDisk,
189253
+ redaction: _redaction,
189254
+ sourceSha256: _sourceSha256,
189255
+ ...file
189256
+ }) => file);
189257
+ const shippedPaths = new Set(manifestFiles.map((f) => f.path));
189258
+ const redactions = this.redactions.filter((r) => shippedPaths.has(r.path)).sort((a, b) => compare(a.path, b.path) || a.line - b.line);
188308
189259
  const bytes = manifestFiles.reduce((sum, f) => sum + f.bytes, 0);
188309
189260
  const manifest = {
188310
189261
  schema: HARNESS_BUNDLE_SCHEMA,
@@ -188326,6 +189277,7 @@ class Collector {
188326
189277
  totals: { files: manifestFiles.length, bytes, excluded: excluded.length },
188327
189278
  files: manifestFiles,
188328
189279
  excluded,
189280
+ ...redactions.length > 0 ? { redactions } : {},
188329
189281
  ignoredPresent: this.collapseIgnored(),
188330
189282
  toolchain,
188331
189283
  env: this.env.finalize(new Set(manifestFiles.map((f) => f.path))),
@@ -188471,6 +189423,19 @@ function isOutside(rel2) {
188471
189423
  function stripUndefined(value) {
188472
189424
  return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));
188473
189425
  }
189426
+ function rangeCovered(ranges, start, end) {
189427
+ let at = start;
189428
+ for (const [s, e] of ranges) {
189429
+ if (e <= at)
189430
+ continue;
189431
+ if (s > at)
189432
+ return false;
189433
+ at = e;
189434
+ if (at >= end)
189435
+ return true;
189436
+ }
189437
+ return at >= end;
189438
+ }
188474
189439
  // ../../packages/harness-bundle/src/collect/write.ts
188475
189440
  import { createHash as createHash3, randomUUID } from "node:crypto";
188476
189441
  import { createWriteStream, existsSync as existsSync5, lstatSync as lstatSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync5, readlinkSync as readlinkSync2, realpathSync as realpathSync3, renameSync, rmSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
@@ -188634,6 +189599,14 @@ function replaceTogether(moves, token) {
188634
189599
  for (const [backup] of backups)
188635
189600
  rmSync(backup, { force: true });
188636
189601
  }
189602
+ function shippedBody(file, disk = readFileSync5(file.absolutePath)) {
189603
+ if (!file.redaction)
189604
+ return disk;
189605
+ const body = Buffer.from(redactText(file.path, disk.toString("utf8"), file.categories, file.redaction).text, "utf8");
189606
+ if (sha256(body) !== file.sha256)
189607
+ throw new Error(`${file.path} changed after collection; re-run the collector`);
189608
+ return body;
189609
+ }
188637
189610
  function* tarChunks(files) {
188638
189611
  const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
188639
189612
  for (const file of sorted) {
@@ -188652,10 +189625,10 @@ function* tarChunks(files) {
188652
189625
  } else {
188653
189626
  const body = readFileSync5(file.absolutePath);
188654
189627
  const executableNow = (statSync(file.absolutePath).mode & 73) !== 0;
188655
- if (sha256(body) !== file.sha256 || executableNow !== file.executable) {
189628
+ if (sha256(body) !== (file.sourceSha256 ?? file.sha256) || executableNow !== file.executable) {
188656
189629
  throw new Error(`${file.path} changed after collection; re-run the collector`);
188657
189630
  }
188658
- entry = { path: file.path, type: "file", mode: file.executable ? 493 : 420, body };
189631
+ entry = { path: file.path, type: "file", mode: file.executable ? 493 : 420, body: shippedBody(file, body) };
188659
189632
  }
188660
189633
  yield* encodeEntry(entry);
188661
189634
  }
@@ -188689,7 +189662,6 @@ async function collectRegistration(request) {
188689
189662
  throw new Error(`Git could not list this checkout, so the bundle would ignore .gitignore; nothing was collected. ${gitFailure ?? "Check that git works here (safe.directory, a readable index)."}`);
188690
189663
  }
188691
189664
  const deployArtifacts = request.imageRef ? [{ producedBy: "customer", kind: "oci_image", digest: request.imageRef.slice(request.imageRef.lastIndexOf("@") + 1), imageRef: request.imageRef }] : [];
188692
- redactEnvExamples(result, join7(request.outDir, "redacted"));
188693
189665
  const written = await writeBundle(result, request.outDir, { deployArtifacts: async () => deployArtifacts });
188694
189666
  const stage = join7(request.outDir, "stage");
188695
189667
  let dependencyMap;
@@ -188701,6 +189673,7 @@ async function collectRegistration(request) {
188701
189673
  } finally {
188702
189674
  rmSync2(stage, { recursive: true, force: true });
188703
189675
  }
189676
+ keepRedactedExamplesFalse(dependencyMap, written.manifest);
188704
189677
  const mapPath = join7(request.outDir, "dependency-map.json");
188705
189678
  writeFileSync4(mapPath, `${JSON.stringify(dependencyMap, null, 2)}
188706
189679
  `);
@@ -188713,64 +189686,20 @@ async function collectRegistration(request) {
188713
189686
  dependencyMapFile: await digestFile(mapPath)
188714
189687
  };
188715
189688
  }
188716
- var MAX_ENV_RESCAN_BYTES = 2 * 1024 * 1024;
188717
- function redactEnvExamples(result, dir) {
188718
- const byPath = new Map(result.manifest.files.map((file) => [file.path, file]));
188719
- let redacted = false;
188720
- for (const file of result.files) {
188721
- if (!file.categories.includes("env_example") || file.symlinkTarget !== undefined)
188722
- continue;
188723
- redacted = true;
188724
- const body = Buffer.from(envNamesOnly(readFileSync6(file.absolutePath, "utf8")));
188725
- const target = join7(dir, file.path);
188726
- mkdirSync4(dirname5(target), { recursive: true });
188727
- writeFileSync4(target, body, { mode: file.executable ? 493 : 420 });
188728
- const sha2562 = createHash4("sha256").update(body).digest("hex");
188729
- Object.assign(file, { absolutePath: target, sha256: sha2562, bytes: body.byteLength });
188730
- const listed = byPath.get(file.path);
188731
- if (listed)
188732
- Object.assign(listed, { sha256: sha2562, bytes: body.byteLength });
188733
- }
188734
- result.manifest.totals.bytes = result.manifest.files.reduce((sum, file) => sum + file.bytes, 0);
188735
- if (redacted) {
188736
- const env = new EnvCollector;
188737
- for (const file of result.files) {
188738
- if (file.encoding !== "text" || file.symlinkTarget !== undefined || file.bytes > MAX_ENV_RESCAN_BYTES)
188739
- continue;
188740
- env.scanFile(file.path, readFileSync6(file.absolutePath, "utf8"), file.categories);
188741
- }
188742
- result.manifest.env = env.finalize(new Set(result.files.map((file) => file.path)));
188743
- }
188744
- }
188745
- function envNamesOnly(text) {
188746
- const names = [];
188747
- let open = null;
188748
- for (const line of text.split(`
188749
- `)) {
188750
- if (open) {
188751
- if (closes(line, open, 0))
188752
- open = null;
188753
- continue;
188754
- }
188755
- const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
188756
- if (!match)
189689
+ function keepRedactedExamplesFalse(map, manifest) {
189690
+ const redactedPaths = new Set((manifest.redactions ?? []).map((r) => r.path));
189691
+ const redactedEnvFiles = new Set(manifest.files.filter((file) => file.categories.includes("env_example") && redactedPaths.has(file.path)).map((file) => file.path));
189692
+ if (redactedEnvFiles.size === 0)
189693
+ return;
189694
+ const collected = new Map(manifest.env.map((ref) => [ref.name, ref]));
189695
+ const sourcePath = (source) => source.replace(/:\d+$/, "");
189696
+ for (const ref of map.env) {
189697
+ const seen = collected.get(ref.name);
189698
+ if (!seen || seen.hasExampleValue || !ref.hasExampleValue)
188757
189699
  continue;
188758
- names.push(match[1]);
188759
- const quote = match[2][0];
188760
- if ((quote === '"' || quote === "'" || quote === "`") && !closes(match[2], quote, 1))
188761
- open = quote;
188762
- }
188763
- return names.map((name) => `${name}=
188764
- `).join("");
188765
- }
188766
- function closes(text, quote, from) {
188767
- for (let i = from;i < text.length; i++) {
188768
- if (text[i] === "\\" && quote !== "'")
188769
- i++;
188770
- else if (text[i] === quote)
188771
- return true;
189700
+ if (seen.sources.some((source) => redactedEnvFiles.has(sourcePath(source))))
189701
+ ref.hasExampleValue = false;
188772
189702
  }
188773
- return false;
188774
189703
  }
188775
189704
  function insideCheckout(dir) {
188776
189705
  for (let current = realpathSync4(dir);; current = dirname5(current)) {
@@ -188983,7 +189912,7 @@ function parseRegisterOptions(context, argv) {
188983
189912
  }
188984
189913
  function defaultHarnessName(root) {
188985
189914
  try {
188986
- const name = JSON.parse(readFileSync7(join8(root, "package.json"), "utf8")).name;
189915
+ const name = JSON.parse(readFileSync6(join8(root, "package.json"), "utf8")).name;
188987
189916
  if (typeof name === "string" && name)
188988
189917
  return sanitizeName(name.replace(/^@[^/]+\//, ""));
188989
189918
  } catch {}
@@ -189019,6 +189948,7 @@ function buildRegisterReport(collected) {
189019
189948
  byCategory: [...byCategory].map(([category, entry]) => ({ category, ...entry })).sort((a, b) => b.bytes - a.bytes)
189020
189949
  },
189021
189950
  excluded: manifest.excluded,
189951
+ redactions: manifest.redactions ?? [],
189022
189952
  ignoredPresent: manifest.ignoredPresent,
189023
189953
  env,
189024
189954
  toolchain: manifest.toolchain,
@@ -189133,6 +190063,15 @@ function renderRegisterReport(report, registration) {
189133
190063
  const rule2 = item.rule ? `, rule ${item.rule}` : "";
189134
190064
  lines.push(` ${pad(item.path, 40)} ${item.reason}: ${EXCLUSION_REASONS[item.reason] ?? item.reason}${rule2}${size}`);
189135
190065
  }
190066
+ if (report.redactions.length > 0) {
190067
+ lines.push("");
190068
+ lines.push(`Redacted: ${report.redactions.length} value(s) masked in place. The files ship; the values never leave this machine.`);
190069
+ for (const item of report.redactions.slice(0, 40)) {
190070
+ lines.push(` ${pad(`${item.path}:${item.line}`, 40)} ${item.rule} (${item.length} chars)`);
190071
+ }
190072
+ if (report.redactions.length > 40)
190073
+ lines.push(` ... ${report.redactions.length - 40} more (see the manifest)`);
190074
+ }
189136
190075
  if (report.ignoredPresent.length > 0) {
189137
190076
  lines.push("");
189138
190077
  lines.push(`Git-ignored but present, not uploaded (names only): ${report.ignoredPresent.length}`);
@@ -189239,7 +190178,7 @@ function formatBytes(bytes) {
189239
190178
  }
189240
190179
 
189241
190180
  // src/harness-artifact-bodies.ts
189242
- import { existsSync as existsSync7, lstatSync as lstatSync5, readFileSync as readFileSync8, realpathSync as realpathSync5 } from "node:fs";
190181
+ import { existsSync as existsSync7, lstatSync as lstatSync5, readFileSync as readFileSync7, realpathSync as realpathSync5 } from "node:fs";
189243
190182
  import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5 } from "node:path";
189244
190183
  var MAX_SCANNED_FILE_BYTES2 = 512 * 1024;
189245
190184
  var SNAPSHOT_BUDGET_SKIP_REASONS = new Set([
@@ -189303,7 +190242,7 @@ function readBoundedRepoText(absPath, rootDir) {
189303
190242
  return;
189304
190243
  }
189305
190244
  try {
189306
- return readFileSync8(absPath, "utf8");
190245
+ return readFileSync7(absPath, "utf8");
189307
190246
  } catch {
189308
190247
  return;
189309
190248
  }
@@ -189854,9 +190793,9 @@ function detectPromptLiterals(file, out) {
189854
190793
  });
189855
190794
  };
189856
190795
  if (ext === ".py") {
189857
- const tripleQuoted = /("""|''')([\s\S]*?)\1/g;
190796
+ const tripleQuoted2 = /("""|''')([\s\S]*?)\1/g;
189858
190797
  let match;
189859
- while ((match = tripleQuoted.exec(file.text)) !== null) {
190798
+ while ((match = tripleQuoted2.exec(file.text)) !== null) {
189860
190799
  record(match.index, match.index + match[0].length - 1, match[2], "python.triple_quoted_string");
189861
190800
  }
189862
190801
  return;
@@ -190036,7 +190975,7 @@ import { extname as extname8 } from "node:path";
190036
190975
  // src/prompt-sources.ts
190037
190976
  var import_typescript2 = __toESM(require_typescript(), 1);
190038
190977
  import { createHash as createHash5 } from "node:crypto";
190039
- import { lstatSync as lstatSync7, readFileSync as readFileSync11, realpathSync as realpathSync8 } from "node:fs";
190978
+ import { lstatSync as lstatSync7, readFileSync as readFileSync10, realpathSync as realpathSync8 } from "node:fs";
190040
190979
  import { extname as extname3, isAbsolute as isAbsolute4, relative as relative5, resolve as resolve9 } from "node:path";
190041
190980
 
190042
190981
  // src/prompt-frameworks.ts
@@ -190058,7 +190997,7 @@ var TYPESCRIPT_AGENT_MODULES = [
190058
190997
 
190059
190998
  // src/prompt-source-code.ts
190060
190999
  var import_typescript = __toESM(require_typescript(), 1);
190061
- import { existsSync as existsSync8, lstatSync as lstatSync6, readFileSync as readFileSync9, realpathSync as realpathSync6 } from "node:fs";
191000
+ import { existsSync as existsSync8, lstatSync as lstatSync6, readFileSync as readFileSync8, realpathSync as realpathSync6 } from "node:fs";
190062
191001
  import { dirname as dirname8, extname as extname2, join as join9, resolve as resolve7 } from "node:path";
190063
191002
  var EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
190064
191003
  var MAX_BYTES = 2 * 1024 * 1024;
@@ -190099,7 +191038,7 @@ function parseFile(filePath, state) {
190099
191038
  const cached = state.parsed.get(path);
190100
191039
  if (cached)
190101
191040
  return cached;
190102
- const text = readFileSync9(path, "utf8");
191041
+ const text = readFileSync8(path, "utf8");
190103
191042
  state.bytesRead += Buffer.byteLength(text);
190104
191043
  state.files.push(path);
190105
191044
  const scriptKind = extname2(path) === ".json" ? import_typescript.default.ScriptKind.JSON : /\.tsx?$/.test(extname2(path)) ? import_typescript.default.ScriptKind.TSX : import_typescript.default.ScriptKind.JSX;
@@ -190537,7 +191476,7 @@ function referenceNames(node) {
190537
191476
 
190538
191477
  // src/prompt-source-python.ts
190539
191478
  import { spawnSync as spawnSync2 } from "node:child_process";
190540
- import { readFileSync as readFileSync10, realpathSync as realpathSync7 } from "node:fs";
191479
+ import { readFileSync as readFileSync9, realpathSync as realpathSync7 } from "node:fs";
190541
191480
  import { resolve as resolve8 } from "node:path";
190542
191481
  var PYTHON_AGENT_MODULES_LITERAL = JSON.stringify(PYTHON_AGENT_MODULES);
190543
191482
  var SELECTOR_HELPER = String.raw`import ast,json,os,sys
@@ -191004,7 +191943,7 @@ except Exception as e:
191004
191943
  function extractPythonSource(rootDir, sourcePath, symbol, _forceDependencyGraph = false) {
191005
191944
  let raw = "";
191006
191945
  try {
191007
- raw = readFileSync10(sourcePath, "utf8");
191946
+ raw = readFileSync9(sourcePath, "utf8");
191008
191947
  } catch {}
191009
191948
  const result = spawnSync2("python3", ["-I", "-c", HELPER], {
191010
191949
  input: JSON.stringify({ root: rootDir, path: sourcePath, symbol, force: _forceDependencyGraph }),
@@ -191098,7 +192037,7 @@ function resolvePromptSource(rootDir, source) {
191098
192037
  } else if (source.selector?.symbol) {
191099
192038
  throw new Error(`${sourcePath}: symbol selectors require a JavaScript, TypeScript, or Python source file`);
191100
192039
  } else if (ext === ".json" || ext === ".yaml" || ext === ".yml" || hasPointer || source.kind === "messages") {
191101
- const parsed = parseStructured(readFileSync11(sourceAbs, "utf8"), sourcePath, ext);
192040
+ const parsed = parseStructured(readFileSync10(sourceAbs, "utf8"), sourcePath, ext);
191102
192041
  const selected = hasPointer ? jsonPointer(parsed, source.selector.pointer, sourcePath) : parsed;
191103
192042
  const selectedField = source.selector?.pointer?.split("/").at(-1)?.replaceAll("~1", "/").replaceAll("~0", "~");
191104
192043
  const inferredKind = !source.kind && selectedField && /^(?:template|system_template|instance_template)$/.test(selectedField) ? "template" : source.kind;
@@ -191108,7 +192047,7 @@ function resolvePromptSource(rootDir, source) {
191108
192047
  messages = structured.messages;
191109
192048
  kind = structured.kind;
191110
192049
  } else {
191111
- const raw = readFileSync11(sourceAbs, "utf8");
192050
+ const raw = readFileSync10(sourceAbs, "utf8");
191112
192051
  if (ext === ".md" && /\.prompt\.md$/i.test(sourcePath)) {
191113
192052
  const legacy = parseLegacyMarkdown(raw);
191114
192053
  content = legacy.content;
@@ -191131,7 +192070,7 @@ function resolvePromptSource(rootDir, source) {
191131
192070
  const depAbs = safePath(root, dependency.path);
191132
192071
  files.add(depAbs);
191133
192072
  if (typeof dependency.selector?.pointer === "string") {
191134
- const parsed = parseStructured(readFileSync11(depAbs, "utf8"), dependency.path, extname3(depAbs).toLowerCase());
192073
+ const parsed = parseStructured(readFileSync10(depAbs, "utf8"), dependency.path, extname3(depAbs).toLowerCase());
191135
192074
  jsonPointer(parsed, dependency.selector.pointer, dependency.path);
191136
192075
  } else if (dependency.selector?.symbol && CODE_EXTENSIONS2.has(extname3(depAbs).toLowerCase())) {
191137
192076
  const dep = extname3(depAbs).toLowerCase() === ".py" ? extractPythonSource(root, depAbs, dependency.selector.symbol) : extractCodeSource(root, depAbs, dependency.selector.symbol);
@@ -191154,7 +192093,7 @@ function resolvePromptSource(rootDir, source) {
191154
192093
  assertSafeMaterializedContent(content, sourcePath);
191155
192094
  else
191156
192095
  assertSafeMaterializedContent(materialized, sourcePath);
191157
- const dependencies = [...files].sort().map((path) => ({ path: relative5(root, path).replaceAll("\\", "/"), contentHash: sha2562(readFileSync11(path)) }));
192096
+ const dependencies = [...files].sort().map((path) => ({ path: relative5(root, path).replaceAll("\\", "/"), contentHash: sha2562(readFileSync10(path)) }));
191158
192097
  const sourceHash = sha2562(JSON.stringify({ kind, content, systemPrompt: systemPrompt ?? null, messages: messages ?? null }));
191159
192098
  const sourceDefinition = {
191160
192099
  schemaVersion: 1,
@@ -191224,7 +192163,7 @@ function discoverStructuredPromptSources(rootDir, sourcePath) {
191224
192163
  const ext = extname3(file).toLowerCase();
191225
192164
  if (![".json", ".yaml", ".yml"].includes(ext))
191226
192165
  return [];
191227
- const value = parseStructured(readFileSync11(file, "utf8"), sourcePath, ext);
192166
+ const value = parseStructured(readFileSync10(file, "utf8"), sourcePath, ext);
191228
192167
  const sources = [];
191229
192168
  const base = sourcePath.replace(/\.[^.]+$/, "").replace(/[^A-Za-z0-9_.-]+/g, ".");
191230
192169
  const dedicatedPath = /(^|\/)prompts?(?:\/|\.[^/]+$)|\.prompt\.[^.]+$/i.test(sourcePath);
@@ -191300,14 +192239,14 @@ function discoverCodePromptCandidates(rootDir, sourcePath) {
191300
192239
  addName(candidate.symbol, candidate.knownBuilder === true);
191301
192240
  modelInputs.add(candidate.symbol);
191302
192241
  }
191303
- for (const match of readFileSync11(file, "utf8").matchAll(/^(?:(?:async\s+)?def\s+([A-Za-z_]\w*)|([A-Za-z_]\w*)\s*=\s*([^\n#]+))/gm)) {
192242
+ for (const match of readFileSync10(file, "utf8").matchAll(/^(?:(?:async\s+)?def\s+([A-Za-z_]\w*)|([A-Za-z_]\w*)\s*=\s*([^\n#]+))/gm)) {
191304
192243
  const name = match[1] ?? match[2];
191305
192244
  const rhs = match[3] ?? "";
191306
192245
  if (name && isPromptName2(name) && (match[1] || /['"`\[({+]/.test(rhs) || /^[A-Za-z_]\w*\s*$/.test(rhs)))
191307
192246
  addName(name, Boolean(match[1]));
191308
192247
  }
191309
192248
  } else {
191310
- const sf = import_typescript2.default.createSourceFile(file, readFileSync11(file, "utf8"), import_typescript2.default.ScriptTarget.Latest, true, import_typescript2.default.ScriptKind.TSX);
192249
+ const sf = import_typescript2.default.createSourceFile(file, readFileSync10(file, "utf8"), import_typescript2.default.ScriptTarget.Latest, true, import_typescript2.default.ScriptKind.TSX);
191311
192250
  const agentConstructors = new Set;
191312
192251
  for (const statement of sf.statements) {
191313
192252
  if (!import_typescript2.default.isImportDeclaration(statement) || !import_typescript2.default.isStringLiteral(statement.moduleSpecifier) || !TYPESCRIPT_AGENT_MODULES.includes(statement.moduleSpecifier.text))
@@ -191541,7 +192480,7 @@ function sha2562(input) {
191541
192480
  }
191542
192481
 
191543
192482
  // src/prompt-source-verification/index.ts
191544
- import { readFileSync as readFileSync15 } from "node:fs";
192483
+ import { readFileSync as readFileSync14 } from "node:fs";
191545
192484
  import { z } from "zod";
191546
192485
 
191547
192486
  // src/prompt-source-validation.ts
@@ -191630,7 +192569,7 @@ function isPromptDefinitionPath(path) {
191630
192569
  }
191631
192570
 
191632
192571
  // src/prompt-source-verification/evidence.ts
191633
- import { readFileSync as readFileSync14 } from "node:fs";
192572
+ import { readFileSync as readFileSync13 } from "node:fs";
191634
192573
  import { extname as extname7 } from "node:path";
191635
192574
 
191636
192575
  // src/prompt-source-verification/paths.ts
@@ -191660,14 +192599,14 @@ function safePromptEvidencePath(rootDir, path) {
191660
192599
  var import_typescript3 = __toESM(require_typescript(), 1);
191661
192600
  import { execFileSync as execFileSync2 } from "node:child_process";
191662
192601
  import { extname as extname5 } from "node:path";
191663
- import { readFileSync as readFileSync12 } from "node:fs";
192602
+ import { readFileSync as readFileSync11 } from "node:fs";
191664
192603
  function proveSink(root, sink) {
191665
192604
  const file = safePromptEvidencePath(root, sink.path);
191666
192605
  const cache = new Map;
191667
192606
  if (!cache.has(file)) {
191668
192607
  if (extname5(file) === ".py") {
191669
192608
  try {
191670
- const rows = JSON.parse(execFileSync2("python3", ["-I", "-c", PYTHON_PROOF], { input: readFileSync12(file, "utf8"), encoding: "utf8", timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }));
192609
+ const rows = JSON.parse(execFileSync2("python3", ["-I", "-c", PYTHON_PROOF], { input: readFileSync11(file, "utf8"), encoding: "utf8", timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }));
191671
192610
  cache.set(file, new Map(Object.entries(rows).map(([line, proof]) => [Number(line), proof])));
191672
192611
  } catch {
191673
192612
  cache.set(file, new Map);
@@ -191834,7 +192773,7 @@ print(json.dumps(rows))
191834
192773
  `;
191835
192774
 
191836
192775
  // src/prompt-source-location.ts
191837
- import { readFileSync as readFileSync13 } from "node:fs";
192776
+ import { readFileSync as readFileSync12 } from "node:fs";
191838
192777
  import { extname as extname6 } from "node:path";
191839
192778
  function structuredExcerpt(text, source) {
191840
192779
  if (source.selector?.pointer === undefined)
@@ -191888,7 +192827,7 @@ function promptDefinitionExcerpt(rootDir, source) {
191888
192827
  return;
191889
192828
  return { ...extraction.range, text: extraction.sourceText, ...extraction.declarationKind ? { declarationKind: extraction.declarationKind } : {} };
191890
192829
  }
191891
- const text = readFileSync13(file, "utf8");
192830
+ const text = readFileSync12(file, "utf8");
191892
192831
  return structuredExcerpt(text, source);
191893
192832
  }
191894
192833
  function promptDefinitionRange(rootDir, source) {
@@ -191915,7 +192854,7 @@ function reviewEvidence(rootDir, source, evidence) {
191915
192854
  for (const item of evidence) {
191916
192855
  let text;
191917
192856
  try {
191918
- text = readFileSync14(safePromptEvidencePath(rootDir, item.path), "utf8");
192857
+ text = readFileSync13(safePromptEvidencePath(rootDir, item.path), "utf8");
191919
192858
  } catch (error) {
191920
192859
  if (item.kind === "model_endpoint")
191921
192860
  return { valid: false, endpointVerified: false, reason: error instanceof Error ? error.message : String(error) };
@@ -192030,7 +192969,7 @@ async function independentlyVerifyPromptSource(rootDir, source, verifier) {
192030
192969
  let priorJudgment;
192031
192970
  try {
192032
192971
  const before = JSON.stringify(checked.resolved.sourceDefinition);
192033
- const sourceBytes = readFileSync15(safePromptEvidencePath(rootDir, source.path), "utf8");
192972
+ const sourceBytes = readFileSync14(safePromptEvidencePath(rootDir, source.path), "utf8");
192034
192973
  const instruction = promptVerificationInstruction(checked.source, checked.sourceHash, promptDefinitionExcerpt(rootDir, checked.source));
192035
192974
  for (let attempt = 0;attempt < 2; attempt++) {
192036
192975
  const repair = attempt ? `
@@ -192061,7 +193000,7 @@ ${output}` : "";
192061
193000
  }
192062
193001
  if (reviewed.truncated || reviewed.sourceHash !== checked.sourceHash)
192063
193002
  return pending("Verifier output is truncated or cites a different source hash", reviewed.evidence, reviewed.role, normalizeUsage(undefined, false));
192064
- if (before !== snapshot() || readFileSync15(safePromptEvidencePath(rootDir, source.path), "utf8") !== sourceBytes)
193003
+ if (before !== snapshot() || readFileSync14(safePromptEvidencePath(rootDir, source.path), "utf8") !== sourceBytes)
192065
193004
  return pending("Source changed during verification", reviewed.evidence, reviewed.role, normalizeUsage(undefined, false));
192066
193005
  if (reviewed.role === "non_prompt")
192067
193006
  return { status: "rejected", reason: reviewed.reason, evidence: reviewed.evidence, sourceHash: checked.sourceHash, role: reviewed.role, usage: normalizeUsage(undefined, false) };
@@ -192421,7 +193360,7 @@ function applyPromptDiscovery(graph, discovery) {
192421
193360
  }
192422
193361
 
192423
193362
  // src/harness-candidate-promotion.ts
192424
- import { readFileSync as readFileSync16 } from "node:fs";
193363
+ import { readFileSync as readFileSync15 } from "node:fs";
192425
193364
  import { resolve as resolve12 } from "node:path";
192426
193365
  var PROMOTED_CONFIDENCE = {
192427
193366
  high: 0.85,
@@ -192452,7 +193391,7 @@ function readFileLines(rootDir, path, cache) {
192452
193391
  return cached;
192453
193392
  let lines;
192454
193393
  try {
192455
- lines = readFileSync16(resolve12(rootDir, path), "utf8").split(/\r?\n/);
193394
+ lines = readFileSync15(resolve12(rootDir, path), "utf8").split(/\r?\n/);
192456
193395
  } catch {
192457
193396
  lines = null;
192458
193397
  }
@@ -193996,7 +194935,7 @@ async function runHarnessCommand(context) {
193996
194935
  throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
193997
194936
  }
193998
194937
  if (context.flags["github-actions"] === "true") {
193999
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-nph5t2s7.js");
194938
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-tmy3gqwa.js");
194000
194939
  const result = await runGithubActionsAnalyze(rootDir, {
194001
194940
  writeReport: (report2) => {
194002
194941
  const normalized = normalizeHarnessReport(report2);
@@ -194330,7 +195269,7 @@ async function runPromptConcernAnalysis(context, rootDir, scanPlan, tenant) {
194330
195269
  const manifest = join10(rootDir, ".moda/prompts.yml");
194331
195270
  let existing = [];
194332
195271
  if (existsSync10(manifest)) {
194333
- const document = $parseDocument(readFileSync17(manifest, "utf8"));
195272
+ const document = $parseDocument(readFileSync16(manifest, "utf8"));
194334
195273
  if (document.errors.length)
194335
195274
  throw new CliInputError("Invalid prompt manifest.", document.errors[0].message);
194336
195275
  existing = document.toJS({ maxAliasCount: 20 })?.sources ?? [];
@@ -194801,7 +195740,7 @@ function repairCitationLines(report, rootDir) {
194801
195740
  continue;
194802
195741
  let text;
194803
195742
  try {
194804
- text = readFileSync17(candidatePath, "utf8");
195743
+ text = readFileSync16(candidatePath, "utf8");
194805
195744
  } catch {
194806
195745
  continue;
194807
195746
  }
@@ -196018,7 +196957,7 @@ function collectSnapshotInputs(rootDir, maxFiles, maxTextBytes) {
196018
196957
  let text = "";
196019
196958
  if (isTextFile(path)) {
196020
196959
  try {
196021
- text = readFileSync17(current, "utf8");
196960
+ text = readFileSync16(current, "utf8");
196022
196961
  inventoryTextBytes += Buffer.byteLength(text, "utf8");
196023
196962
  } catch {
196024
196963
  return;
@@ -196258,7 +197197,7 @@ function readHarnessSyncState(rootDir) {
196258
197197
  if (!existsSync10(path))
196259
197198
  return;
196260
197199
  try {
196261
- const parsed = JSON.parse(readFileSync17(path, "utf8"));
197200
+ const parsed = JSON.parse(readFileSync16(path, "utf8"));
196262
197201
  if (!isRecord4(parsed) || parsed.schemaVersion !== "sync_state.v0.1") {
196263
197202
  return;
196264
197203
  }
@@ -196392,7 +197331,7 @@ function warnFresherSubdirHarnessReports(context, rootDir, explicitReport) {
196392
197331
  writeHarnessNotice(context, `A fresher harness report exists at ${newest.path} (this directory has ${rootState}). If you meant that report, re-run with --path=${subdir}.`);
196393
197332
  }
196394
197333
  function readHarnessGraph(manifestPath) {
196395
- return JSON.parse(readFileSync17(manifestPath, "utf8"));
197334
+ return JSON.parse(readFileSync16(manifestPath, "utf8"));
196396
197335
  }
196397
197336
  function writeHarnessArtifacts(graph, rootDir, options = {}) {
196398
197337
  const modaDir = join10(rootDir, MODA_DIR);
@@ -196631,7 +197570,7 @@ function inventoryFiles(rootDir, skipped, options = {}) {
196631
197570
  recordSkipped(skipped, { path: rel2, reason: "scan text budget exceeded" });
196632
197571
  } else {
196633
197572
  try {
196634
- item.text = readFileSync17(current, "utf8");
197573
+ item.text = readFileSync16(current, "utf8");
196635
197574
  textBytes += Buffer.byteLength(item.text, "utf8");
196636
197575
  } catch {
196637
197576
  recordSkipped(skipped, { path: rel2, reason: "text read failed" });
@@ -196938,7 +197877,7 @@ function maybeAddPromptArtifact(graph, file) {
196938
197877
  if (!isPrompt)
196939
197878
  return;
196940
197879
  const id = stableId2("prompt", file.path);
196941
- const text = file.text ?? readFileSync17(file.absPath, "utf8");
197880
+ const text = file.text ?? readFileSync16(file.absPath, "utf8");
196942
197881
  upsertArtifact(graph, {
196943
197882
  id,
196944
197883
  type: "prompt",
@@ -197519,7 +198458,7 @@ function detectProviderPackagesInManifests(rootDir) {
197519
198458
  const packageJsonPath = join10(rootDir, "package.json");
197520
198459
  if (existsSync10(packageJsonPath)) {
197521
198460
  try {
197522
- const deps = packageDependencyNames(readFileSync17(packageJsonPath, "utf8"));
198461
+ const deps = packageDependencyNames(readFileSync16(packageJsonPath, "utf8"));
197523
198462
  for (const provider of PROVIDER_CATALOG) {
197524
198463
  for (const name of provider.packageNames) {
197525
198464
  if (deps.has(name))
@@ -197531,7 +198470,7 @@ function detectProviderPackagesInManifests(rootDir) {
197531
198470
  const pyprojectPath = join10(rootDir, "pyproject.toml");
197532
198471
  if (existsSync10(pyprojectPath)) {
197533
198472
  try {
197534
- const deps = pyprojectDependencyNames(readFileSync17(pyprojectPath, "utf8"));
198473
+ const deps = pyprojectDependencyNames(readFileSync16(pyprojectPath, "utf8"));
197535
198474
  for (const provider of PROVIDER_CATALOG) {
197536
198475
  for (const name of provider.packageNames) {
197537
198476
  if (deps.has(name.toLowerCase()))
@@ -197948,7 +198887,7 @@ function readSmallTextFileForHash(absPath, rootDir) {
197948
198887
  if (!isTextFile(rel2))
197949
198888
  return;
197950
198889
  try {
197951
- return readFileSync17(absPath, "utf8");
198890
+ return readFileSync16(absPath, "utf8");
197952
198891
  } catch {
197953
198892
  return;
197954
198893
  }