vibefortress 0.1.32 → 0.1.52
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/.claude-plugin/plugin.json +1 -1
- package/dist/MANIFEST.sha256 +4 -4
- package/dist/cli.js +21 -2
- package/dist/hook.js +165 -40
- package/dist/panel.html +1 -1
- package/dist/release.json +9 -9
- package/dist/release.sig +1 -1
- package/dist/server.js +2284 -1105
- package/package.json +1 -1
package/dist/MANIFEST.sha256
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
43acc2492adebae8e98d838a0f0b4b6e8e1d12073385b67306c123287a6051ad THIRD_PARTY_LICENSES.txt
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
f1f23262dcf2fe49120dcc50d8d5c228d3c4ccd34976931c06bb9b1ec1ad44d2 cli.js
|
|
3
|
+
b5647dd61e739195912a5132d55b671dba78e1a100de72af11951ba8a0983306 hook.js
|
|
4
|
+
501dc52451ad036d7345b1cdf152b0e9ab6ecaaee718b13459983356f97ce43f panel.html
|
|
5
5
|
cb454e01aa8690db6a4589c97d9d683b866d93fae4163b516951ae1cc27774a8 rules/vibefort.yaml
|
|
6
6
|
86821fb93d339d54a9a5a1488128ec2bb313d04ec0213a6b88a58f9d6e94068c scanners.lock.json
|
|
7
|
-
|
|
7
|
+
9d9301fe27919ce338c60d78568155888f305da98fa04b1e45ddb350426b36c6 server.js
|
package/dist/cli.js
CHANGED
|
@@ -245,11 +245,13 @@ var init_clients = __esm({
|
|
|
245
245
|
command(e, "remove the hosted service", ["claude", "mcp", "remove", "--scope", "user", REMOTE_NAME], hasRemote, true)
|
|
246
246
|
];
|
|
247
247
|
const a = [];
|
|
248
|
+
const pluginBringsRemote = o.local && mcpUrl(e.env) === DEFAULT_MCP_URL;
|
|
249
|
+
if (pluginBringsRemote && hasRemote()) a.push(command(e, "remove the separate hosted-service entry (the plugin includes it now)", ["claude", "mcp", "remove", "--scope", "user", REMOTE_NAME], hasRemote, true));
|
|
248
250
|
if (o.local) a.push(
|
|
249
251
|
command(e, "add the Vibe Fortress marketplace", ["claude", "plugin", "marketplace", "add", source], hasMarket),
|
|
250
|
-
command(e,
|
|
252
|
+
command(e, `install the plugin (local scanner, ${pluginBringsRemote ? "hosted service, " : ""}skills, hooks)`, ["claude", "plugin", "install", plugin, "--scope", "user"], hasPlugin)
|
|
251
253
|
);
|
|
252
|
-
if (o.remote) a.push(command(e, "add the hosted service", ["claude", "mcp", "add", "--transport", "http", "--scope", "user", REMOTE_NAME, mcpUrl(e.env)], hasRemote));
|
|
254
|
+
if (o.remote && !pluginBringsRemote) a.push(command(e, "add the hosted service", ["claude", "mcp", "add", "--transport", "http", "--scope", "user", REMOTE_NAME, mcpUrl(e.env)], hasRemote));
|
|
253
255
|
return a;
|
|
254
256
|
}
|
|
255
257
|
};
|
|
@@ -467,6 +469,12 @@ var HELP = `Vibe Fortress: security that keeps up with vibe coding.
|
|
|
467
469
|
Usage:
|
|
468
470
|
npx vibefortress find your AI tools and install Vibe Fortress in them
|
|
469
471
|
npx vibefortress uninstall remove what the installer added
|
|
472
|
+
npx vibefortress collect --scan <id> --code <code>
|
|
473
|
+
npx vibefortress check --scan <id> --code <code>
|
|
474
|
+
first step of a deep scan: describe the checked-out commit (file list from git,
|
|
475
|
+
packages from the lockfile), run the local tools and send the results (never code)
|
|
476
|
+
to Vibe Fortress. Options: --path <folder>, --base <commit>, --server <address>,
|
|
477
|
+
--dry-run (print what would be sent, send nothing)
|
|
470
478
|
|
|
471
479
|
Options:
|
|
472
480
|
--yes, -y do not ask, install in every tool found
|
|
@@ -505,6 +513,17 @@ async function main(argv) {
|
|
|
505
513
|
const r = spawnSync2(process.execPath, [hook, rest[0]], { stdio: "inherit" });
|
|
506
514
|
return r.status ?? 0;
|
|
507
515
|
}
|
|
516
|
+
if (cmd === "collect" || cmd === "check") {
|
|
517
|
+
const built = join3(here, "server.js"), entry = existsSync4(built) ? built : join3(here, "..", "local", "index.ts");
|
|
518
|
+
if (!existsSync4(entry)) {
|
|
519
|
+
console.error("vibefortress: server.js is missing from this install.");
|
|
520
|
+
return 1;
|
|
521
|
+
}
|
|
522
|
+
process.env.VIBEFORT_ENTRY = cmd;
|
|
523
|
+
process.env.VIBEFORT_COLLECT_ARGS = JSON.stringify(rest);
|
|
524
|
+
await import(pathToFileURL(entry).href);
|
|
525
|
+
return -1;
|
|
526
|
+
}
|
|
508
527
|
const { runInstaller: runInstaller2, parseArgs: parseArgs2, realEnv: realEnv2 } = await Promise.resolve().then(() => (init_run(), run_exports));
|
|
509
528
|
const parsed = parseArgs2(cmd === "install" || cmd === "uninstall" ? rest : argv, cmd === "uninstall" ? "uninstall" : "install");
|
|
510
529
|
if ("error" in parsed) {
|
package/dist/hook.js
CHANGED
|
@@ -19749,12 +19749,28 @@ var env = external_exports.object({
|
|
|
19749
19749
|
APP_SECRET: external_exports.string().min(16).optional(),
|
|
19750
19750
|
GITHUB_CLIENT_ID: external_exports.string().optional(),
|
|
19751
19751
|
GITHUB_CLIENT_SECRET: external_exports.string().optional(),
|
|
19752
|
+
CONTACT_EMAIL: external_exports.string().email().optional(),
|
|
19753
|
+
REVIEWER_LOGIN: external_exports.enum(["0", "1"]).default("0"),
|
|
19754
|
+
REVIEWER_LOGIN_SECRET: external_exports.string().optional(),
|
|
19755
|
+
REVIEWER_LOGIN_UNTIL: external_exports.string().optional(),
|
|
19756
|
+
SIGNIN_LOCKED: external_exports.enum(["0", "1"]).default("0"),
|
|
19757
|
+
SERVE_SITE: external_exports.enum(["0", "1"]).default("0"),
|
|
19758
|
+
GITLAB_CLIENT_ID: external_exports.string().optional(),
|
|
19759
|
+
GITLAB_CLIENT_SECRET: external_exports.string().optional(),
|
|
19760
|
+
GITLAB_BASE_URL: external_exports.string().default("https://gitlab.com"),
|
|
19761
|
+
GOOGLE_CLIENT_ID: external_exports.string().optional(),
|
|
19762
|
+
GOOGLE_CLIENT_SECRET: external_exports.string().optional(),
|
|
19763
|
+
APPLE_CLIENT_ID: external_exports.string().optional(),
|
|
19764
|
+
APPLE_TEAM_ID: external_exports.string().optional(),
|
|
19765
|
+
APPLE_KEY_ID: external_exports.string().optional(),
|
|
19766
|
+
APPLE_PRIVATE_KEY: external_exports.string().optional(),
|
|
19752
19767
|
BILLING_MODE: external_exports.enum(["fake", "stripe"]).default("fake"),
|
|
19753
19768
|
STRIPE_SECRET_KEY: external_exports.string().optional(),
|
|
19754
19769
|
STRIPE_WEBHOOK_SECRET: external_exports.string().optional(),
|
|
19755
19770
|
STRIPE_PRICE_ID: external_exports.string().optional(),
|
|
19756
19771
|
STRIPE_PRICE_MONTH: external_exports.string().optional(),
|
|
19757
19772
|
STRIPE_PRICE_YEAR: external_exports.string().optional(),
|
|
19773
|
+
STRIPE_API_VERSION: external_exports.string().default("2025-09-30.clover"),
|
|
19758
19774
|
MANTIS_DIR: external_exports.string().default("vendor/mantis"),
|
|
19759
19775
|
OSV_MODE: external_exports.enum(["real", "fake"]).default("real"),
|
|
19760
19776
|
FREE_REPOS: num(1),
|
|
@@ -19770,18 +19786,38 @@ var env = external_exports.object({
|
|
|
19770
19786
|
TRIAL_DAYS: num(30),
|
|
19771
19787
|
PERIOD_GRACE_DAYS: num(3),
|
|
19772
19788
|
SKILL_TOKEN_BUDGET: num(12e3),
|
|
19789
|
+
ANSWER_CHAR_BUDGET: external_exports.coerce.number().min(12e3).default(3e4),
|
|
19773
19790
|
MAX_RESEARCH_TASKS: num(12),
|
|
19774
19791
|
MAX_INVESTIGATIONS: num(8),
|
|
19775
19792
|
SWEEP: external_exports.enum(["on", "off"]).default("on"),
|
|
19776
19793
|
SWEEP_FILES_PER_TASK: num(10),
|
|
19777
19794
|
SPOT_CHECKS: num(3),
|
|
19795
|
+
SPOT_CHECKS_MAX: num(6),
|
|
19796
|
+
RISK_STEPS_MAX: num(20),
|
|
19797
|
+
STEP_FILES: num(8),
|
|
19798
|
+
STEP_KB: num(120),
|
|
19799
|
+
SWEEP_FILES_MAX: num(80),
|
|
19800
|
+
LARGE_FILE_KB: num(40),
|
|
19801
|
+
MAX_INVESTIGATIONS_CEILING: num(16),
|
|
19778
19802
|
INVENTORY_MAX: num(2e3),
|
|
19803
|
+
INVENTORY_OMITTED_MAX: num(400),
|
|
19804
|
+
TREE_MODE: external_exports.enum(["real", "fake", "off"]).default("real"),
|
|
19805
|
+
TREE_TIMEOUT_MS: num(8e3),
|
|
19806
|
+
DEPS_PER_REPORT: num(1e3),
|
|
19807
|
+
COLLECT_CODE_TTL_MIN: num(60),
|
|
19808
|
+
COLLECT_MAX_BYTES: num(8e6),
|
|
19809
|
+
FULL_COVERAGE: external_exports.enum(["on", "off"]).default("on"),
|
|
19810
|
+
STEP_LINES: num(1500),
|
|
19811
|
+
COVER_STEP_FILES: num(12),
|
|
19812
|
+
PROOF_CHECK: external_exports.enum(["on", "off"]).default("on"),
|
|
19813
|
+
PROOF_CODE_TTL_MIN: num(60),
|
|
19814
|
+
REVIEW_REOPEN_MAX: num(2),
|
|
19779
19815
|
ATTACK_CLASSES: external_exports.enum(["on", "off"]).default("off"),
|
|
19780
19816
|
ATTACK_CLASS_BUDGET: num(3e3),
|
|
19781
19817
|
TEAM_SEATS: num(5),
|
|
19782
19818
|
INVITE_TTL_DAYS: num(7),
|
|
19783
19819
|
LAPSE_READONLY_DAYS: num(30),
|
|
19784
|
-
TEAM_WORKSPACES_PER_OWNER: num(
|
|
19820
|
+
TEAM_WORKSPACES_PER_OWNER: num(10),
|
|
19785
19821
|
INVITE_CREATE_PER_DAY: num(20),
|
|
19786
19822
|
INVITEE_RETENTION_DAYS: num(30),
|
|
19787
19823
|
INVITE_DELIVERY: external_exports.enum(["dev"]).default("dev"),
|
|
@@ -19811,6 +19847,12 @@ var config2 = {
|
|
|
19811
19847
|
host: env.HOST,
|
|
19812
19848
|
/** Gated private beta (feature 032, D-264): allows AUTH_MODE=dev and BILLING_MODE=fake in production, only with DEV_ACCESS_TOKEN. */
|
|
19813
19849
|
privateBeta: env.PRIVATE_BETA === "1",
|
|
19850
|
+
/** The owner's contact address shown on the legal pages (043, D-399). */
|
|
19851
|
+
contactEmail: env.CONTACT_EMAIL,
|
|
19852
|
+
/** Temporary review account for app directory reviewers (043, D-396). Off unless all three values are set and valid: see src/auth/reviewer.ts. */
|
|
19853
|
+
reviewer: { flag: env.REVIEWER_LOGIN === "1", secret: env.REVIEWER_LOGIN_SECRET ?? "", until: env.REVIEWER_LOGIN_UNTIL ?? "" },
|
|
19854
|
+
/** Serve the marketing site (site/dist) from this server, and send other host names of the domain to PUBLIC_URL (D-303). */
|
|
19855
|
+
serveSite: env.SERVE_SITE === "1",
|
|
19814
19856
|
publicUrl: env.PUBLIC_URL.replace(/\/$/, ""),
|
|
19815
19857
|
databaseUrl: env.DATABASE_URL,
|
|
19816
19858
|
pgliteDir: env.PGLITE_DIR,
|
|
@@ -19818,18 +19860,45 @@ var config2 = {
|
|
|
19818
19860
|
devAccessToken: env.DEV_ACCESS_TOKEN,
|
|
19819
19861
|
appSecret: env.APP_SECRET ?? randomBytes(32).toString("hex"),
|
|
19820
19862
|
github: { clientId: env.GITHUB_CLIENT_ID, clientSecret: env.GITHUB_CLIENT_SECRET },
|
|
19863
|
+
/** Website sign-in providers (feature 035, D-300). A provider is offered only when every value it needs is set. Secrets are never logged or returned. */
|
|
19864
|
+
signIn: {
|
|
19865
|
+
gitlab: { baseUrl: env.GITLAB_BASE_URL.replace(/\/$/, ""), clientId: env.GITLAB_CLIENT_ID, clientSecret: env.GITLAB_CLIENT_SECRET },
|
|
19866
|
+
google: { clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET },
|
|
19867
|
+
apple: { clientId: env.APPLE_CLIENT_ID, teamId: env.APPLE_TEAM_ID, keyId: env.APPLE_KEY_ID, privateKey: env.APPLE_PRIVATE_KEY },
|
|
19868
|
+
emailLinkMinutes: 15,
|
|
19869
|
+
/** Public launch before any provider exists (D-303): every method is listed as "coming soon" and none can start. */
|
|
19870
|
+
locked: env.SIGNIN_LOCKED === "1"
|
|
19871
|
+
},
|
|
19821
19872
|
billingMode: env.BILLING_MODE,
|
|
19822
|
-
stripe: { secretKey: env.STRIPE_SECRET_KEY, webhookSecret: env.STRIPE_WEBHOOK_SECRET, priceMonth: env.STRIPE_PRICE_MONTH ?? env.STRIPE_PRICE_ID, priceYear: env.STRIPE_PRICE_YEAR },
|
|
19873
|
+
stripe: { secretKey: env.STRIPE_SECRET_KEY, webhookSecret: env.STRIPE_WEBHOOK_SECRET, priceMonth: env.STRIPE_PRICE_MONTH ?? env.STRIPE_PRICE_ID, priceYear: env.STRIPE_PRICE_YEAR, apiVersion: env.STRIPE_API_VERSION },
|
|
19823
19874
|
mantisDir: env.MANTIS_DIR,
|
|
19824
19875
|
osvMode: env.OSV_MODE,
|
|
19825
19876
|
skillTokenBudget: env.SKILL_TOKEN_BUDGET,
|
|
19826
|
-
/** Largest
|
|
19827
|
-
answerCharBudget:
|
|
19877
|
+
/** Largest tool answer, in characters, per channel (text block, and structuredContent as JSON). A host that forwards both channels sees twice this, so 30,000 keeps the worst case at about 15K tokens, under Claude Code's 25K-token tool-result limit (D-189, D-308). Skills that do not fit are served in parts (D-309). */
|
|
19878
|
+
answerCharBudget: env.ANSWER_CHAR_BUDGET,
|
|
19879
|
+
/** One limit for the threat-model text: stage contract, tool schema and refusal message (036, FR-029). */
|
|
19880
|
+
threatModelMaxChars: 6e3,
|
|
19828
19881
|
maxResearchTasks: env.MAX_RESEARCH_TASKS,
|
|
19829
19882
|
maxInvestigations: env.MAX_INVESTIGATIONS,
|
|
19830
19883
|
sweep: { enabled: env.SWEEP === "on", filesPerTask: Math.max(1, env.SWEEP_FILES_PER_TASK) },
|
|
19831
19884
|
spotChecks: Math.max(0, env.SPOT_CHECKS),
|
|
19885
|
+
spotChecksMax: Math.max(0, env.SPOT_CHECKS_MAX),
|
|
19886
|
+
/** Coverage engine budgets (037, D-320): they grow with the reviewable files and stop at these ceilings. */
|
|
19887
|
+
coverage: { riskStepsMax: Math.max(0, env.RISK_STEPS_MAX), stepFiles: Math.max(1, env.STEP_FILES), stepKb: Math.max(10, env.STEP_KB), sweepFilesMax: Math.max(0, env.SWEEP_FILES_MAX), largeFileKb: Math.max(1, env.LARGE_FILE_KB), investigationsCeiling: Math.max(1, env.MAX_INVESTIGATIONS_CEILING) },
|
|
19832
19888
|
inventoryMax: Math.max(1, env.INVENTORY_MAX),
|
|
19889
|
+
inventoryOmittedMax: Math.max(1, env.INVENTORY_OMITTED_MAX),
|
|
19890
|
+
/** File list fetched by the server from GitHub for public repositories, metadata only, no token (038, D-330). "fake" serves registered trees only (tests, local runs), "off" never fetches. */
|
|
19891
|
+
tree: { mode: env.TREE_MODE, timeoutMs: Math.max(500, env.TREE_TIMEOUT_MS) },
|
|
19892
|
+
/** Largest number of packages in one dependency report (038, D-332). */
|
|
19893
|
+
depsPerReport: Math.max(50, env.DEPS_PER_REPORT),
|
|
19894
|
+
/** Collector upload of a deep scan (039): one-time code lifetime and the size limit of a bundle. Count limits live with the bundle schema. */
|
|
19895
|
+
collect: { codeTtlMin: Math.max(1, env.COLLECT_CODE_TTL_MIN), maxBytes: Math.max(1e4, env.COLLECT_MAX_BYTES) },
|
|
19896
|
+
/** Every file that needs review gets a step (039, D-342, D-348). "off" restores the size-based budgets of 037. Steps are sized by lines when the collector counted them. */
|
|
19897
|
+
fullCoverage: { enabled: env.FULL_COVERAGE === "on", stepLines: Math.max(100, env.STEP_LINES), stepFiles: Math.max(1, env.COVER_STEP_FILES) },
|
|
19898
|
+
/** The check on the user's machine after triage (041, D-364): on when the scan has a collector upload; its one-time code lifetime. */
|
|
19899
|
+
proof: { enabled: env.PROOF_CHECK === "on", codeTtlMin: Math.max(1, env.PROOF_CODE_TTL_MIN) },
|
|
19900
|
+
/** How often a review step reopens for files left unread without an accepted reason (041, D-369); 0 restores the 039 behaviour. */
|
|
19901
|
+
reviewReopenMax: Math.max(0, env.REVIEW_REOPEN_MAX),
|
|
19833
19902
|
/** Local scanning (feature 013). Which engines run per plan is configuration (FR-017); safety basics stay on for every plan. */
|
|
19834
19903
|
localScans: {
|
|
19835
19904
|
engines: { secrets: true, deps: true, code: true, config: true },
|
|
@@ -19879,17 +19948,67 @@ var config2 = {
|
|
|
19879
19948
|
var SERVICES = ["supabase", "firebase", "vercel", "neon", "stripe", "clerk", "auth0", "cloudflare", "netlify", "railway", "render", "upstash", "aws", "azure", "gcloud", "kubernetes"];
|
|
19880
19949
|
|
|
19881
19950
|
// src/domain/mask.ts
|
|
19882
|
-
var
|
|
19951
|
+
var M = "\u2026[masked]";
|
|
19952
|
+
var cut = (t, n = 4) => `${t.slice(0, n)}${M}`;
|
|
19953
|
+
var TECH = /^(?:(?:sha(?:1|224|256|384|512)|base(?:32|58|64)|oauth2|ed25519|x25519|x509|utf(?:8|16)|md5|argon2|pbkdf2|bcrypt|scrypt|http[23]|ipv[46]|aes(?:128|192|256)|rsa(?:2048|3072|4096)|pkcs\d{1,2}|i18n|l10n|a11y|k8s|e2e|es20\d\d|[hrep]s(?:256|384|512))[a-z]{0,12}|utf8mb4|s3|ec2|es\d|tls1|v\d{1,3}|p\d{2,3}|\d{1,6}|\d{1,3}[a-z]{1,2})$/i;
|
|
19954
|
+
var SEP = /[-_.+=/:@]+/;
|
|
19955
|
+
var CRED_PREFIX = /^(?:whsec|sk|pk|rk|gh[pousr]|github_pat|glpat|xox[baprs]|npm|AKIA|ASIA|AIza|SG|eyJ)[_\-.]/;
|
|
19956
|
+
function camelWordy(s) {
|
|
19957
|
+
if (!/^[A-Za-z]+$/.test(s)) return false;
|
|
19958
|
+
if (s === s.toLowerCase() || s === s.toUpperCase()) return true;
|
|
19959
|
+
const words = s.match(/[A-Z]?[a-z]+|[A-Z]+(?![a-z])/g) ?? [];
|
|
19960
|
+
return words.filter((w) => w.length === 1).length <= 1 && words.length <= Math.max(1, Math.floor(s.length / 3));
|
|
19961
|
+
}
|
|
19962
|
+
function wordy(t) {
|
|
19963
|
+
if (CRED_PREFIX.test(t)) return false;
|
|
19964
|
+
const segs = t.split(SEP).filter(Boolean);
|
|
19965
|
+
return segs.length > 0 && segs.every((s) => TECH.test(s) || camelWordy(s));
|
|
19966
|
+
}
|
|
19967
|
+
function pinnedId(t, off, str) {
|
|
19968
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t)) return /\bfinding(?:\s+id)?\s*[:#]?\s*[`'"]?$/i.test(str.slice(Math.max(0, off - 16), off));
|
|
19969
|
+
if (!/^[0-9a-f]{7,64}$/.test(t)) return false;
|
|
19970
|
+
return /(?:@|sha(?:256|512):|\b(?:commit|sha|digest|blob|tree|revision|rev|pinned to|pin to|pin)\s*[:=]?\s*[`'"]?)$/i.test(str.slice(Math.max(0, off - 24), off));
|
|
19971
|
+
}
|
|
19972
|
+
function valueLike(v) {
|
|
19973
|
+
if (v.includes(M)) return false;
|
|
19974
|
+
if (/\d/.test(v) && /[A-Za-z]/.test(v)) return !wordy(v);
|
|
19975
|
+
if (/[!#$%^&*~@]/.test(v) && !/^[\w.+-]+@[\w-]+\.[\w.-]+$/.test(v)) return true;
|
|
19976
|
+
if (/^[A-Za-z]{12,}$/.test(v)) return !camelWordy(v);
|
|
19977
|
+
return v.length >= 20 && !wordy(v);
|
|
19978
|
+
}
|
|
19979
|
+
var KNOWN_TOKENS = /(?<![A-Za-z0-9])(?:AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{3,}|sk_(?:live|test)_[A-Za-z0-9]{8,}|sk-[A-Za-z0-9_-]{20,}|eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}|SG\.[A-Za-z0-9_-]{10,}|glpat-[A-Za-z0-9_-]{16,}|npm_[A-Za-z0-9]{16,}|whsec_[A-Za-z0-9]{16,}|r[ks]_(?:live|test)_[A-Za-z0-9]{8,}|pk_live_[A-Za-z0-9]{8,}|AIza[0-9A-Za-z_-]{30,})|(?<![A-Za-z0-9_])[a-z]{30,}(?![A-Za-z0-9_])/g;
|
|
19980
|
+
var KEYWORD = /^(?:string|number|boolean|bool|int|str|true|false|null|undefined|none|nil|any|unknown|void|never|required|optional|missing|value|values|redacted|\*{2,}|\d{1,7})$/i;
|
|
19981
|
+
var CODE_REF = /^[A-Za-z_$][\w$]*(?:\??\.[\w$]+)+\(?$|[([<{]|^\$/;
|
|
19883
19982
|
function maskAlways(s) {
|
|
19884
|
-
return s.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g, "-----BEGIN PRIVATE KEY-----\u2026[masked]-----END PRIVATE KEY-----").replace(/\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+\/=-]{8,}/gi, (
|
|
19885
|
-
const q2 = /^["'`]/.test(v) ? v[0] : "";
|
|
19886
|
-
|
|
19887
|
-
|
|
19983
|
+
return s.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g, "-----BEGIN PRIVATE KEY-----\u2026[masked]-----END PRIVATE KEY-----").replace(/\b(Bearer|Basic|Token)\s+([A-Za-z0-9._~+\/=-]{8,})/gi, (m, a, v) => valueLike(v) ? `${a} ${M}` : m).replace(/\b(passw(?:or)?d|passphrase|secret|token|api[ _-]?key|key)\s+(?:is|was)\s+(["'`]?)([^\s"'`,;)]{4,})/gi, (m, a, q2, v) => valueLike(v) || q2 && (/^pass/i.test(a) || !wordy(v)) ? `${a} is ${M}` : m).replace(/\b(secret|password|passphrase|token|pw)\s+(?=[^\s"'`,;)]*\d)([^\s"'`,;)]{6,})/gi, (m, a, v) => wordy(v) || /^\d{1,4}(?:[-/.:]\d{1,4})+$/.test(v) ? m : `${a} ${M}`).replace(KNOWN_TOKENS, (t) => cut(t)).replace(/(:\/\/[^\/\s:@]+:)([^@\s\/]+)(@)/g, (_m, a, _v, c) => `${a}${M}${c}`).replace(/((?:key|secret|passw(?:or)?d|pwd|pw|pass|token|credential)[A-Za-z0-9_]{0,80}["']?\s{0,20}(?:[=!]==?|[:=])\s{0,20})("[^"\n]*"|'[^'\n]*'|`[^`\n]*`|[^\s"'`,;)}]{3,})/gi, (m, a, v, off, str) => {
|
|
19984
|
+
const q2 = /^["'`]/.test(v) ? v[0] : "", inner = q2 ? v.slice(1, -1) : v;
|
|
19985
|
+
if (inner.includes(M)) return m;
|
|
19986
|
+
if (!q2 && (KEYWORD.test(inner.replace(/\.+$/, "")) || CODE_REF.test(inner))) return m;
|
|
19987
|
+
const word = a.match(/^[A-Za-z0-9_]+/)?.[0] ?? "", pre = str.slice(0, off).slice(-1);
|
|
19988
|
+
const inIdentifier = /[_0-9]/.test(pre) || /[a-z]/.test(pre) && /^[A-Z]/.test(word) || /[A-Z]/.test(pre) && word === word.toUpperCase();
|
|
19989
|
+
const loneWord = /^(?:keys?|pass)$/i.test(word) && !inIdentifier;
|
|
19990
|
+
if (loneWord && !valueLike(inner)) return m;
|
|
19991
|
+
return `${a}${q2}${inner.slice(0, 2)}${M}${q2}`;
|
|
19992
|
+
}).replace(/(?<![A-Za-z0-9_\/.+])(?=[A-Za-z0-9\/+]*[A-Z])(?=[A-Za-z0-9\/+]*[a-z])(?=[A-Za-z0-9\/+]*[\d\/+])[A-Za-z0-9\/+]{40}(?![A-Za-z0-9_\/.+])/g, (t) => wordy(t) ? t : cut(t)).replace(/(?<![A-Za-z0-9_\-])[A-Z0-9]{4,}(?:-[A-Z0-9]{4,}){2,}(?![A-Za-z0-9_\-])/g, (t, off, str) => {
|
|
19993
|
+
const g = t.split("-");
|
|
19994
|
+
if (/\d/.test(t)) return g.every((x) => x.length === g[0].length) || !wordy(t) ? cut(t) : t;
|
|
19995
|
+
return /\b(?:secret|key|token|passw(?:or)?d|licen[cs]e|serial|credential)s?\W{0,3}(?:is\W{0,2})?$/i.test(str.slice(Math.max(0, off - 24), off)) ? cut(t) : t;
|
|
19996
|
+
}).replace(/(?<![A-Za-z0-9_\-+=/.])(?=[A-Za-z0-9_+=]*\d)(?=[A-Za-z0-9_+=]*[A-Za-z])[A-Za-z0-9_+=]{16,19}(?![A-Za-z0-9_\-+=/.])/g, (t, off, str) => wordy(t) || pinnedId(t, off, str) ? t : cut(t)).replace(/(?<![A-Za-z0-9_\-+=/.])(?=[A-Za-z0-9_\-+=]*\d)(?=[A-Za-z0-9_\-+=]*[A-Za-z])[A-Za-z0-9_\-+=]{20,}(?![A-Za-z0-9_\-+=/.])/g, (t, off, str) => wordy(t) || pinnedId(t, off, str) ? t : cut(t)).replace(/(?<![A-Za-z0-9_\-+=/.:@])(?=[A-Za-z0-9_]*\d)(?=[A-Za-z0-9_]*[A-Za-z])[A-Za-z0-9_]{8,}(?![A-Za-z0-9_\-+=/.:@]|…)/g, (t, off, str) => /^[0-9a-f]{8,19}$/.test(t) || wordy(t) || pinnedId(t, off, str) ? t : `${t.slice(0, 2)}${M}`);
|
|
19997
|
+
}
|
|
19998
|
+
function nameLike(v) {
|
|
19999
|
+
const words = v.replace(/\$\{\{[^}]*\}\}|\$\{[^}]*\}|\$[A-Za-z_][A-Za-z0-9_]*|\$\d+|\?/g, " ").trim().split(/[\s=,;()*<>]+/).map((w) => w.replace(/^[([{<]+|[)\]}>.,:;!?]+$/g, "")).filter(Boolean);
|
|
20000
|
+
return words.every((w) => wordy(w));
|
|
19888
20001
|
}
|
|
19889
20002
|
function maskExcerpt(s) {
|
|
19890
20003
|
return maskAlways(s).split("\n").map((line) => {
|
|
19891
20004
|
const secretLine = /key|secret|pass|pwd|token|cred|auth/i.test(line);
|
|
19892
|
-
return line.replace(/(["'`])([^"'`\n]*)\1/g, (m, q2, v
|
|
20005
|
+
return line.replace(/(["'`])([^"'`\n]*)\1/g, (m, q2, v, off, str) => {
|
|
20006
|
+
if (v.includes(M) || v.length < 3) return m;
|
|
20007
|
+
const SECRET_ID = "[\\w.$\\]\\[]*(?:passw(?:or)?d|passphrase|pwd|pw|pass|secret|token|key|credential)\\w*";
|
|
20008
|
+
const compared = new RegExp(`${SECRET_ID}\\s*(?:[=!]==?)\\s*$`, "i").test(str.slice(0, off)) || new RegExp(`^\\s*(?:[=!]==?)\\s*${SECRET_ID}`, "i").test(str.slice(off + m.length));
|
|
20009
|
+
const hit = secretLine ? compared || !nameLike(v) : /^\S{12,}$/.test(v) && !/[\/.]/.test(v) && !nameLike(v);
|
|
20010
|
+
return hit ? `${q2}${v.slice(0, 2)}${M}${q2}` : m;
|
|
20011
|
+
});
|
|
19893
20012
|
}).join("\n");
|
|
19894
20013
|
}
|
|
19895
20014
|
|
|
@@ -20944,39 +21063,45 @@ var sev = (s) => {
|
|
|
20944
21063
|
const v = (s ?? "").toUpperCase();
|
|
20945
21064
|
return v === "CRITICAL" ? "CRITICAL" : v === "HIGH" ? "HIGH" : v === "LOW" ? "LOW" : "MEDIUM";
|
|
20946
21065
|
};
|
|
20947
|
-
var
|
|
20948
|
-
|
|
20949
|
-
|
|
20950
|
-
|
|
20951
|
-
|
|
20952
|
-
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
|
|
20961
|
-
|
|
20962
|
-
|
|
20963
|
-
|
|
20964
|
-
vulns
|
|
20965
|
-
|
|
20966
|
-
|
|
20967
|
-
|
|
20968
|
-
|
|
20969
|
-
|
|
20970
|
-
|
|
20971
|
-
|
|
20972
|
-
|
|
21066
|
+
var OSV_BATCH = 1e3;
|
|
21067
|
+
function osvSourceWith(fetchFn = fetch) {
|
|
21068
|
+
return {
|
|
21069
|
+
async lookup(deps) {
|
|
21070
|
+
const out = /* @__PURE__ */ new Map();
|
|
21071
|
+
let budget = 25;
|
|
21072
|
+
for (let at = 0; at < deps.length; at += OSV_BATCH) {
|
|
21073
|
+
const list = deps.slice(at, at + OSV_BATCH);
|
|
21074
|
+
const res2 = await fetchFn("https://api.osv.dev/v1/querybatch", {
|
|
21075
|
+
method: "POST",
|
|
21076
|
+
headers: { "content-type": "application/json" },
|
|
21077
|
+
signal: AbortSignal.timeout(2e4),
|
|
21078
|
+
body: JSON.stringify({ queries: list.map((d) => ({ package: { name: d.name, ecosystem: d.ecosystem ?? "npm" }, version: d.version })) })
|
|
21079
|
+
});
|
|
21080
|
+
if (!res2.ok) throw new Error(`OSV ${res2.status}`);
|
|
21081
|
+
const body = await res2.json();
|
|
21082
|
+
for (let i = 0; i < list.length; i++) {
|
|
21083
|
+
const vulns = [];
|
|
21084
|
+
for (const { id } of (body.results[i]?.vulns ?? []).slice(0, 3)) {
|
|
21085
|
+
if (budget-- <= 0) {
|
|
21086
|
+
vulns.push({ id, summary: "Known vulnerability", severity: "MEDIUM" });
|
|
21087
|
+
continue;
|
|
21088
|
+
}
|
|
21089
|
+
try {
|
|
21090
|
+
const d = await (await fetchFn(`https://api.osv.dev/v1/vulns/${encodeURIComponent(id)}`, { signal: AbortSignal.timeout(8e3) })).json();
|
|
21091
|
+
const fixed = d.affected?.flatMap((a) => a.ranges?.flatMap((r) => r.events?.map((e) => e.fixed).filter(Boolean) ?? []) ?? [])[0];
|
|
21092
|
+
vulns.push({ id, summary: String(d.summary ?? d.details ?? "Known vulnerability").slice(0, 200), severity: sev(d.database_specific?.severity), fixedIn: fixed });
|
|
21093
|
+
} catch {
|
|
21094
|
+
vulns.push({ id, summary: "Known vulnerability", severity: "MEDIUM" });
|
|
21095
|
+
}
|
|
21096
|
+
}
|
|
21097
|
+
if (vulns.length) out.set(depKey(list[i]), vulns);
|
|
20973
21098
|
}
|
|
20974
21099
|
}
|
|
20975
|
-
|
|
21100
|
+
return out;
|
|
20976
21101
|
}
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
|
|
21102
|
+
};
|
|
21103
|
+
}
|
|
21104
|
+
var osvSource = osvSourceWith();
|
|
20980
21105
|
var fakeVulnSource = {
|
|
20981
21106
|
async lookup(deps) {
|
|
20982
21107
|
const out = /* @__PURE__ */ new Map();
|