@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
@@ -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
 
@@ -2852,9 +2856,6 @@ var MCP_INSTRUCTIONS = getMcpInstructions();
2852
2856
 
2853
2857
  // packages/shared/dist/codeScanner.js
2854
2858
  var import_ignore = __toESM(require_ignore(), 1);
2855
- init_secretPatterns();
2856
- init_protectedPaths();
2857
- init_projectIdentityFile();
2858
2859
 
2859
2860
  // node_modules/web-tree-sitter/web-tree-sitter.js
2860
2861
  var __defProp2 = Object.defineProperty;
@@ -6907,7 +6908,269 @@ var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm")
6907
6908
  // packages/shared/dist/ast/astChunker.js
6908
6909
  var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
6909
6910
 
6911
+ // packages/shared/dist/redactCredentials.js
6912
+ var REDACTED = "<redacted credential>";
6913
+ var MAX_PEM_BODY_LENGTH = 8 * 1024;
6914
+ var LETTER_OR_DIGIT = /[A-Za-z0-9]/;
6915
+ function hasLetterOrDigit(value) {
6916
+ return LETTER_OR_DIGIT.test(value);
6917
+ }
6918
+ function redactContextValue(match, head = "", value = "") {
6919
+ return hasLetterOrDigit(value) ? `${head}${REDACTED}` : match;
6920
+ }
6921
+ 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;
6922
+ var PLACEHOLDER_ENV_VAR = /^\$[A-Z_][A-Z0-9_]*$/;
6923
+ function isPlaceholderPassword(password) {
6924
+ return PLACEHOLDER_PASSWORD.test(password) || PLACEHOLDER_ENV_VAR.test(password);
6925
+ }
6926
+ function redactUrlPassword(match, head = "", password = "") {
6927
+ return isPlaceholderPassword(password) || !hasLetterOrDigit(password) ? match : `${head}${REDACTED}@`;
6928
+ }
6929
+ var SHELL_WORD = String.raw`'[^'\n]*'|"[^"\n]*"|(?!${REDACTED})[^\s'"\`;|&()[\]{}\\,/][^\s'"\`;|&]*`;
6930
+ var OPENING_QUOTE = String.raw`(["'\`]?)`;
6931
+ var TRAILING_PUNCTUATION = /[.,:;!?)\]}]+$/;
6932
+ var PROSE_WORDS = /* @__PURE__ */ new Set([
6933
+ "a",
6934
+ "an",
6935
+ "and",
6936
+ "as",
6937
+ "for",
6938
+ "in",
6939
+ "is",
6940
+ "it",
6941
+ "on",
6942
+ "or",
6943
+ "the",
6944
+ "to",
6945
+ "with",
6946
+ "flag",
6947
+ "option",
6948
+ "argument",
6949
+ "instead",
6950
+ "which",
6951
+ "that",
6952
+ "this",
6953
+ "puts",
6954
+ "passes",
6955
+ "leaks",
6956
+ "exposes"
6957
+ ]);
6958
+ var REGEX_DELIMITER = /\/(?:[{}()[\];]|$)/;
6959
+ function shellWordSecret(word) {
6960
+ const quote = word[0];
6961
+ if ((quote === "'" || quote === '"') && word.length >= 2 && word.endsWith(quote)) {
6962
+ const inner = word.slice(1, -1);
6963
+ if (!hasLetterOrDigit(inner) || quote === '"' && inner.startsWith("$"))
6964
+ return null;
6965
+ if (inner !== inner.trim())
6966
+ return null;
6967
+ return isPlaceholderPassword(inner) ? null : inner;
6968
+ }
6969
+ if (word.startsWith("$"))
6970
+ return null;
6971
+ const core = word.replace(TRAILING_PUNCTUATION, "");
6972
+ if (!hasLetterOrDigit(core) || isPlaceholderPassword(core))
6973
+ return null;
6974
+ if (REGEX_DELIMITER.test(core))
6975
+ return null;
6976
+ return PROSE_WORDS.has(core.toLowerCase()) ? null : core;
6977
+ }
6978
+ function redactShellWord(match, head = "", word = "", tail = "") {
6979
+ return shellWordSecret(word) === null ? match : `${head}${REDACTED}${tail}`;
6980
+ }
6981
+ function redactQuotedShellWord(match, head = "", openingQuote = "", word = "", tail = "") {
6982
+ if (openingQuote !== "" && word.startsWith(openingQuote))
6983
+ return match;
6984
+ return redactShellWord(match, head, word, tail);
6985
+ }
6986
+ var SSHPASS_COMMAND = String.raw`ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*`;
6987
+ function redactSshpassWord(match, head = "", openingQuote = "", word = "", tail = "", command = "") {
6988
+ if (openingQuote !== "" && word.startsWith(openingQuote))
6989
+ return match;
6990
+ const secret = shellWordSecret(word);
6991
+ if (secret === null)
6992
+ return match;
6993
+ const bare = word[0] !== "'" && word[0] !== '"';
6994
+ if (bare && /^[a-z]+$/.test(secret) && command === "")
6995
+ return match;
6996
+ return `${head}${REDACTED}${tail}`;
6997
+ }
6998
+ var PATTERNS = [
6999
+ {
7000
+ kind: "bearer",
7001
+ pattern: /(\bBearer\s+)([^\s]{40,})/gi,
7002
+ replacement: redactContextValue,
7003
+ secretGroup: 2
7004
+ },
7005
+ {
7006
+ // Before openai, whose sk- shape would otherwise claim these keys.
7007
+ kind: "anthropic",
7008
+ pattern: /(?<![A-Za-z0-9_-])sk-ant-(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
7009
+ replacement: REDACTED
7010
+ },
7011
+ {
7012
+ kind: "openai",
7013
+ pattern: /(?<![A-Za-z0-9_-])sk-(?:proj-)?(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
7014
+ replacement: REDACTED
7015
+ },
7016
+ {
7017
+ kind: "shopify",
7018
+ pattern: /(?<![A-Za-z0-9_])shp(?:at|ss|ca)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/g,
7019
+ replacement: REDACTED
7020
+ },
7021
+ {
7022
+ kind: "aws_access_key",
7023
+ pattern: /(?<![A-Z0-9])AKIA[A-Z0-9]{16}(?![A-Z0-9])/g,
7024
+ replacement: REDACTED
7025
+ },
7026
+ {
7027
+ kind: "github",
7028
+ pattern: /(?<![A-Za-z0-9_])gh[opu]_[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
7029
+ replacement: REDACTED
7030
+ },
7031
+ {
7032
+ kind: "slack",
7033
+ pattern: /(?<![A-Za-z0-9-])xox[abp]-(?=-*[A-Za-z0-9])[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
7034
+ replacement: REDACTED
7035
+ },
7036
+ {
7037
+ kind: "sentry",
7038
+ pattern: /(?<![A-Za-z0-9_])sntry[us]_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
7039
+ replacement: REDACTED
7040
+ },
7041
+ {
7042
+ kind: "fly",
7043
+ pattern: /(?<![A-Za-z0-9_])(?:FlyV1\s+)?fm2_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
7044
+ replacement: REDACTED
7045
+ },
7046
+ {
7047
+ kind: "google_api_key",
7048
+ pattern: /(?<![A-Za-z0-9_-])AIza(?=[_-]*[A-Za-z0-9])[0-9A-Za-z_-]{35}(?![A-Za-z0-9_-])/g,
7049
+ replacement: REDACTED
7050
+ },
7051
+ {
7052
+ kind: "jwt",
7053
+ // header.payload.signature, both JSON segments starting eyJ. The length
7054
+ // floors keep `eyJ...` prose and signature-less examples out.
7055
+ 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,
7056
+ replacement: REDACTED
7057
+ },
7058
+ {
7059
+ kind: "mnemonik_component_token",
7060
+ // mnc_ / mncr_ + 43 base64url (oauth/components.ts); 32+ also catches a
7061
+ // partial token. The body must contain an upper-case letter or digit, so a
7062
+ // snake_case identifier is not mistaken for one; the 8-character
7063
+ // display prefix (mnc_ + 8) is too short.
7064
+ pattern: /(?<![A-Za-z0-9_])mncr?_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g,
7065
+ replacement: REDACTED
7066
+ },
7067
+ {
7068
+ kind: "stripe",
7069
+ // Secret and restricted keys (sk_/rk_ live|test) and webhook signing secrets.
7070
+ 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,
7071
+ replacement: REDACTED
7072
+ },
7073
+ {
7074
+ kind: "mnemonik_proxy_token",
7075
+ // pxt_ + 32 hex (the format server.ts validates). A bare prefix in prose is left.
7076
+ pattern: /(?<![A-Za-z0-9_])pxt_[0-9a-f]{32,}/g,
7077
+ replacement: REDACTED
7078
+ },
7079
+ {
7080
+ kind: "mnemonik_api_key",
7081
+ // mnk_ + 64 hex (AuthManager.generateApiKey); 32+ also catches a half key.
7082
+ // The stored 12-character key_prefix (mnk_ + 8 hex) is not a credential.
7083
+ pattern: /(?<![A-Za-z0-9_])mnk_[0-9a-f]{32,}/g,
7084
+ replacement: REDACTED
7085
+ },
7086
+ {
7087
+ kind: "mnemonik_api_key_legacy",
7088
+ // mnk_ + 43 base64url: keys minted by hand before AuthManager's hex form
7089
+ // (production's only key in 2026-10 has this shape, and one stored memory
7090
+ // held a development key in it). Authentication accepts any mnk_ bearer.
7091
+ // The body needs upper and lower case and a digit, so a snake_case
7092
+ // identifier that starts mnk_ is not taken for one.
7093
+ 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,
7094
+ replacement: REDACTED
7095
+ },
7096
+ {
7097
+ kind: "named_value",
7098
+ pattern: /(\b[A-Za-z_][A-Za-z0-9_]*(?:_TOKEN|_KEY|_SECRET|_PASSWORD|_PASS|APIKEY|API_KEY)\s*(?:=|:)\s*)([^\s]{16,})/gi,
7099
+ replacement: redactContextValue,
7100
+ secretGroup: 2
7101
+ },
7102
+ {
7103
+ kind: "connection_string",
7104
+ // scheme://user:password@host: only the password is replaced; scheme,
7105
+ // user, host and database stay readable. The user may be empty
7106
+ // (redis://:password@host). The password runs to the last `@` before a
7107
+ // path, query, fragment, quote or whitespace, so an unencoded `@` inside
7108
+ // it is still covered. Runs after named_value, which already replaces a
7109
+ // whole URL assigned to a secret-named variable.
7110
+ pattern: /\b((?:postgres(?:ql)?|mysql|mariadb|mongodb|rediss?|amqps?|mssql|sqlserver)(?:\+[a-z0-9]+)?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
7111
+ replacement: redactUrlPassword,
7112
+ secretGroup: 2
7113
+ },
7114
+ {
7115
+ kind: "url_basic_auth",
7116
+ // https://user:password@host: same shape and placeholder rules as a
7117
+ // connection string; only the password is replaced.
7118
+ pattern: /\b(https?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
7119
+ replacement: redactUrlPassword,
7120
+ secretGroup: 2
7121
+ },
7122
+ // Command-line passwords. Each keeps the command and replaces only the
7123
+ // password word; an expansion or placeholder in that position is left.
7124
+ {
7125
+ // sshpass -p <password> (also -p'<password>' and -p"<password>"), after
7126
+ // any other sshpass flags. 13 memories, 11 scanner chunks, 15 versions and
7127
+ // 6 dated facts in production held one on 2026-10-12.
7128
+ kind: "sshpass_password",
7129
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(${SHELL_WORD})()(?=((?:[ \t]+(?:${SSHPASS_COMMAND})(?![\w-]))?))`, "g"),
7130
+ replacement: redactSshpassWord,
7131
+ secretGroup: 3,
7132
+ commandLine: true
7133
+ },
7134
+ {
7135
+ // Environment variables whose only purpose is a password, in shell
7136
+ // assignment form (no space around `=`): libpq's PGPASSWORD (one memory,
7137
+ // version and fact in production), MySQL's MYSQL_PWD, sshpass -e's
7138
+ // SSHPASS. named_value needs an `_PASSWORD`-style suffix and 16
7139
+ // characters, so it misses all three. Not an awk program that matches or
7140
+ // prints the assignment: `/^PGPASSWORD=/ { print "PGPASSWORD=" pw }`.
7141
+ kind: "password_env_var",
7142
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\b(?:PGPASSWORD|MYSQL_PWD|SSHPASS)=)(${SHELL_WORD})()`, "g"),
7143
+ replacement: redactQuotedShellWord,
7144
+ secretGroup: 3,
7145
+ commandLine: true
7146
+ },
7147
+ {
7148
+ // --password=<value>, the long option every CLI that takes one spells
7149
+ // this way. The `=` keeps prose about the option ("--password is
7150
+ // required") out; the spaced form is not matched for that reason.
7151
+ kind: "password_option",
7152
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}(?<![\w-])--password=)(${SHELL_WORD})()`, "g"),
7153
+ replacement: redactQuotedShellWord,
7154
+ secretGroup: 3,
7155
+ commandLine: true
7156
+ },
7157
+ {
7158
+ // echo <password> | sudo -S: sudo reads the password from stdin. One
7159
+ // memory, two versions and two facts in production.
7160
+ kind: "sudo_password",
7161
+ 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"),
7162
+ replacement: redactShellWord,
7163
+ secretGroup: 2,
7164
+ commandLine: true
7165
+ }
7166
+ ];
7167
+
7168
+ // packages/shared/dist/sourceRedaction.js
7169
+ init_secretPatterns();
7170
+
6910
7171
  // packages/shared/dist/codeScanner.js
7172
+ init_protectedPaths();
7173
+ init_projectIdentityFile();
6911
7174
  var MAX_SCANNED_FILE_BYTES = 10 * 1024 * 1024;
6912
7175
  var BUILT_IN_IGNORE_DIRS = [
6913
7176
  // JS / web
@@ -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
 
@@ -2718,9 +2722,6 @@ var MCP_INSTRUCTIONS = getMcpInstructions();
2718
2722
 
2719
2723
  // packages/shared/dist/codeScanner.js
2720
2724
  var import_ignore = __toESM(require_ignore(), 1);
2721
- init_secretPatterns();
2722
- init_protectedPaths();
2723
- init_projectIdentityFile();
2724
2725
 
2725
2726
  // node_modules/web-tree-sitter/web-tree-sitter.js
2726
2727
  var __defProp2 = Object.defineProperty;
@@ -6773,7 +6774,269 @@ var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm")
6773
6774
  // packages/shared/dist/ast/astChunker.js
6774
6775
  var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
6775
6776
 
6777
+ // packages/shared/dist/redactCredentials.js
6778
+ var REDACTED = "<redacted credential>";
6779
+ var MAX_PEM_BODY_LENGTH = 8 * 1024;
6780
+ var LETTER_OR_DIGIT = /[A-Za-z0-9]/;
6781
+ function hasLetterOrDigit(value) {
6782
+ return LETTER_OR_DIGIT.test(value);
6783
+ }
6784
+ function redactContextValue(match, head = "", value = "") {
6785
+ return hasLetterOrDigit(value) ? `${head}${REDACTED}` : match;
6786
+ }
6787
+ 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;
6788
+ var PLACEHOLDER_ENV_VAR = /^\$[A-Z_][A-Z0-9_]*$/;
6789
+ function isPlaceholderPassword(password) {
6790
+ return PLACEHOLDER_PASSWORD.test(password) || PLACEHOLDER_ENV_VAR.test(password);
6791
+ }
6792
+ function redactUrlPassword(match, head = "", password = "") {
6793
+ return isPlaceholderPassword(password) || !hasLetterOrDigit(password) ? match : `${head}${REDACTED}@`;
6794
+ }
6795
+ var SHELL_WORD = String.raw`'[^'\n]*'|"[^"\n]*"|(?!${REDACTED})[^\s'"\`;|&()[\]{}\\,/][^\s'"\`;|&]*`;
6796
+ var OPENING_QUOTE = String.raw`(["'\`]?)`;
6797
+ var TRAILING_PUNCTUATION = /[.,:;!?)\]}]+$/;
6798
+ var PROSE_WORDS = /* @__PURE__ */ new Set([
6799
+ "a",
6800
+ "an",
6801
+ "and",
6802
+ "as",
6803
+ "for",
6804
+ "in",
6805
+ "is",
6806
+ "it",
6807
+ "on",
6808
+ "or",
6809
+ "the",
6810
+ "to",
6811
+ "with",
6812
+ "flag",
6813
+ "option",
6814
+ "argument",
6815
+ "instead",
6816
+ "which",
6817
+ "that",
6818
+ "this",
6819
+ "puts",
6820
+ "passes",
6821
+ "leaks",
6822
+ "exposes"
6823
+ ]);
6824
+ var REGEX_DELIMITER = /\/(?:[{}()[\];]|$)/;
6825
+ function shellWordSecret(word) {
6826
+ const quote = word[0];
6827
+ if ((quote === "'" || quote === '"') && word.length >= 2 && word.endsWith(quote)) {
6828
+ const inner = word.slice(1, -1);
6829
+ if (!hasLetterOrDigit(inner) || quote === '"' && inner.startsWith("$"))
6830
+ return null;
6831
+ if (inner !== inner.trim())
6832
+ return null;
6833
+ return isPlaceholderPassword(inner) ? null : inner;
6834
+ }
6835
+ if (word.startsWith("$"))
6836
+ return null;
6837
+ const core = word.replace(TRAILING_PUNCTUATION, "");
6838
+ if (!hasLetterOrDigit(core) || isPlaceholderPassword(core))
6839
+ return null;
6840
+ if (REGEX_DELIMITER.test(core))
6841
+ return null;
6842
+ return PROSE_WORDS.has(core.toLowerCase()) ? null : core;
6843
+ }
6844
+ function redactShellWord(match, head = "", word = "", tail = "") {
6845
+ return shellWordSecret(word) === null ? match : `${head}${REDACTED}${tail}`;
6846
+ }
6847
+ function redactQuotedShellWord(match, head = "", openingQuote = "", word = "", tail = "") {
6848
+ if (openingQuote !== "" && word.startsWith(openingQuote))
6849
+ return match;
6850
+ return redactShellWord(match, head, word, tail);
6851
+ }
6852
+ var SSHPASS_COMMAND = String.raw`ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*`;
6853
+ function redactSshpassWord(match, head = "", openingQuote = "", word = "", tail = "", command = "") {
6854
+ if (openingQuote !== "" && word.startsWith(openingQuote))
6855
+ return match;
6856
+ const secret = shellWordSecret(word);
6857
+ if (secret === null)
6858
+ return match;
6859
+ const bare = word[0] !== "'" && word[0] !== '"';
6860
+ if (bare && /^[a-z]+$/.test(secret) && command === "")
6861
+ return match;
6862
+ return `${head}${REDACTED}${tail}`;
6863
+ }
6864
+ var PATTERNS = [
6865
+ {
6866
+ kind: "bearer",
6867
+ pattern: /(\bBearer\s+)([^\s]{40,})/gi,
6868
+ replacement: redactContextValue,
6869
+ secretGroup: 2
6870
+ },
6871
+ {
6872
+ // Before openai, whose sk- shape would otherwise claim these keys.
6873
+ kind: "anthropic",
6874
+ pattern: /(?<![A-Za-z0-9_-])sk-ant-(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
6875
+ replacement: REDACTED
6876
+ },
6877
+ {
6878
+ kind: "openai",
6879
+ pattern: /(?<![A-Za-z0-9_-])sk-(?:proj-)?(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
6880
+ replacement: REDACTED
6881
+ },
6882
+ {
6883
+ kind: "shopify",
6884
+ pattern: /(?<![A-Za-z0-9_])shp(?:at|ss|ca)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/g,
6885
+ replacement: REDACTED
6886
+ },
6887
+ {
6888
+ kind: "aws_access_key",
6889
+ pattern: /(?<![A-Z0-9])AKIA[A-Z0-9]{16}(?![A-Z0-9])/g,
6890
+ replacement: REDACTED
6891
+ },
6892
+ {
6893
+ kind: "github",
6894
+ pattern: /(?<![A-Za-z0-9_])gh[opu]_[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
6895
+ replacement: REDACTED
6896
+ },
6897
+ {
6898
+ kind: "slack",
6899
+ pattern: /(?<![A-Za-z0-9-])xox[abp]-(?=-*[A-Za-z0-9])[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
6900
+ replacement: REDACTED
6901
+ },
6902
+ {
6903
+ kind: "sentry",
6904
+ pattern: /(?<![A-Za-z0-9_])sntry[us]_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
6905
+ replacement: REDACTED
6906
+ },
6907
+ {
6908
+ kind: "fly",
6909
+ pattern: /(?<![A-Za-z0-9_])(?:FlyV1\s+)?fm2_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
6910
+ replacement: REDACTED
6911
+ },
6912
+ {
6913
+ kind: "google_api_key",
6914
+ pattern: /(?<![A-Za-z0-9_-])AIza(?=[_-]*[A-Za-z0-9])[0-9A-Za-z_-]{35}(?![A-Za-z0-9_-])/g,
6915
+ replacement: REDACTED
6916
+ },
6917
+ {
6918
+ kind: "jwt",
6919
+ // header.payload.signature, both JSON segments starting eyJ. The length
6920
+ // floors keep `eyJ...` prose and signature-less examples out.
6921
+ 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,
6922
+ replacement: REDACTED
6923
+ },
6924
+ {
6925
+ kind: "mnemonik_component_token",
6926
+ // mnc_ / mncr_ + 43 base64url (oauth/components.ts); 32+ also catches a
6927
+ // partial token. The body must contain an upper-case letter or digit, so a
6928
+ // snake_case identifier is not mistaken for one; the 8-character
6929
+ // display prefix (mnc_ + 8) is too short.
6930
+ pattern: /(?<![A-Za-z0-9_])mncr?_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g,
6931
+ replacement: REDACTED
6932
+ },
6933
+ {
6934
+ kind: "stripe",
6935
+ // Secret and restricted keys (sk_/rk_ live|test) and webhook signing secrets.
6936
+ 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,
6937
+ replacement: REDACTED
6938
+ },
6939
+ {
6940
+ kind: "mnemonik_proxy_token",
6941
+ // pxt_ + 32 hex (the format server.ts validates). A bare prefix in prose is left.
6942
+ pattern: /(?<![A-Za-z0-9_])pxt_[0-9a-f]{32,}/g,
6943
+ replacement: REDACTED
6944
+ },
6945
+ {
6946
+ kind: "mnemonik_api_key",
6947
+ // mnk_ + 64 hex (AuthManager.generateApiKey); 32+ also catches a half key.
6948
+ // The stored 12-character key_prefix (mnk_ + 8 hex) is not a credential.
6949
+ pattern: /(?<![A-Za-z0-9_])mnk_[0-9a-f]{32,}/g,
6950
+ replacement: REDACTED
6951
+ },
6952
+ {
6953
+ kind: "mnemonik_api_key_legacy",
6954
+ // mnk_ + 43 base64url: keys minted by hand before AuthManager's hex form
6955
+ // (production's only key in 2026-10 has this shape, and one stored memory
6956
+ // held a development key in it). Authentication accepts any mnk_ bearer.
6957
+ // The body needs upper and lower case and a digit, so a snake_case
6958
+ // identifier that starts mnk_ is not taken for one.
6959
+ 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,
6960
+ replacement: REDACTED
6961
+ },
6962
+ {
6963
+ kind: "named_value",
6964
+ pattern: /(\b[A-Za-z_][A-Za-z0-9_]*(?:_TOKEN|_KEY|_SECRET|_PASSWORD|_PASS|APIKEY|API_KEY)\s*(?:=|:)\s*)([^\s]{16,})/gi,
6965
+ replacement: redactContextValue,
6966
+ secretGroup: 2
6967
+ },
6968
+ {
6969
+ kind: "connection_string",
6970
+ // scheme://user:password@host: only the password is replaced; scheme,
6971
+ // user, host and database stay readable. The user may be empty
6972
+ // (redis://:password@host). The password runs to the last `@` before a
6973
+ // path, query, fragment, quote or whitespace, so an unencoded `@` inside
6974
+ // it is still covered. Runs after named_value, which already replaces a
6975
+ // whole URL assigned to a secret-named variable.
6976
+ pattern: /\b((?:postgres(?:ql)?|mysql|mariadb|mongodb|rediss?|amqps?|mssql|sqlserver)(?:\+[a-z0-9]+)?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
6977
+ replacement: redactUrlPassword,
6978
+ secretGroup: 2
6979
+ },
6980
+ {
6981
+ kind: "url_basic_auth",
6982
+ // https://user:password@host: same shape and placeholder rules as a
6983
+ // connection string; only the password is replaced.
6984
+ pattern: /\b(https?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
6985
+ replacement: redactUrlPassword,
6986
+ secretGroup: 2
6987
+ },
6988
+ // Command-line passwords. Each keeps the command and replaces only the
6989
+ // password word; an expansion or placeholder in that position is left.
6990
+ {
6991
+ // sshpass -p <password> (also -p'<password>' and -p"<password>"), after
6992
+ // any other sshpass flags. 13 memories, 11 scanner chunks, 15 versions and
6993
+ // 6 dated facts in production held one on 2026-10-12.
6994
+ kind: "sshpass_password",
6995
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(${SHELL_WORD})()(?=((?:[ \t]+(?:${SSHPASS_COMMAND})(?![\w-]))?))`, "g"),
6996
+ replacement: redactSshpassWord,
6997
+ secretGroup: 3,
6998
+ commandLine: true
6999
+ },
7000
+ {
7001
+ // Environment variables whose only purpose is a password, in shell
7002
+ // assignment form (no space around `=`): libpq's PGPASSWORD (one memory,
7003
+ // version and fact in production), MySQL's MYSQL_PWD, sshpass -e's
7004
+ // SSHPASS. named_value needs an `_PASSWORD`-style suffix and 16
7005
+ // characters, so it misses all three. Not an awk program that matches or
7006
+ // prints the assignment: `/^PGPASSWORD=/ { print "PGPASSWORD=" pw }`.
7007
+ kind: "password_env_var",
7008
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}\b(?:PGPASSWORD|MYSQL_PWD|SSHPASS)=)(${SHELL_WORD})()`, "g"),
7009
+ replacement: redactQuotedShellWord,
7010
+ secretGroup: 3,
7011
+ commandLine: true
7012
+ },
7013
+ {
7014
+ // --password=<value>, the long option every CLI that takes one spells
7015
+ // this way. The `=` keeps prose about the option ("--password is
7016
+ // required") out; the spaced form is not matched for that reason.
7017
+ kind: "password_option",
7018
+ pattern: new RegExp(String.raw`(${OPENING_QUOTE}(?<![\w-])--password=)(${SHELL_WORD})()`, "g"),
7019
+ replacement: redactQuotedShellWord,
7020
+ secretGroup: 3,
7021
+ commandLine: true
7022
+ },
7023
+ {
7024
+ // echo <password> | sudo -S: sudo reads the password from stdin. One
7025
+ // memory, two versions and two facts in production.
7026
+ kind: "sudo_password",
7027
+ 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"),
7028
+ replacement: redactShellWord,
7029
+ secretGroup: 2,
7030
+ commandLine: true
7031
+ }
7032
+ ];
7033
+
7034
+ // packages/shared/dist/sourceRedaction.js
7035
+ init_secretPatterns();
7036
+
6776
7037
  // packages/shared/dist/codeScanner.js
7038
+ init_protectedPaths();
7039
+ init_projectIdentityFile();
6777
7040
  var MAX_SCANNED_FILE_BYTES = 10 * 1024 * 1024;
6778
7041
  var BUILT_IN_IGNORE_DIRS = [
6779
7042
  // JS / web