@mnemonik/cli 7.141.0 → 7.141.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/auth/credentials.js +266 -3
  2. package/dist/auth/index.js +266 -3
  3. package/dist/auth/installSession.js +262 -0
  4. package/dist/auth/status.js +262 -0
  5. package/dist/authorizationWords.js +262 -0
  6. package/dist/diagnostics.js +266 -3
  7. package/dist/digests.json +38 -38
  8. package/dist/hooksLog.js +4 -0
  9. package/dist/identity/migrate.js +266 -3
  10. package/dist/install/hosts.js +262 -0
  11. package/dist/install/journal.js +266 -3
  12. package/dist/install/journey.js +266 -3
  13. package/dist/install/ownership.js +266 -3
  14. package/dist/install/transaction.js +266 -3
  15. package/dist/install/ui.js +266 -3
  16. package/dist/installSession.js +262 -0
  17. package/dist/installation.js +266 -3
  18. package/dist/launcher.js +266 -3
  19. package/dist/preflight.js +354 -92
  20. package/dist/project/eligibility.js +266 -3
  21. package/dist/project/records.js +266 -3
  22. package/dist/project.js +270 -7
  23. package/dist/router.js +270 -7
  24. package/dist/runtime/releaseSource.js +266 -3
  25. package/dist/runtime/selfUpdate.js +266 -3
  26. package/dist/runtime/signers.js +67 -7
  27. package/dist/runtime/store.js +4 -0
  28. package/dist/runtime/updateCheck.js +266 -3
  29. package/dist/sbom.json +21 -21
  30. package/dist/scanner/control.js +266 -3
  31. package/dist/scanner/data.js +262 -0
  32. package/dist/scanner/discover.js +354 -92
  33. package/dist/scanner/enable.js +270 -7
  34. package/dist/scanner/picker.js +325 -63
  35. package/dist/scanner/service.js +266 -3
  36. package/dist/scanner/update.js +266 -3
  37. package/dist/scanner-release.json +24 -24
  38. package/dist/status.js +266 -3
  39. package/dist/vendor/local-setup.js +266 -3
  40. package/package.json +31 -31
@@ -4961,6 +4961,268 @@ var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm")
4961
4961
  // packages/shared/dist/ast/astChunker.js
4962
4962
  var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
4963
4963
 
