@mnemonik/cli 7.140.3 → 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.
- package/dist/auth/credentials.js +266 -3
- package/dist/auth/index.js +266 -3
- package/dist/auth/installSession.js +262 -0
- package/dist/auth/status.js +262 -0
- package/dist/authorizationWords.js +262 -0
- package/dist/diagnostics.js +266 -3
- package/dist/digests.json +38 -38
- package/dist/hooksLog.js +4 -0
- package/dist/identity/migrate.js +266 -3
- package/dist/install/hosts.js +262 -0
- package/dist/install/journal.js +266 -3
- package/dist/install/journey.js +266 -3
- package/dist/install/ownership.js +266 -3
- package/dist/install/transaction.js +266 -3
- package/dist/install/ui.js +266 -3
- package/dist/installSession.js +262 -0
- package/dist/installation.js +266 -3
- package/dist/launcher.js +266 -3
- package/dist/preflight.js +354 -92
- package/dist/project/eligibility.js +266 -3
- package/dist/project/records.js +266 -3
- package/dist/project.js +270 -7
- package/dist/router.js +270 -7
- package/dist/runtime/releaseSource.js +266 -3
- package/dist/runtime/selfUpdate.js +266 -3
- package/dist/runtime/signers.js +67 -7
- package/dist/runtime/store.js +4 -0
- package/dist/runtime/updateCheck.js +266 -3
- package/dist/sbom.json +21 -21
- package/dist/scanner/control.js +266 -3
- package/dist/scanner/data.js +262 -0
- package/dist/scanner/discover.js +354 -92
- package/dist/scanner/enable.js +270 -7
- package/dist/scanner/picker.js +325 -63
- package/dist/scanner/service.js +266 -3
- package/dist/scanner/update.js +266 -3
- package/dist/scanner-release.json +24 -24
- package/dist/status.js +266 -3
- package/dist/vendor/local-setup.js +266 -3
- package/package.json +31 -31
package/dist/project.js
CHANGED
|
@@ -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
|
|
|
@@ -901,7 +905,7 @@ var init_protectedPaths = __esm({
|
|
|
901
905
|
|
|
902
906
|
// packages/shared/dist/projectIdentityFile.js
|
|
903
907
|
import { lstat, readFile } from "node:fs/promises";
|
|
904
|
-
import { join } from "node:path";
|
|
908
|
+
import { join as join2 } from "node:path";
|
|
905
909
|
function parseIdentityFile(text) {
|
|
906
910
|
let value;
|
|
907
911
|
try {
|
|
@@ -962,7 +966,7 @@ function isConfigurationOnly(text) {
|
|
|
962
966
|
}
|
|
963
967
|
}
|
|
964
968
|
async function readIdentityFile(dir, options = {}) {
|
|
965
|
-
const path =
|
|
969
|
+
const path = join2(dir, ".mnemonik.json");
|
|
966
970
|
try {
|
|
967
971
|
const stat = await lstat(path);
|
|
968
972
|
if (stat.isSymbolicLink())
|
|
@@ -3200,9 +3204,6 @@ var MCP_INSTRUCTIONS = getMcpInstructions();
|
|
|
3200
3204
|
|
|
3201
3205
|
// packages/shared/dist/codeScanner.js
|
|
3202
3206
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3203
|
-
init_secretPatterns();
|
|
3204
|
-
init_protectedPaths();
|
|
3205
|
-
init_projectIdentityFile();
|
|
3206
3207
|
|
|
3207
3208
|
// node_modules/web-tree-sitter/web-tree-sitter.js
|
|
3208
3209
|
var __defProp2 = Object.defineProperty;
|
|
@@ -7248,14 +7249,276 @@ var Query = class {
|
|
|
7248
7249
|
};
|
|
7249
7250
|
|
|
7250
7251
|
// packages/shared/dist/ast/grammars.js
|
|
7251
|
-
import { dirname, extname, join
|
|
7252
|
+
import { dirname, extname, join } from "node:path";
|
|
7252
7253
|
import { fileURLToPath } from "node:url";
|
|
7253
|
-
var WASM_DIR =
|
|
7254
|
+
var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm");
|
|
7254
7255
|
|
|
7255
7256
|
// packages/shared/dist/ast/astChunker.js
|
|
7256
7257
|
var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
|
|
7257
7258
|
|
|
7259
|
+
// packages/shared/dist/redactCredentials.js
|
|
7260
|
+
var REDACTED = "<redacted credential>";
|
|
7261
|
+
var MAX_PEM_BODY_LENGTH = 8 * 1024;
|
|
7262
|
+
var LETTER_OR_DIGIT = /[A-Za-z0-9]/;
|
|
7263
|
+
function hasLetterOrDigit(value) {
|
|
7264
|
+
return LETTER_OR_DIGIT.test(value);
|
|
7265
|
+
}
|
|
7266
|
+
function redactContextValue(match, head = "", value = "") {
|
|
7267
|
+
return hasLetterOrDigit(value) ? `${head}${REDACTED}` : match;
|
|
7268
|
+
}
|
|
7269
|
+
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;
|
|
7270
|
+
var PLACEHOLDER_ENV_VAR = /^\$[A-Z_][A-Z0-9_]*$/;
|
|
7271
|
+
function isPlaceholderPassword(password) {
|
|
7272
|
+
return PLACEHOLDER_PASSWORD.test(password) || PLACEHOLDER_ENV_VAR.test(password);
|
|
7273
|
+
}
|
|
7274
|
+
function redactUrlPassword(match, head = "", password = "") {
|
|
7275
|
+
return isPlaceholderPassword(password) || !hasLetterOrDigit(password) ? match : `${head}${REDACTED}@`;
|
|
7276
|
+
}
|
|
7277
|
+
var SHELL_WORD = String.raw`'[^'\n]*'|"[^"\n]*"|(?!${REDACTED})[^\s'"\`;|&()[\]{}\\,/][^\s'"\`;|&]*`;
|
|
7278
|
+
var OPENING_QUOTE = String.raw`(["'\`]?)`;
|
|
7279
|
+
var TRAILING_PUNCTUATION = /[.,:;!?)\]}]+$/;
|
|
7280
|
+
var PROSE_WORDS = /* @__PURE__ */ new Set([
|
|
7281
|
+
"a",
|
|
7282
|
+
"an",
|
|
7283
|
+
"and",
|
|
7284
|
+
"as",
|
|
7285
|
+
"for",
|
|
7286
|
+
"in",
|
|
7287
|
+
"is",
|
|
7288
|
+
"it",
|
|
7289
|
+
"on",
|
|
7290
|
+
"or",
|
|
7291
|
+
"the",
|
|
7292
|
+
"to",
|
|
7293
|
+
"with",
|
|
7294
|
+
"flag",
|
|
7295
|
+
"option",
|
|
7296
|
+
"argument",
|
|
7297
|
+
"instead",
|
|
7298
|
+
"which",
|
|
7299
|
+
"that",
|
|
7300
|
+
"this",
|
|
7301
|
+
"puts",
|
|
7302
|
+
"passes",
|
|
7303
|
+
"leaks",
|
|
7304
|
+
"exposes"
|
|
7305
|
+
]);
|
|
7306
|
+
var REGEX_DELIMITER = /\/(?:[{}()[\];]|$)/;
|
|
7307
|
+
function shellWordSecret(word) {
|
|
7308
|
+
const quote = word[0];
|
|
7309
|
+
if ((quote === "'" || quote === '"') && word.length >= 2 && word.endsWith(quote)) {
|
|
7310
|
+
const inner = word.slice(1, -1);
|
|
7311
|
+
if (!hasLetterOrDigit(inner) || quote === '"' && inner.startsWith("$"))
|
|
7312
|
+
return null;
|
|
7313
|
+
if (inner !== inner.trim())
|
|
7314
|
+
return null;
|
|
7315
|
+
return isPlaceholderPassword(inner) ? null : inner;
|
|
7316
|
+
}
|
|
7317
|
+
if (word.startsWith("$"))
|
|
7318
|
+
return null;
|
|
7319
|
+
const core = word.replace(TRAILING_PUNCTUATION, "");
|
|
7320
|
+
if (!hasLetterOrDigit(core) || isPlaceholderPassword(core))
|
|
7321
|
+
return null;
|
|
7322
|
+
if (REGEX_DELIMITER.test(core))
|
|
7323
|
+
return null;
|
|
7324
|
+
return PROSE_WORDS.has(core.toLowerCase()) ? null : core;
|
|
7325
|
+
}
|
|
7326
|
+
function redactShellWord(match, head = "", word = "", tail = "") {
|
|
7327
|
+
return shellWordSecret(word) === null ? match : `${head}${REDACTED}${tail}`;
|
|
7328
|
+
}
|
|
7329
|
+
function redactQuotedShellWord(match, head = "", openingQuote = "", word = "", tail = "") {
|
|
7330
|
+
if (openingQuote !== "" && word.startsWith(openingQuote))
|
|
7331
|
+
return match;
|
|
7332
|
+
return redactShellWord(match, head, word, tail);
|
|
7333
|
+
}
|
|
7334
|
+
var SSHPASS_COMMAND = String.raw`ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*`;
|
|
7335
|
+
function redactSshpassWord(match, head = "", openingQuote = "", word = "", tail = "", command = "") {
|
|
7336
|
+
if (openingQuote !== "" && word.startsWith(openingQuote))
|
|
7337
|
+
return match;
|
|
7338
|
+
const secret = shellWordSecret(word);
|
|
7339
|
+
if (secret === null)
|
|
7340
|
+
return match;
|
|
7341
|
+
const bare = word[0] !== "'" && word[0] !== '"';
|
|
7342
|
+
if (bare && /^[a-z]+$/.test(secret) && command === "")
|
|
7343
|
+
return match;
|
|
7344
|
+
return `${head}${REDACTED}${tail}`;
|
|
7345
|
+
}
|
|
7346
|
+
var PATTERNS = [
|
|
7347
|
+
{
|
|
7348
|
+
kind: "bearer",
|
|
7349
|
+
pattern: /(\bBearer\s+)([^\s]{40,})/gi,
|
|
7350
|
+
replacement: redactContextValue,
|
|
7351
|
+
secretGroup: 2
|
|
7352
|
+
},
|
|
7353
|
+
{
|
|
7354
|
+
// Before openai, whose sk- shape would otherwise claim these keys.
|
|
7355
|
+
kind: "anthropic",
|
|
7356
|
+
pattern: /(?<![A-Za-z0-9_-])sk-ant-(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
7357
|
+
replacement: REDACTED
|
|
7358
|
+
},
|
|
7359
|
+
{
|
|
7360
|
+
kind: "openai",
|
|
7361
|
+
pattern: /(?<![A-Za-z0-9_-])sk-(?:proj-)?(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
|
|
7362
|
+
replacement: REDACTED
|
|
7363
|
+
},
|
|
7364
|
+
{
|
|
7365
|
+
kind: "shopify",
|
|
7366
|
+
pattern: /(?<![A-Za-z0-9_])shp(?:at|ss|ca)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/g,
|
|
7367
|
+
replacement: REDACTED
|
|
7368
|
+
},
|
|
7369
|
+
{
|
|
7370
|
+
kind: "aws_access_key",
|
|
7371
|
+
pattern: /(?<![A-Z0-9])AKIA[A-Z0-9]{16}(?![A-Z0-9])/g,
|
|
7372
|
+
replacement: REDACTED
|
|
7373
|
+
},
|
|
7374
|
+
{
|
|
7375
|
+
kind: "github",
|
|
7376
|
+
pattern: /(?<![A-Za-z0-9_])gh[opu]_[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
|
|
7377
|
+
replacement: REDACTED
|
|
7378
|
+
},
|
|
7379
|
+
{
|
|
7380
|
+
kind: "slack",
|
|
7381
|
+
pattern: /(?<![A-Za-z0-9-])xox[abp]-(?=-*[A-Za-z0-9])[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
|
|
7382
|
+
replacement: REDACTED
|
|
7383
|
+
},
|
|
7384
|
+
{
|
|
7385
|
+
kind: "sentry",
|
|
7386
|
+
pattern: /(?<![A-Za-z0-9_])sntry[us]_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
|
|
7387
|
+
replacement: REDACTED
|
|
7388
|
+
},
|
|
7389
|
+
{
|
|
7390
|
+
kind: "fly",
|
|
7391
|
+
pattern: /(?<![A-Za-z0-9_])(?:FlyV1\s+)?fm2_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
|
|
7392
|
+
replacement: REDACTED
|
|
7393
|
+
},
|
|
7394
|
+
{
|
|
7395
|
+
kind: "google_api_key",
|
|
7396
|
+
pattern: /(?<![A-Za-z0-9_-])AIza(?=[_-]*[A-Za-z0-9])[0-9A-Za-z_-]{35}(?![A-Za-z0-9_-])/g,
|
|
7397
|
+
replacement: REDACTED
|
|
7398
|
+
},
|
|
7399
|
+
{
|
|
7400
|
+
kind: "jwt",
|
|
7401
|
+
// header.payload.signature, both JSON segments starting eyJ. The length
|
|
7402
|
+
// floors keep `eyJ...` prose and signature-less examples out.
|
|
7403
|
+
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,
|
|
7404
|
+
replacement: REDACTED
|
|
7405
|
+
},
|
|
7406
|
+
{
|
|
7407
|
+
kind: "mnemonik_component_token",
|
|
7408
|
+
// mnc_ / mncr_ + 43 base64url (oauth/components.ts); 32+ also catches a
|
|
7409
|
+
// partial token. The body must contain an upper-case letter or digit, so a
|
|
7410
|
+
// snake_case identifier is not mistaken for one; the 8-character
|
|
7411
|
+
// display prefix (mnc_ + 8) is too short.
|
|
7412
|
+
pattern: /(?<![A-Za-z0-9_])mncr?_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g,
|
|
7413
|
+
replacement: REDACTED
|
|
7414
|
+
},
|
|
7415
|
+
{
|
|
7416
|
+
kind: "stripe",
|
|
7417
|
+
// Secret and restricted keys (sk_/rk_ live|test) and webhook signing secrets.
|
|
7418
|
+
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,
|
|
7419
|
+
replacement: REDACTED
|
|
7420
|
+
},
|
|
7421
|
+
{
|
|
7422
|
+
kind: "mnemonik_proxy_token",
|
|
7423
|
+
// pxt_ + 32 hex (the format server.ts validates). A bare prefix in prose is left.
|
|
7424
|
+
pattern: /(?<![A-Za-z0-9_])pxt_[0-9a-f]{32,}/g,
|
|
7425
|
+
replacement: REDACTED
|
|
7426
|
+
},
|
|
7427
|
+
{
|
|
7428
|
+
kind: "mnemonik_api_key",
|
|
7429
|
+
// mnk_ + 64 hex (AuthManager.generateApiKey); 32+ also catches a half key.
|
|
7430
|
+
// The stored 12-character key_prefix (mnk_ + 8 hex) is not a credential.
|
|
7431
|
+
pattern: /(?<![A-Za-z0-9_])mnk_[0-9a-f]{32,}/g,
|
|
7432
|
+
replacement: REDACTED
|
|
7433
|
+
},
|
|
7434
|
+
{
|
|
7435
|
+
kind: "mnemonik_api_key_legacy",
|
|
7436
|
+
// mnk_ + 43 base64url: keys minted by hand before AuthManager's hex form
|
|
7437
|
+
// (production's only key in 2026-10 has this shape, and one stored memory
|
|
7438
|
+
// held a development key in it). Authentication accepts any mnk_ bearer.
|
|
7439
|
+
// The body needs upper and lower case and a digit, so a snake_case
|
|
7440
|
+
// identifier that starts mnk_ is not taken for one.
|
|
7441
|
+
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,
|
|
7442
|
+
replacement: REDACTED
|
|
7443
|
+
},
|
|
7444
|
+
{
|
|
7445
|
+
kind: "named_value",
|
|
7446
|
+
pattern: /(\b[A-Za-z_][A-Za-z0-9_]*(?:_TOKEN|_KEY|_SECRET|_PASSWORD|_PASS|APIKEY|API_KEY)\s*(?:=|:)\s*)([^\s]{16,})/gi,
|
|
7447
|
+
replacement: redactContextValue,
|
|
7448
|
+
secretGroup: 2
|
|
7449
|
+
},
|
|
7450
|
+
{
|
|
7451
|
+
kind: "connection_string",
|
|
7452
|
+
// scheme://user:password@host: only the password is replaced; scheme,
|
|
7453
|
+
// user, host and database stay readable. The user may be empty
|
|
7454
|
+
// (redis://:password@host). The password runs to the last `@` before a
|
|
7455
|
+
// path, query, fragment, quote or whitespace, so an unencoded `@` inside
|
|
7456
|
+
// it is still covered. Runs after named_value, which already replaces a
|
|
7457
|
+
// whole URL assigned to a secret-named variable.
|
|
7458
|
+
pattern: /\b((?:postgres(?:ql)?|mysql|mariadb|mongodb|rediss?|amqps?|mssql|sqlserver)(?:\+[a-z0-9]+)?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
|
|
7459
|
+
replacement: redactUrlPassword,
|
|
7460
|
+
secretGroup: 2
|
|
7461
|
+
},
|
|
7462
|
+
{
|
|
7463
|
+
kind: "url_basic_auth",
|
|
7464
|
+
// https://user:password@host: same shape and placeholder rules as a
|
|
7465
|
+
// connection string; only the password is replaced.
|
|
7466
|
+
pattern: /\b(https?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
|
|
7467
|
+
replacement: redactUrlPassword,
|
|
7468
|
+
secretGroup: 2
|
|
7469
|
+
},
|
|
7470
|
+
// Command-line passwords. Each keeps the command and replaces only the
|
|
7471
|
+
// password word; an expansion or placeholder in that position is left.
|
|
7472
|
+
{
|
|
7473
|
+
// sshpass -p <password> (also -p'<password>' and -p"<password>"), after
|
|
7474
|
+
// any other sshpass flags. 13 memories, 11 scanner chunks, 15 versions and
|
|
7475
|
+
// 6 dated facts in production held one on 2026-10-12.
|
|
7476
|
+
kind: "sshpass_password",
|
|
7477
|
+
pattern: new RegExp(String.raw`(${OPENING_QUOTE}\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(${SHELL_WORD})()(?=((?:[ \t]+(?:${SSHPASS_COMMAND})(?![\w-]))?))`, "g"),
|
|
7478
|
+
replacement: redactSshpassWord,
|
|
7479
|
+
secretGroup: 3,
|
|
7480
|
+
commandLine: true
|
|
7481
|
+
},
|
|
7482
|
+
{
|
|
7483
|
+
// Environment variables whose only purpose is a password, in shell
|
|
7484
|
+
// assignment form (no space around `=`): libpq's PGPASSWORD (one memory,
|
|
7485
|
+
// version and fact in production), MySQL's MYSQL_PWD, sshpass -e's
|
|
7486
|
+
// SSHPASS. named_value needs an `_PASSWORD`-style suffix and 16
|
|
7487
|
+
// characters, so it misses all three. Not an awk program that matches or
|
|
7488
|
+
// prints the assignment: `/^PGPASSWORD=/ { print "PGPASSWORD=" pw }`.
|
|
7489
|
+
kind: "password_env_var",
|
|
7490
|
+
pattern: new RegExp(String.raw`(${OPENING_QUOTE}\b(?:PGPASSWORD|MYSQL_PWD|SSHPASS)=)(${SHELL_WORD})()`, "g"),
|
|
7491
|
+
replacement: redactQuotedShellWord,
|
|
7492
|
+
secretGroup: 3,
|
|
7493
|
+
commandLine: true
|
|
7494
|
+
},
|
|
7495
|
+
{
|
|
7496
|
+
// --password=<value>, the long option every CLI that takes one spells
|
|
7497
|
+
// this way. The `=` keeps prose about the option ("--password is
|
|
7498
|
+
// required") out; the spaced form is not matched for that reason.
|
|
7499
|
+
kind: "password_option",
|
|
7500
|
+
pattern: new RegExp(String.raw`(${OPENING_QUOTE}(?<![\w-])--password=)(${SHELL_WORD})()`, "g"),
|
|
7501
|
+
replacement: redactQuotedShellWord,
|
|
7502
|
+
secretGroup: 3,
|
|
7503
|
+
commandLine: true
|
|
7504
|
+
},
|
|
7505
|
+
{
|
|
7506
|
+
// echo <password> | sudo -S: sudo reads the password from stdin. One
|
|
7507
|
+
// memory, two versions and two facts in production.
|
|
7508
|
+
kind: "sudo_password",
|
|
7509
|
+
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"),
|
|
7510
|
+
replacement: redactShellWord,
|
|
7511
|
+
secretGroup: 2,
|
|
7512
|
+
commandLine: true
|
|
7513
|
+
}
|
|
7514
|
+
];
|
|
7515
|
+
|
|
7516
|
+
// packages/shared/dist/sourceRedaction.js
|
|
7517
|
+
init_secretPatterns();
|
|
7518
|
+
|
|
7258
7519
|
// packages/shared/dist/codeScanner.js
|
|
7520
|
+
init_protectedPaths();
|
|
7521
|
+
init_projectIdentityFile();
|
|
7259
7522
|
var MAX_SCANNED_FILE_BYTES = 10 * 1024 * 1024;
|
|
7260
7523
|
var BUILT_IN_IGNORE_DIRS = [
|
|
7261
7524
|
// JS / web
|
package/dist/router.js
CHANGED
|
@@ -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
|
|
|
@@ -901,7 +905,7 @@ var init_protectedPaths = __esm({
|
|
|
901
905
|
|
|
902
906
|
// packages/shared/dist/projectIdentityFile.js
|
|
903
907
|
import { lstat, readFile } from "node:fs/promises";
|
|
904
|
-
import { join } from "node:path";
|
|
908
|
+
import { join as join2 } from "node:path";
|
|
905
909
|
function parseIdentityFile(text) {
|
|
906
910
|
let value;
|
|
907
911
|
try {
|
|
@@ -962,7 +966,7 @@ function isConfigurationOnly(text) {
|
|
|
962
966
|
}
|
|
963
967
|
}
|
|
964
968
|
async function readIdentityFile(dir, options = {}) {
|
|
965
|
-
const path =
|
|
969
|
+
const path = join2(dir, ".mnemonik.json");
|
|
966
970
|
try {
|
|
967
971
|
const stat = await lstat(path);
|
|
968
972
|
if (stat.isSymbolicLink())
|
|
@@ -3093,9 +3097,6 @@ var MCP_INSTRUCTIONS = getMcpInstructions();
|
|
|
3093
3097
|
|
|
3094
3098
|
// packages/shared/dist/codeScanner.js
|
|
3095
3099
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3096
|
-
init_secretPatterns();
|
|
3097
|
-
init_protectedPaths();
|
|
3098
|
-
init_projectIdentityFile();
|
|
3099
3100
|
|
|
3100
3101
|
// node_modules/web-tree-sitter/web-tree-sitter.js
|
|
3101
3102
|
var __defProp2 = Object.defineProperty;
|
|
@@ -7141,14 +7142,276 @@ var Query = class {
|
|
|
7141
7142
|
};
|
|
7142
7143
|
|
|
7143
7144
|
// packages/shared/dist/ast/grammars.js
|
|
7144
|
-
import { dirname, extname, join
|
|
7145
|
+
import { dirname, extname, join } from "node:path";
|
|
7145
7146
|
import { fileURLToPath } from "node:url";
|
|
7146
|
-
var WASM_DIR =
|
|
7147
|
+
var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm");
|
|
7147
7148
|
|
|
7148
7149
|
// packages/shared/dist/ast/astChunker.js
|
|
7149
7150
|
var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
|
|
7150
7151
|
|
|
7152
|
+
// packages/shared/dist/redactCredentials.js
|
|
7153
|
+
var REDACTED = "<redacted credential>";
|
|
7154
|
+
var MAX_PEM_BODY_LENGTH = 8 * 1024;
|
|
7155
|
+
var LETTER_OR_DIGIT = /[A-Za-z0-9]/;
|
|
7156
|
+
function hasLetterOrDigit(value) {
|
|
7157
|
+
return LETTER_OR_DIGIT.test(value);
|
|
7158
|
+
}
|
|
7159
|
+
function redactContextValue(match, head = "", value = "") {
|
|
7160
|
+
return hasLetterOrDigit(value) ? `${head}${REDACTED}` : match;
|
|
7161
|
+
}
|
|
7162
|
+
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;
|
|
7163
|
+
var PLACEHOLDER_ENV_VAR = /^\$[A-Z_][A-Z0-9_]*$/;
|
|
7164
|
+
function isPlaceholderPassword(password) {
|
|
7165
|
+
return PLACEHOLDER_PASSWORD.test(password) || PLACEHOLDER_ENV_VAR.test(password);
|
|
7166
|
+
}
|
|
7167
|
+
function redactUrlPassword(match, head = "", password = "") {
|
|
7168
|
+
return isPlaceholderPassword(password) || !hasLetterOrDigit(password) ? match : `${head}${REDACTED}@`;
|
|
7169
|
+
}
|
|
7170
|
+
var SHELL_WORD = String.raw`'[^'\n]*'|"[^"\n]*"|(?!${REDACTED})[^\s'"\`;|&()[\]{}\\,/][^\s'"\`;|&]*`;
|
|
7171
|
+
var OPENING_QUOTE = String.raw`(["'\`]?)`;
|
|
7172
|
+
var TRAILING_PUNCTUATION = /[.,:;!?)\]}]+$/;
|
|
7173
|
+
var PROSE_WORDS = /* @__PURE__ */ new Set([
|
|
7174
|
+
"a",
|
|
7175
|
+
"an",
|
|
7176
|
+
"and",
|
|
7177
|
+
"as",
|
|
7178
|
+
"for",
|
|
7179
|
+
"in",
|
|
7180
|
+
"is",
|
|
7181
|
+
"it",
|
|
7182
|
+
"on",
|
|
7183
|
+
"or",
|
|
7184
|
+
"the",
|
|
7185
|
+
"to",
|
|
7186
|
+
"with",
|
|
7187
|
+
"flag",
|
|
7188
|
+
"option",
|
|
7189
|
+
"argument",
|
|
7190
|
+
"instead",
|
|
7191
|
+
"which",
|
|
7192
|
+
"that",
|
|
7193
|
+
"this",
|
|
7194
|
+
"puts",
|
|
7195
|
+
"passes",
|
|
7196
|
+
"leaks",
|
|
7197
|
+
"exposes"
|
|
7198
|
+
]);
|
|
7199
|
+
var REGEX_DELIMITER = /\/(?:[{}()[\];]|$)/;
|
|
7200
|
+
function shellWordSecret(word) {
|
|
7201
|
+
const quote = word[0];
|
|
7202
|
+
if ((quote === "'" || quote === '"') && word.length >= 2 && word.endsWith(quote)) {
|
|
7203
|
+
const inner = word.slice(1, -1);
|
|
7204
|
+
if (!hasLetterOrDigit(inner) || quote === '"' && inner.startsWith("$"))
|
|
7205
|
+
return null;
|
|
7206
|
+
if (inner !== inner.trim())
|
|
7207
|
+
return null;
|
|
7208
|
+
return isPlaceholderPassword(inner) ? null : inner;
|
|
7209
|
+
}
|
|
7210
|
+
if (word.startsWith("$"))
|
|
7211
|
+
return null;
|
|
7212
|
+
const core = word.replace(TRAILING_PUNCTUATION, "");
|
|
7213
|
+
if (!hasLetterOrDigit(core) || isPlaceholderPassword(core))
|
|
7214
|
+
return null;
|
|
7215
|
+
if (REGEX_DELIMITER.test(core))
|
|
7216
|
+
return null;
|
|
7217
|
+
return PROSE_WORDS.has(core.toLowerCase()) ? null : core;
|
|
7218
|
+
}
|
|
7219
|
+
function redactShellWord(match, head = "", word = "", tail = "") {
|
|
7220
|
+
return shellWordSecret(word) === null ? match : `${head}${REDACTED}${tail}`;
|
|
7221
|
+
}
|
|
7222
|
+
function redactQuotedShellWord(match, head = "", openingQuote = "", word = "", tail = "") {
|
|
7223
|
+
if (openingQuote !== "" && word.startsWith(openingQuote))
|
|
7224
|
+
return match;
|
|
7225
|
+
return redactShellWord(match, head, word, tail);
|
|
7226
|
+
}
|
|
7227
|
+
var SSHPASS_COMMAND = String.raw`ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*`;
|
|
7228
|
+
function redactSshpassWord(match, head = "", openingQuote = "", word = "", tail = "", command = "") {
|
|
7229
|
+
if (openingQuote !== "" && word.startsWith(openingQuote))
|
|
7230
|
+
return match;
|
|
7231
|
+
const secret = shellWordSecret(word);
|
|
7232
|
+
if (secret === null)
|
|
7233
|
+
return match;
|
|
7234
|
+
const bare = word[0] !== "'" && word[0] !== '"';
|
|
7235
|
+
if (bare && /^[a-z]+$/.test(secret) && command === "")
|
|
7236
|
+
return match;
|
|
7237
|
+
return `${head}${REDACTED}${tail}`;
|
|
7238
|
+
}
|
|
7239
|
+
var PATTERNS = [
|
|
7240
|
+
{
|
|
7241
|
+
kind: "bearer",
|
|
7242
|
+
pattern: /(\bBearer\s+)([^\s]{40,})/gi,
|
|
7243
|
+
replacement: redactContextValue,
|
|
7244
|
+
secretGroup: 2
|
|
7245
|
+
},
|
|
7246
|
+
{
|
|
7247
|
+
// Before openai, whose sk- shape would otherwise claim these keys.
|
|
7248
|
+
kind: "anthropic",
|
|
7249
|
+
pattern: /(?<![A-Za-z0-9_-])sk-ant-(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
7250
|
+
replacement: REDACTED
|
|
7251
|
+
},
|
|
7252
|
+
{
|
|
7253
|
+
kind: "openai",
|
|
7254
|
+
pattern: /(?<![A-Za-z0-9_-])sk-(?:proj-)?(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
|
|
7255
|
+
replacement: REDACTED
|
|
7256
|
+
},
|
|
7257
|
+
{
|
|
7258
|
+
kind: "shopify",
|
|
7259
|
+
pattern: /(?<![A-Za-z0-9_])shp(?:at|ss|ca)_[A-Za-z0-9]{16,}(?![A-Za-z0-9])/g,
|
|
7260
|
+
replacement: REDACTED
|
|
7261
|
+
},
|
|
7262
|
+
{
|
|
7263
|
+
kind: "aws_access_key",
|
|
7264
|
+
pattern: /(?<![A-Z0-9])AKIA[A-Z0-9]{16}(?![A-Z0-9])/g,
|
|
7265
|
+
replacement: REDACTED
|
|
7266
|
+
},
|
|
7267
|
+
{
|
|
7268
|
+
kind: "github",
|
|
7269
|
+
pattern: /(?<![A-Za-z0-9_])gh[opu]_[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
|
|
7270
|
+
replacement: REDACTED
|
|
7271
|
+
},
|
|
7272
|
+
{
|
|
7273
|
+
kind: "slack",
|
|
7274
|
+
pattern: /(?<![A-Za-z0-9-])xox[abp]-(?=-*[A-Za-z0-9])[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
|
|
7275
|
+
replacement: REDACTED
|
|
7276
|
+
},
|
|
7277
|
+
{
|
|
7278
|
+
kind: "sentry",
|
|
7279
|
+
pattern: /(?<![A-Za-z0-9_])sntry[us]_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
|
|
7280
|
+
replacement: REDACTED
|
|
7281
|
+
},
|
|
7282
|
+
{
|
|
7283
|
+
kind: "fly",
|
|
7284
|
+
pattern: /(?<![A-Za-z0-9_])(?:FlyV1\s+)?fm2_(?=[_-]*[A-Za-z0-9])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
|
|
7285
|
+
replacement: REDACTED
|
|
7286
|
+
},
|
|
7287
|
+
{
|
|
7288
|
+
kind: "google_api_key",
|
|
7289
|
+
pattern: /(?<![A-Za-z0-9_-])AIza(?=[_-]*[A-Za-z0-9])[0-9A-Za-z_-]{35}(?![A-Za-z0-9_-])/g,
|
|
7290
|
+
replacement: REDACTED
|
|
7291
|
+
},
|
|
7292
|
+
{
|
|
7293
|
+
kind: "jwt",
|
|
7294
|
+
// header.payload.signature, both JSON segments starting eyJ. The length
|
|
7295
|
+
// floors keep `eyJ...` prose and signature-less examples out.
|
|
7296
|
+
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,
|
|
7297
|
+
replacement: REDACTED
|
|
7298
|
+
},
|
|
7299
|
+
{
|
|
7300
|
+
kind: "mnemonik_component_token",
|
|
7301
|
+
// mnc_ / mncr_ + 43 base64url (oauth/components.ts); 32+ also catches a
|
|
7302
|
+
// partial token. The body must contain an upper-case letter or digit, so a
|
|
7303
|
+
// snake_case identifier is not mistaken for one; the 8-character
|
|
7304
|
+
// display prefix (mnc_ + 8) is too short.
|
|
7305
|
+
pattern: /(?<![A-Za-z0-9_])mncr?_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g,
|
|
7306
|
+
replacement: REDACTED
|
|
7307
|
+
},
|
|
7308
|
+
{
|
|
7309
|
+
kind: "stripe",
|
|
7310
|
+
// Secret and restricted keys (sk_/rk_ live|test) and webhook signing secrets.
|
|
7311
|
+
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,
|
|
7312
|
+
replacement: REDACTED
|
|
7313
|
+
},
|
|
7314
|
+
{
|
|
7315
|
+
kind: "mnemonik_proxy_token",
|
|
7316
|
+
// pxt_ + 32 hex (the format server.ts validates). A bare prefix in prose is left.
|
|
7317
|
+
pattern: /(?<![A-Za-z0-9_])pxt_[0-9a-f]{32,}/g,
|
|
7318
|
+
replacement: REDACTED
|
|
7319
|
+
},
|
|
7320
|
+
{
|
|
7321
|
+
kind: "mnemonik_api_key",
|
|
7322
|
+
// mnk_ + 64 hex (AuthManager.generateApiKey); 32+ also catches a half key.
|
|
7323
|
+
// The stored 12-character key_prefix (mnk_ + 8 hex) is not a credential.
|
|
7324
|
+
pattern: /(?<![A-Za-z0-9_])mnk_[0-9a-f]{32,}/g,
|
|
7325
|
+
replacement: REDACTED
|
|
7326
|
+
},
|
|
7327
|
+
{
|
|
7328
|
+
kind: "mnemonik_api_key_legacy",
|
|
7329
|
+
// mnk_ + 43 base64url: keys minted by hand before AuthManager's hex form
|
|
7330
|
+
// (production's only key in 2026-10 has this shape, and one stored memory
|
|
7331
|
+
// held a development key in it). Authentication accepts any mnk_ bearer.
|
|
7332
|
+
// The body needs upper and lower case and a digit, so a snake_case
|
|
7333
|
+
// identifier that starts mnk_ is not taken for one.
|
|
7334
|
+
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,
|
|
7335
|
+
replacement: REDACTED
|
|
7336
|
+
},
|
|
7337
|
+
{
|
|
7338
|
+
kind: "named_value",
|
|
7339
|
+
pattern: /(\b[A-Za-z_][A-Za-z0-9_]*(?:_TOKEN|_KEY|_SECRET|_PASSWORD|_PASS|APIKEY|API_KEY)\s*(?:=|:)\s*)([^\s]{16,})/gi,
|
|
7340
|
+
replacement: redactContextValue,
|
|
7341
|
+
secretGroup: 2
|
|
7342
|
+
},
|
|
7343
|
+
{
|
|
7344
|
+
kind: "connection_string",
|
|
7345
|
+
// scheme://user:password@host: only the password is replaced; scheme,
|
|
7346
|
+
// user, host and database stay readable. The user may be empty
|
|
7347
|
+
// (redis://:password@host). The password runs to the last `@` before a
|
|
7348
|
+
// path, query, fragment, quote or whitespace, so an unencoded `@` inside
|
|
7349
|
+
// it is still covered. Runs after named_value, which already replaces a
|
|
7350
|
+
// whole URL assigned to a secret-named variable.
|
|
7351
|
+
pattern: /\b((?:postgres(?:ql)?|mysql|mariadb|mongodb|rediss?|amqps?|mssql|sqlserver)(?:\+[a-z0-9]+)?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
|
|
7352
|
+
replacement: redactUrlPassword,
|
|
7353
|
+
secretGroup: 2
|
|
7354
|
+
},
|
|
7355
|
+
{
|
|
7356
|
+
kind: "url_basic_auth",
|
|
7357
|
+
// https://user:password@host: same shape and placeholder rules as a
|
|
7358
|
+
// connection string; only the password is replaced.
|
|
7359
|
+
pattern: /\b(https?:\/\/[^\s:/?#@'"`<>]{0,256}:)([^\s/?#'"`]{1,512})@/gi,
|
|
7360
|
+
replacement: redactUrlPassword,
|
|
7361
|
+
secretGroup: 2
|
|
7362
|
+
},
|
|
7363
|
+
// Command-line passwords. Each keeps the command and replaces only the
|
|
7364
|
+
// password word; an expansion or placeholder in that position is left.
|
|
7365
|
+
{
|
|
7366
|
+
// sshpass -p <password> (also -p'<password>' and -p"<password>"), after
|
|
7367
|
+
// any other sshpass flags. 13 memories, 11 scanner chunks, 15 versions and
|
|
7368
|
+
// 6 dated facts in production held one on 2026-10-12.
|
|
7369
|
+
kind: "sshpass_password",
|
|
7370
|
+
pattern: new RegExp(String.raw`(${OPENING_QUOTE}\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(${SHELL_WORD})()(?=((?:[ \t]+(?:${SSHPASS_COMMAND})(?![\w-]))?))`, "g"),
|
|
7371
|
+
replacement: redactSshpassWord,
|
|
7372
|
+
secretGroup: 3,
|
|
7373
|
+
commandLine: true
|
|
7374
|
+
},
|
|
7375
|
+
{
|
|
7376
|
+
// Environment variables whose only purpose is a password, in shell
|
|
7377
|
+
// assignment form (no space around `=`): libpq's PGPASSWORD (one memory,
|
|
7378
|
+
// version and fact in production), MySQL's MYSQL_PWD, sshpass -e's
|
|
7379
|
+
// SSHPASS. named_value needs an `_PASSWORD`-style suffix and 16
|
|
7380
|
+
// characters, so it misses all three. Not an awk program that matches or
|
|
7381
|
+
// prints the assignment: `/^PGPASSWORD=/ { print "PGPASSWORD=" pw }`.
|
|
7382
|
+
kind: "password_env_var",
|
|
7383
|
+
pattern: new RegExp(String.raw`(${OPENING_QUOTE}\b(?:PGPASSWORD|MYSQL_PWD|SSHPASS)=)(${SHELL_WORD})()`, "g"),
|
|
7384
|
+
replacement: redactQuotedShellWord,
|
|
7385
|
+
secretGroup: 3,
|
|
7386
|
+
commandLine: true
|
|
7387
|
+
},
|
|
7388
|
+
{
|
|
7389
|
+
// --password=<value>, the long option every CLI that takes one spells
|
|
7390
|
+
// this way. The `=` keeps prose about the option ("--password is
|
|
7391
|
+
// required") out; the spaced form is not matched for that reason.
|
|
7392
|
+
kind: "password_option",
|
|
7393
|
+
pattern: new RegExp(String.raw`(${OPENING_QUOTE}(?<![\w-])--password=)(${SHELL_WORD})()`, "g"),
|
|
7394
|
+
replacement: redactQuotedShellWord,
|
|
7395
|
+
secretGroup: 3,
|
|
7396
|
+
commandLine: true
|
|
7397
|
+
},
|
|
7398
|
+
{
|
|
7399
|
+
// echo <password> | sudo -S: sudo reads the password from stdin. One
|
|
7400
|
+
// memory, two versions and two facts in production.
|
|
7401
|
+
kind: "sudo_password",
|
|
7402
|
+
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"),
|
|
7403
|
+
replacement: redactShellWord,
|
|
7404
|
+
secretGroup: 2,
|
|
7405
|
+
commandLine: true
|
|
7406
|
+
}
|
|
7407
|
+
];
|
|
7408
|
+
|
|
7409
|
+
// packages/shared/dist/sourceRedaction.js
|
|
7410
|
+
init_secretPatterns();
|
|
7411
|
+
|
|
7151
7412
|
// packages/shared/dist/codeScanner.js
|
|
7413
|
+
init_protectedPaths();
|
|
7414
|
+
init_projectIdentityFile();
|
|
7152
7415
|
var MAX_SCANNED_FILE_BYTES = 10 * 1024 * 1024;
|
|
7153
7416
|
var BUILT_IN_IGNORE_DIRS = [
|
|
7154
7417
|
// JS / web
|