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