4964
+ // packages/shared/dist/redactCredentials.js
4965
+ var REDACTED = "<redacted credential>";
4966
+ var MAX_PEM_BODY_LENGTH = 8 * 1024;
4967
+ var LETTER_OR_DIGIT = /[A-Za-z0-9]/;
4968
+ function hasLetterOrDigit(value) {
4969
+ return LETTER_OR_DIGIT.test(value);
4970
+ }
4971
+ function redactContextValue(match, head = "", value = "") {
4972
+ return hasLetterOrDigit(value) ? `${head}${REDACTED}` : match;
4973
+ }
4974
+ var PLACEHOLDER_PASSWORD = /^(?:\[REDACTED(?::[A-Za-z0-9_-]+)?\]|redacted|<[^<>]*>|\{\{[^{}]*\}\}|\$\{[^{}]+\}|%[A-Za-z_][A-Za-z0-9_]*%|\*+|x{3,}|\.\.\.|\u2026|password|passwd|pass|pwd|pw|secret|changeme|change[_-]me|example|test|(?:your|my)[a-z_-]*?(?:password|passwd|pass|pwd|secret|key|token|here))$/i;
4975
+ var PLACEHOLDER_ENV_VAR = /^\$[A-Z_][A-Z0-9_]*$/;
4976
+ function isPlaceholderPassword(password) {
4977
+ return PLACEHOLDER_PASSWORD.test(password) || PLACEHOLDER_ENV_VAR.test(password);
4978
+ }
4979
+ function redactUrlPassword(match, head = "", password = "") {
4980
+ return isPlaceholderPassword(password) || !hasLetterOrDigit(password) ? match : `${head}${REDACTED}@`;
4981
+ }
4982
+ var SHELL_WORD = String.raw`'[^'\n]*'|"[^"\n]*"|(?!${REDACTED})[^\s'"\`;|&()[\]{}\\,/][^\s'"\`;|&]*`;
4983
+ var OPENING_QUOTE = String.raw`(["'\`]?)`;
4984
+ var TRAILING_PUNCTUATION = /[.,:;!?)\]}]+$/;
4985
+ var PROSE_WORDS = /* @__PURE__ */ new Set([
4986
+ "a",
4987
+ "an",
4988
+ "and",
4989
+ "as",
4990
+ "for",
4991
+ "in",
4992
+ "is",
4993
+ "it",
4994
+ "on",
4995
+ "or",
4996
+ "the",
4997
+ "to",
4998
+ "with",
4999
+ "flag",
5000
+ "option",
5001
+ "argument",
5002
+ "instead",
5003
+ "which",
5004
+ "that",
5005
+ "this",
5006
+ "puts",
5007
+ "passes",
5008
+ "leaks",
5009
+ "exposes"
5010
+ ]);
5011
+ var REGEX_DELIMITER = /\/(?:[{}()[\];]|$)/;
5012
+ function shellWordSecret(word) {
5013
+ const quote = word[0];
5014
+ if ((quote === "'" || quote === '"') && word.length >= 2 && word.endsWith(quote)) {
5015
+ const inner = word.slice(1, -1);
5016
+ if (!hasLetterOrDigit(inner) || quote === '"' && inner.startsWith("$"))
5017
+ return null;
5018
+ if (inner !== inner.trim())
5019
+ return null;
5020
+ return isPlaceholderPassword(inner) ? null : inner;
5021
+ }
5022
+ if (word.startsWith("$"))
5023
+ return null;
5024
+ const core = word.replace(TRAILING_PUNCTUATION, "");
5025
+ if (!hasLetterOrDigit(core) || isPlaceholderPassword(core))
5026
+ return null;
5027
+ if (REGEX_DELIMITER.test(core))
5028
+ return null;
5029
+ return PROSE_WORDS.has(core.toLowerCase()) ? null : core;
5030
+ }
5031
+ function redactShellWord(match, head = "", word = "", tail = "") {
5032
+ return shellWordSecret(word) === null ? match : `${head}${REDACTED}${tail}`;
5033
+ }
5034
+ function redactQuotedShellWord(match, head = "", openingQuote = "", word = "", tail = "") {
5035
+ if (openingQuote !== "" && word.startsWith(openingQuote))
5036
+ return match;
5037
+ return redactShellWord(match, head, word, tail);
5038
+ }
5039
+ var SSHPASS_COMMAND = String.raw`ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*`;
5040
+ function redactSshpassWord(match, head = "", openingQuote = "", word = "", tail = "", command = "") {
5041
+ if (openingQuote !== "" && word.startsWith(openingQuote))
5042
+ return match;
5043
+ const secret = shellWordSecret(word);
5044
+ if (secret === null)
5045
+ return match;
5046
+ const bare = word[0] !== "'" && word[0] !== '"';
5047
+ if (bare && /^[a-z]+$/.test(secret) && command === "")
5048
+ return match;
5049
+ return `${head}${REDACTED}${tail}`;
5050
+ }
5051
+ var PATTERNS = [
5052
+ {
5053
+ kind: "bearer",
5054
+ pattern: /(\bBearer\s+)([^\s]{40,})/gi,
5055
+ replacement: redactContextValue,
5056
+ secretGroup: 2
5057
+ },
5058
+ {
5059
+ // Before openai, whose sk- shape would otherwise claim these keys.
5060
+ kind: "anthropic",
5061
+ pattern: /(?<![A-Za-z0-9_-])sk-ant-(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
5062
+ replacement: REDACTED
5063
+ },
5064
+ {
5065
+ kind: "openai",
5066
+ pattern: /(?<![A-Za-z0-9_-])sk-(?:proj-)?(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
5067
+ replacement: REDACTED
5068
+ },
5069
+ {
5070
+ kind: "shopify",
5071
+ pattern: /(?<![A-Za-z0-9_])shp(?:at|ss|ca)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/g,
5072
+ replacement: REDACTED
5073
+ },
5074
+ {
5075
+ kind: "aws_access_key",
5076
+ pattern: /(?<![A-Z0-9])AKIA[A-Z0-9]{16}(?![A-Z0-9])/g,
5077
+ replacement: REDACTED
5078
+ },
5079
+ {
5080
+ kind: "github",
5081
+ pattern: /(?<![A-Za-z0-9_])gh[opu]_[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
5082
+ replacement: REDACTED
5083
+ },
5084
+ {
5085
+ kind: "slack",
5086
+ pattern: /(?<![A-Za-z0-9-])xox[abp]-(?=-*[A-Za-z0-9])[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
5087
+ replacement: REDACTED
5088
+ },
5089
+ {
5090
+ kind: "sentry",
5091
+ pattern: /(?<![A-Za-z0-9_])sntry[us]_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
5092
+ replacement: REDACTED
5093
+ },
5094
+ {
5095
+ kind: "fly",
5096
+ pattern: /(?<![A-Za-z0-9_])(?:FlyV1\s+)?fm2_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
5097
+ replacement: REDACTED
5098
+ },
5099
+ {
5100
+ kind: "google_api_key",
5101
+ pattern: /(?<![A-Za-z0-9_-])AIza(?=[_-]*[A-Za-z0-9])[0-9A-Za-z_-]{35}(?![A-Za-z0-9_-])/g,
5102
+ replacement: REDACTED
5103
+ },
5104
+ {
5105
+ kind: "jwt",
5106
+ // header.payload.signature, both JSON segments starting eyJ. The length
5107
+ // floors keep `eyJ...` prose and signature-less examples out.
5108
+ pattern: /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
5109
+ replacement: REDACTED
5110
+ },
5111
+ {
5112
+ kind: "mnemonik_component_token",
5113
+ // mnc_ / mncr_ + 43 base64url (oauth/components.ts); 32+ also catches a
5114
+ // partial token. The body must contain an upper-case letter or digit, so a
5115
+ // snake_case identifier is not mistaken for one; the 8-character
5116
+ // display prefix (mnc_ + 8) is too short.
5117
+ pattern: /(?<![A-Za-z0-9_])mncr?_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g,
5118
+ replacement: REDACTED
5119
+ },
5120
+ {
5121
+ kind: "stripe",
5122
+ // Secret and restricted keys (sk_/rk_ live|test) and webhook signing secrets.
5123
+ pattern: /(?<![A-Za-z0-9_])(?:(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|whsec_[A-Za-z0-9]{20,})(?![A-Za-z0-9])/g,
5124
+ replacement: REDACTED
5125
+ },
5126
+ {
5127
+ kind: "mnemonik_proxy_token",
5128
+ // pxt_ + 32 hex (the format server.ts validates). A bare prefix in prose is left.
5129
+ pattern: /(?<![A-Za-z0-9_])pxt_[0-9a-f]{32,}/g,
5130
+ replacement: REDACTED
5131
+ },
5132
+ {
5133
+ kind: "mnemonik_api_key",
5134
+ // mnk_ + 64 hex (AuthManager.generateApiKey); 32+ also catches a half key.
5135
+ // The stored 12-character key_prefix (mnk_ + 8 hex) is not a credential.
5136
+ pattern: /(?<![A-Za-z0-9_])mnk_[0-9a-f]{32,}/g,
5137
+ replacement: REDACTED
5138
+ },
5139
+ {
5140
+ kind: "mnemonik_api_key_legacy",
5141
+ // mnk_ + 43 base64url: keys minted by hand before AuthManager's hex form
5142
+ // (production's only key in 2026-10 has this shape, and one stored memory
5143
+ // held a development key in it). Authentication accepts any mnk_ bearer.
5144
+ // The body needs upper and lower case and a digit, so a snake_case
5145
+ // identifier that starts mnk_ is not taken for one.
5146
+ pattern: /(?<![A-Za-z0-9_])mnk_(?=[A-Za-z0-9_-]*[A-Z])(?=[A-Za-z0-9_-]*[a-z])(?=[A-Za-z0-9_-]*[0-9])[A-Za-z0-9_-]{40,}(?![A-Za-z0-9_-])/g,
5147
+ replacement: REDACTED
5148
+ },
5149
+ {
5150
+ kind: "named_value",
5151
+ pattern: /(\b[A-Za-z_][A-Za-z0-9_]*(?:_TOKEN|_KEY|_SECRET|_PASSWORD|_PASS|APIKEY|API_KEY)\s*(?:=|:)\s*)([^\s]{16,})/gi,
5152
+ replacement: redactContextValue,
5153
+ secretGroup: 2
5154
+ },
5155
+ {
5156
+ kind: "connection_string",
5157
+ // scheme://user:password@host: only the password is replaced; scheme,
5158
+ // user, host and database stay readable. The user may be empty
5159
+ // (redis://:password@host). The password runs to the last `@` before a
5160
+ // path, query, fragment, quote or whitespace, so an unencoded `@` inside
5161
+ // it is still covered. Runs after named_value, which already replaces a
5162
+ // whole URL assigned to a secret-named variable.
5163
+ pattern: /\b((?:postgres(?:ql)?|mysql|mariadb|mongodb|rediss?|amqps?|mssql|sqlserver)(?:\+[a-z0-9]+)?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
5164
+ replacement: redactUrlPassword,
5165
+ secretGroup: 2
5166
+ },
5167
+ {
5168
+ kind: "url_basic_auth",
5169
+ // https://user:password@host: same shape and placeholder rules as a
5170
+ // connection string; only the password is replaced.
5171
+ pattern: /\b(https?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
5172
+ replacement: redactUrlPassword,
5173
+ secretGroup: 2
5174
+ },
5175
+ // Command-line passwords. Each keeps the command and replaces only the
5176
+ // password word; an expansion or placeholder in that position is left.
5177
+ {
5178
+ // sshpass -p <password> (also -p'<password>' and -p"<password>"), after
5179
+ // any other sshpass flags. 13 memories, 11 scanner chunks, 15 versions and
5180
+ // 6 dated facts in production held one on 2026-10-12.
5181
+ kind: "sshpass_password",
5182
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(${SHELL_WORD})()(?=((?:[ \t]+(?:${SSHPASS_COMMAND})(?![\w-]))?))`, "g"),
5183
+ replacement: redactSshpassWord,
5184
+ secretGroup: 3,
5185
+ commandLine: true
5186
+ },
5187
+ {
5188
+ // Environment variables whose only purpose is a password, in shell
5189
+ // assignment form (no space around `=`): libpq's PGPASSWORD (one memory,
5190
+ // version and fact in production), MySQL's MYSQL_PWD, sshpass -e's
5191
+ // SSHPASS. named_value needs an `_PASSWORD`-style suffix and 16
5192
+ // characters, so it misses all three. Not an awk program that matches or
5193
+ // prints the assignment: `/^PGPASSWORD=/ { print "PGPASSWORD=" pw }`.
5194
+ kind: "password_env_var",
5195
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\b(?:PGPASSWORD|MYSQL_PWD|SSHPASS)=)(${SHELL_WORD})()`, "g"),
5196
+ replacement: redactQuotedShellWord,
5197
+ secretGroup: 3,
5198
+ commandLine: true
5199
+ },
5200
+ {
5201
+ // --password=<value>, the long option every CLI that takes one spells
5202
+ // this way. The `=` keeps prose about the option ("--password is
5203
+ // required") out; the spaced form is not matched for that reason.
5204
+ kind: "password_option",
5205
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}(?<![\w-])--password=)(${SHELL_WORD})()`, "g"),
5206
+ replacement: redactQuotedShellWord,
5207
+ secretGroup: 3,
5208
+ commandLine: true
5209
+ },
5210
+ {
5211
+ // echo <password> | sudo -S: sudo reads the password from stdin. One
5212
+ // memory, two versions and two facts in production.
5213
+ kind: "sudo_password",
5214
+ pattern: new RegExp(String.raw`(\becho[ \t]+(?:-[neE]+[ \t]+)?)(${SHELL_WORD})([ \t]*\|[ \t]*sudo\b[^|\n]*?[ \t](?:-[A-Za-z]*S[A-Za-z]*|--stdin)\b)`, "g"),
5215
+ replacement: redactShellWord,
5216
+ secretGroup: 2,
5217
+ commandLine: true
5218
+ }
5219
+ ];
5220
+
5221
+ // packages/shared/dist/secretPatterns.js
5222
+ var SECRET_LABEL = String.raw`[\w$-]*(?:api[_-]?key|secret|token|password|credential|auth)|aws_secret_access_key`;
5223
+ var SECRET_LABEL_RE = new RegExp(`^(?:${SECRET_LABEL}|MYSQL_PWD|SSHPASS)$`, "i");
5224
+ var LABELED_VALUE_RE = new RegExp(String.raw`(?<![\w$-])((["']?)(?:${SECRET_LABEL})\2\s*[:=]\s*)(?=\S)`, "gi");
5225
+
4964
5226
  // packages/shared/dist/codeScanner.js
4965
5227
  var MAX_SCANNED_FILE_BYTES = 10 * 1024 * 1024;
4966
5228
  var BUILT_IN_IGNORE_DIRS = [
@@ -886,9 +886,13 @@ var require_ignore = __commonJS({
886
886
  });
887
887
 
888
888
  // packages/shared/dist/secretPatterns.js
889
+ var SECRET_LABEL, SECRET_LABEL_RE, LABELED_VALUE_RE;
889
890
  var init_secretPatterns = __esm({
890
891
  "packages/shared/dist/secretPatterns.js"() {
891
892
  "use strict";
893
+ SECRET_LABEL = String.raw`[\w$-]*(?:api[_-]?key|secret|token|password|credential|auth)|aws_secret_access_key`;
894
+ SECRET_LABEL_RE = new RegExp(`^(?:${SECRET_LABEL}|MYSQL_PWD|SSHPASS)$`, "i");
895
+ LABELED_VALUE_RE = new RegExp(String.raw`(?<![\w$-])((["']?)(?:${SECRET_LABEL})\2\s*[:=]\s*)(?=\S)`, "gi");
892
896
  }
893
897
  });
894
898
 
@@ -2858,9 +2862,6 @@ var MCP_INSTRUCTIONS = getMcpInstructions();
2858
2862
 
2859
2863
  // packages/shared/dist/codeScanner.js
2860
2864
  var import_ignore = __toESM(require_ignore(), 1);
2861
- init_secretPatterns();
2862
- init_protectedPaths();
2863
- init_projectIdentityFile();
2864
2865
 
2865
2866
  // node_modules/web-tree-sitter/web-tree-sitter.js
2866
2867
  var __defProp2 = Object.defineProperty;
@@ -6913,7 +6914,269 @@ var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm")
6913
6914
  // packages/shared/dist/ast/astChunker.js
6914
6915
  var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
6915
6916
 
6917
+ // packages/shared/dist/redactCredentials.js
6918
+ var REDACTED = "<redacted credential>";
6919
+ var MAX_PEM_BODY_LENGTH = 8 * 1024;
6920
+ var LETTER_OR_DIGIT = /[A-Za-z0-9]/;
6921
+ function hasLetterOrDigit(value) {
6922
+ return LETTER_OR_DIGIT.test(value);
6923
+ }
6924
+ function redactContextValue(match, head = "", value = "") {
6925
+ return hasLetterOrDigit(value) ? `${head}${REDACTED}` : match;
6926
+ }
6927
+ var PLACEHOLDER_PASSWORD = /^(?:\[REDACTED(?::[A-Za-z0-9_-]+)?\]|redacted|<[^<>]*>|\{\{[^{}]*\}\}|\$\{[^{}]+\}|%[A-Za-z_][A-Za-z0-9_]*%|\*+|x{3,}|\.\.\.|\u2026|password|passwd|pass|pwd|pw|secret|changeme|change[_-]me|example|test|(?:your|my)[a-z_-]*?(?:password|passwd|pass|pwd|secret|key|token|here))$/i;
6928
+ var PLACEHOLDER_ENV_VAR = /^\$[A-Z_][A-Z0-9_]*$/;
6929
+ function isPlaceholderPassword(password) {
6930
+ return PLACEHOLDER_PASSWORD.test(password) || PLACEHOLDER_ENV_VAR.test(password);
6931
+ }
6932
+ function redactUrlPassword(match, head = "", password = "") {
6933
+ return isPlaceholderPassword(password) || !hasLetterOrDigit(password) ? match : `${head}${REDACTED}@`;
6934
+ }
6935
+ var SHELL_WORD = String.raw`'[^'\n]*'|"[^"\n]*"|(?!${REDACTED})[^\s'"\`;|&()[\]{}\\,/][^\s'"\`;|&]*`;
6936
+ var OPENING_QUOTE = String.raw`(["'\`]?)`;
6937
+ var TRAILING_PUNCTUATION = /[.,:;!?)\]}]+$/;
6938
+ var PROSE_WORDS = /* @__PURE__ */ new Set([
6939
+ "a",
6940
+ "an",
6941
+ "and",
6942
+ "as",
6943
+ "for",
6944
+ "in",
6945
+ "is",
6946
+ "it",
6947
+ "on",
6948
+ "or",
6949
+ "the",
6950
+ "to",
6951
+ "with",
6952
+ "flag",
6953
+ "option",
6954
+ "argument",
6955
+ "instead",
6956
+ "which",
6957
+ "that",
6958
+ "this",
6959
+ "puts",
6960
+ "passes",
6961
+ "leaks",
6962
+ "exposes"
6963
+ ]);
6964
+ var REGEX_DELIMITER = /\/(?:[{}()[\];]|$)/;
6965
+ function shellWordSecret(word) {
6966
+ const quote = word[0];
6967
+ if ((quote === "'" || quote === '"') && word.length >= 2 && word.endsWith(quote)) {
6968
+ const inner = word.slice(1, -1);
6969
+ if (!hasLetterOrDigit(inner) || quote === '"' && inner.startsWith("$"))
6970
+ return null;
6971
+ if (inner !== inner.trim())
6972
+ return null;
6973
+ return isPlaceholderPassword(inner) ? null : inner;
6974
+ }
6975
+ if (word.startsWith("$"))
6976
+ return null;
6977
+ const core = word.replace(TRAILING_PUNCTUATION, "");
6978
+ if (!hasLetterOrDigit(core) || isPlaceholderPassword(core))
6979
+ return null;
6980
+ if (REGEX_DELIMITER.test(core))
6981
+ return null;
6982
+ return PROSE_WORDS.has(core.toLowerCase()) ? null : core;
6983
+ }
6984
+ function redactShellWord(match, head = "", word = "", tail = "") {
6985
+ return shellWordSecret(word) === null ? match : `${head}${REDACTED}${tail}`;
6986
+ }
6987
+ function redactQuotedShellWord(match, head = "", openingQuote = "", word = "", tail = "") {
6988
+ if (openingQuote !== "" && word.startsWith(openingQuote))
6989
+ return match;
6990
+ return redactShellWord(match, head, word, tail);
6991
+ }
6992
+ var SSHPASS_COMMAND = String.raw`ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*`;
6993
+ function redactSshpassWord(match, head = "", openingQuote = "", word = "", tail = "", command = "") {
6994
+ if (openingQuote !== "" && word.startsWith(openingQuote))
6995
+ return match;
6996
+ const secret = shellWordSecret(word);
6997
+ if (secret === null)
6998
+ return match;
6999
+ const bare = word[0] !== "'" && word[0] !== '"';
7000
+ if (bare && /^[a-z]+$/.test(secret) && command === "")
7001
+ return match;
7002
+ return `${head}${REDACTED}${tail}`;
7003
+ }
7004
+ var PATTERNS = [
7005
+ {
7006
+ kind: "bearer",
7007
+ pattern: /(\bBearer\s+)([^\s]{40,})/gi,
7008
+ replacement: redactContextValue,
7009
+ secretGroup: 2
7010
+ },
7011
+ {
7012
+ // Before openai, whose sk- shape would otherwise claim these keys.
7013
+ kind: "anthropic",
7014
+ pattern: /(?<![A-Za-z0-9_-])sk-ant-(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
7015
+ replacement: REDACTED
7016
+ },
7017
+ {
7018
+ kind: "openai",
7019
+ pattern: /(?<![A-Za-z0-9_-])sk-(?:proj-)?(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
7020
+ replacement: REDACTED
7021
+ },
7022
+ {
7023
+ kind: "shopify",
7024
+ pattern: /(?<![A-Za-z0-9_])shp(?:at|ss|ca)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/g,
7025
+ replacement: REDACTED
7026
+ },
7027
+ {
7028
+ kind: "aws_access_key",
7029
+ pattern: /(?<![A-Z0-9])AKIA[A-Z0-9]{16}(?![A-Z0-9])/g,
7030
+ replacement: REDACTED
7031
+ },
7032
+ {
7033
+ kind: "github",
7034
+ pattern: /(?<![A-Za-z0-9_])gh[opu]_[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
7035
+ replacement: REDACTED
7036
+ },
7037
+ {
7038
+ kind: "slack",
7039
+ pattern: /(?<![A-Za-z0-9-])xox[abp]-(?=-*[A-Za-z0-9])[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
7040
+ replacement: REDACTED
7041
+ },
7042
+ {
7043
+ kind: "sentry",
7044
+ pattern: /(?<![A-Za-z0-9_])sntry[us]_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
7045
+ replacement: REDACTED
7046
+ },
7047
+ {
7048
+ kind: "fly",
7049
+ pattern: /(?<![A-Za-z0-9_])(?:FlyV1\s+)?fm2_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
7050
+ replacement: REDACTED
7051
+ },
7052
+ {
7053
+ kind: "google_api_key",
7054
+ pattern: /(?<![A-Za-z0-9_-])AIza(?=[_-]*[A-Za-z0-9])[0-9A-Za-z_-]{35}(?![A-Za-z0-9_-])/g,
7055
+ replacement: REDACTED
7056
+ },
7057
+ {
7058
+ kind: "jwt",
7059
+ // header.payload.signature, both JSON segments starting eyJ. The length
7060
+ // floors keep `eyJ...` prose and signature-less examples out.
7061
+ pattern: /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
7062
+ replacement: REDACTED
7063
+ },
7064
+ {
7065
+ kind: "mnemonik_component_token",
7066
+ // mnc_ / mncr_ + 43 base64url (oauth/components.ts); 32+ also catches a
7067
+ // partial token. The body must contain an upper-case letter or digit, so a
7068
+ // snake_case identifier is not mistaken for one; the 8-character
7069
+ // display prefix (mnc_ + 8) is too short.
7070
+ pattern: /(?<![A-Za-z0-9_])mncr?_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g,
7071
+ replacement: REDACTED
7072
+ },
7073
+ {
7074
+ kind: "stripe",
7075
+ // Secret and restricted keys (sk_/rk_ live|test) and webhook signing secrets.
7076
+ pattern: /(?<![A-Za-z0-9_])(?:(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|whsec_[A-Za-z0-9]{20,})(?![A-Za-z0-9])/g,
7077
+ replacement: REDACTED
7078
+ },
7079
+ {
7080
+ kind: "mnemonik_proxy_token",
7081
+ // pxt_ + 32 hex (the format server.ts validates). A bare prefix in prose is left.
7082
+ pattern: /(?<![A-Za-z0-9_])pxt_[0-9a-f]{32,}/g,
7083
+ replacement: REDACTED
7084
+ },
7085
+ {
7086
+ kind: "mnemonik_api_key",
7087
+ // mnk_ + 64 hex (AuthManager.generateApiKey); 32+ also catches a half key.
7088
+ // The stored 12-character key_prefix (mnk_ + 8 hex) is not a credential.
7089
+ pattern: /(?<![A-Za-z0-9_])mnk_[0-9a-f]{32,}/g,
7090
+ replacement: REDACTED
7091
+ },
7092
+ {
7093
+ kind: "mnemonik_api_key_legacy",
7094
+ // mnk_ + 43 base64url: keys minted by hand before AuthManager's hex form
7095
+ // (production's only key in 2026-10 has this shape, and one stored memory
7096
+ // held a development key in it). Authentication accepts any mnk_ bearer.
7097
+ // The body needs upper and lower case and a digit, so a snake_case
7098
+ // identifier that starts mnk_ is not taken for one.
7099
+ pattern: /(?<![A-Za-z0-9_])mnk_(?=[A-Za-z0-9_-]*[A-Z])(?=[A-Za-z0-9_-]*[a-z])(?=[A-Za-z0-9_-]*[0-9])[A-Za-z0-9_-]{40,}(?![A-Za-z0-9_-])/g,
7100
+ replacement: REDACTED
7101
+ },
7102
+ {
7103
+ kind: "named_value",
7104
+ pattern: /(\b[A-Za-z_][A-Za-z0-9_]*(?:_TOKEN|_KEY|_SECRET|_PASSWORD|_PASS|APIKEY|API_KEY)\s*(?:=|:)\s*)([^\s]{16,})/gi,
7105
+ replacement: redactContextValue,
7106
+ secretGroup: 2
7107
+ },
7108
+ {
7109
+ kind: "connection_string",
7110
+ // scheme://user:password@host: only the password is replaced; scheme,
7111
+ // user, host and database stay readable. The user may be empty
7112
+ // (redis://:password@host). The password runs to the last `@` before a
7113
+ // path, query, fragment, quote or whitespace, so an unencoded `@` inside
7114
+ // it is still covered. Runs after named_value, which already replaces a
7115
+ // whole URL assigned to a secret-named variable.
7116
+ pattern: /\b((?:postgres(?:ql)?|mysql|mariadb|mongodb|rediss?|amqps?|mssql|sqlserver)(?:\+[a-z0-9]+)?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
7117
+ replacement: redactUrlPassword,
7118
+ secretGroup: 2
7119
+ },
7120
+ {
7121
+ kind: "url_basic_auth",
7122
+ // https://user:password@host: same shape and placeholder rules as a
7123
+ // connection string; only the password is replaced.
7124
+ pattern: /\b(https?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
7125
+ replacement: redactUrlPassword,
7126
+ secretGroup: 2
7127
+ },
7128
+ // Command-line passwords. Each keeps the command and replaces only the
7129
+ // password word; an expansion or placeholder in that position is left.
7130
+ {
7131
+ // sshpass -p <password> (also -p'<password>' and -p"<password>"), after
7132
+ // any other sshpass flags. 13 memories, 11 scanner chunks, 15 versions and
7133
+ // 6 dated facts in production held one on 2026-10-12.
7134
+ kind: "sshpass_password",
7135
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(${SHELL_WORD})()(?=((?:[ \t]+(?:${SSHPASS_COMMAND})(?![\w-]))?))`, "g"),
7136
+ replacement: redactSshpassWord,
7137
+ secretGroup: 3,
7138
+ commandLine: true
7139
+ },
7140
+ {
7141
+ // Environment variables whose only purpose is a password, in shell
7142
+ // assignment form (no space around `=`): libpq's PGPASSWORD (one memory,
7143
+ // version and fact in production), MySQL's MYSQL_PWD, sshpass -e's
7144
+ // SSHPASS. named_value needs an `_PASSWORD`-style suffix and 16
7145
+ // characters, so it misses all three. Not an awk program that matches or
7146
+ // prints the assignment: `/^PGPASSWORD=/ { print "PGPASSWORD=" pw }`.
7147
+ kind: "password_env_var",
7148
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\b(?:PGPASSWORD|MYSQL_PWD|SSHPASS)=)(${SHELL_WORD})()`, "g"),
7149
+ replacement: redactQuotedShellWord,
7150
+ secretGroup: 3,
7151
+ commandLine: true
7152
+ },
7153
+ {
7154
+ // --password=<value>, the long option every CLI that takes one spells
7155
+ // this way. The `=` keeps prose about the option ("--password is
7156
+ // required") out; the spaced form is not matched for that reason.
7157
+ kind: "password_option",
7158
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}(?<![\w-])--password=)(${SHELL_WORD})()`, "g"),
7159
+ replacement: redactQuotedShellWord,
7160
+ secretGroup: 3,
7161
+ commandLine: true
7162
+ },
7163
+ {
7164
+ // echo <password> | sudo -S: sudo reads the password from stdin. One
7165
+ // memory, two versions and two facts in production.
7166
+ kind: "sudo_password",
7167
+ pattern: new RegExp(String.raw`(\becho[ \t]+(?:-[neE]+[ \t]+)?)(${SHELL_WORD})([ \t]*\|[ \t]*sudo\b[^|\n]*?[ \t](?:-[A-Za-z]*S[A-Za-z]*|--stdin)\b)`, "g"),
7168
+ replacement: redactShellWord,
7169
+ secretGroup: 2,
7170
+ commandLine: true
7171
+ }
7172
+ ];
7173
+
7174
+ // packages/shared/dist/sourceRedaction.js
7175
+ init_secretPatterns();
7176
+
6916
7177
  // packages/shared/dist/codeScanner.js
7178
+ init_protectedPaths();
7179
+ init_projectIdentityFile();
6917
7180
  var MAX_SCANNED_FILE_BYTES = 10 * 1024 * 1024;
6918
7181
  var BUILT_IN_IGNORE_DIRS = [
6919
7182
  // JS / web