seaworthycode 1.2.3 → 1.2.4
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/index.js +150 -26
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/wasm/tree-sitter-sql.wasm +0 -0
package/dist/index.js
CHANGED
|
@@ -6,10 +6,10 @@ import { existsSync as existsSync6 } from "fs";
|
|
|
6
6
|
import { dirname as dirname7, resolve as resolve4 } from "path";
|
|
7
7
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8
8
|
import { cac } from "cac";
|
|
9
|
-
import { createRequire as
|
|
9
|
+
import { createRequire as createRequire4 } from "module";
|
|
10
10
|
|
|
11
11
|
// src/cli-action.ts
|
|
12
|
-
import { stat, access as access2 } from "fs/promises";
|
|
12
|
+
import { stat, access as access2, appendFile, mkdir } from "fs/promises";
|
|
13
13
|
import { resolve as resolve3 } from "path";
|
|
14
14
|
import { homedir as homedir3 } from "os";
|
|
15
15
|
import { join as join15 } from "path";
|
|
@@ -93,6 +93,7 @@ var CACHE_DIR = join(CONFIG_DIR, "cache");
|
|
|
93
93
|
var AST_CACHE_DIR = join(CACHE_DIR, "ast");
|
|
94
94
|
var DEP_CACHE_DIR = join(CACHE_DIR, "deps");
|
|
95
95
|
var LLM_CACHE_DIR = join(CACHE_DIR, "llm");
|
|
96
|
+
var LOG_DIR = join(CONFIG_DIR, "logs");
|
|
96
97
|
var VALID_CATEGORIES = /* @__PURE__ */ new Set([
|
|
97
98
|
"security",
|
|
98
99
|
"resilience",
|
|
@@ -400,11 +401,11 @@ async function crawl(options) {
|
|
|
400
401
|
const ext2 = entry.name.includes(".") ? entry.name.slice(entry.name.lastIndexOf(".")) : "";
|
|
401
402
|
if (SKIP_EXTENSIONS.has(ext2)) continue;
|
|
402
403
|
try {
|
|
403
|
-
const
|
|
404
|
-
if (!
|
|
405
|
-
if (
|
|
406
|
-
const { isBinary
|
|
407
|
-
if (
|
|
404
|
+
const stat2 = await fs.stat(fullPath);
|
|
405
|
+
if (!stat2.isFile()) continue;
|
|
406
|
+
if (stat2.size > maxFileSize) continue;
|
|
407
|
+
const { isBinary, isGenerated } = await peekFileHead(fullPath);
|
|
408
|
+
if (isBinary || isGenerated) continue;
|
|
408
409
|
if (await shouldSkipMinifiedJs(fullPath, entry.name)) continue;
|
|
409
410
|
files.push({
|
|
410
411
|
path: fullPath,
|
|
@@ -420,17 +421,20 @@ async function crawl(options) {
|
|
|
420
421
|
if (ig.ignores(relPath)) continue;
|
|
421
422
|
const ext = entry.name.includes(".") ? entry.name.slice(entry.name.lastIndexOf(".")) : "";
|
|
422
423
|
if (SKIP_EXTENSIONS.has(ext)) continue;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
424
|
+
try {
|
|
425
|
+
const stat2 = await fs.stat(fullPath);
|
|
426
|
+
if (stat2.size > maxFileSize) continue;
|
|
427
|
+
const { isBinary, isGenerated } = await peekFileHead(fullPath);
|
|
428
|
+
if (isBinary || isGenerated) continue;
|
|
429
|
+
if (await shouldSkipMinifiedJs(fullPath, entry.name)) continue;
|
|
430
|
+
files.push({
|
|
431
|
+
path: fullPath,
|
|
432
|
+
relativePath: relPath,
|
|
433
|
+
content: () => fs.readFile(fullPath, "utf-8"),
|
|
434
|
+
language: inferLanguage(entry.name)
|
|
435
|
+
});
|
|
436
|
+
} catch {
|
|
437
|
+
}
|
|
434
438
|
}
|
|
435
439
|
}
|
|
436
440
|
await walk(targetDir);
|
|
@@ -779,10 +783,12 @@ var copy = {
|
|
|
779
783
|
"report.tierWatermark": (licensedTo) => `\u2693 Pro \xB7 ${licensedTo}`,
|
|
780
784
|
"report.scanProgress.starting": (targetDir) => `\u2693 Scanning ${targetDir}...`,
|
|
781
785
|
"report.scanProgress.category": (label, count) => count > 0 ? ` \u2693 ${label} \u2014 ${count} issue(s)` : ` \u2693 ${label} \u2713`,
|
|
786
|
+
"report.licenseNotice": (licensedTo) => `Licensed to ${licensedTo}`,
|
|
782
787
|
"report.cta.clean": `\u2713 All clear \u2014 your project is shipshape!`,
|
|
783
788
|
"report.cta.hasFindings": `Fix these before you ship. ${SITE_URL}/docs`,
|
|
784
789
|
"report.cta.critical": `\u26A0 Critical issues found \u2014 patch before you deploy. ${SITE_URL}/docs`,
|
|
785
790
|
"report.proUpsellNamed": (names, count) => `\u2693 Pro unlocks ${count} more check${count === 1 ? "" : "s"} (${names}). ${SITE_URL}`,
|
|
791
|
+
"report.preScanNotice": (count) => `\u2693 Pro unlocks ${count} additional checks. Upgrade at ${SITE_URL}`,
|
|
786
792
|
"report.llmPrivacyWarning": "Pro checks will send code context to your LLM provider via your API key. No data is sent to Seaworthy servers.",
|
|
787
793
|
"report.confidenceHigh": "high confidence",
|
|
788
794
|
"report.confidenceMedium": "medium confidence",
|
|
@@ -7655,7 +7661,7 @@ registry.register({
|
|
|
7655
7661
|
}
|
|
7656
7662
|
});
|
|
7657
7663
|
var CHECK_ID10 = "security.xss-surface";
|
|
7658
|
-
var CGI_PATH_HINT =
|
|
7664
|
+
var CGI_PATH_HINT = new RegExp("(^|[/_-])cgi([/_.-]|$)", "i");
|
|
7659
7665
|
function isLikelyCgiBashScript(file) {
|
|
7660
7666
|
return CGI_PATH_HINT.test(file.relativePath);
|
|
7661
7667
|
}
|
|
@@ -15842,7 +15848,7 @@ var copy3 = {
|
|
|
15842
15848
|
"cli.setup.apiKeyPrompt": "API key for",
|
|
15843
15849
|
"cli.setup.modelPrompt": "Model (press Enter for default)",
|
|
15844
15850
|
"cli.setup.baseUrlPrompt": "Custom API base URL (press Enter to use default)",
|
|
15845
|
-
"cli.setup.done": "Config saved. Run `
|
|
15851
|
+
"cli.setup.done": "Config saved. Run `npx seaworthycode .` to scan your project.",
|
|
15846
15852
|
"cli.setup.skipped": "Setup skipped. Running free-tier scan.",
|
|
15847
15853
|
"cli.firstRun": "First time? Let's get you set up.",
|
|
15848
15854
|
"cli.category.security": "Security",
|
|
@@ -15868,7 +15874,19 @@ var copy3 = {
|
|
|
15868
15874
|
"cli.error.config.unknownKey": "Unknown config key. Valid keys: licenseKey, llmProvider, llmApiKey, llmModel, llmApiUrl",
|
|
15869
15875
|
"cli.error.config.invalidProvider": "Invalid provider. Valid providers: anthropic, openai, kimi, moonshot, deepseek, gemini, ollama, openrouter",
|
|
15870
15876
|
"cli.error.config.invalidAction": "Invalid config action. Usage: seaworthy config <set|get|list>",
|
|
15871
|
-
"cli.config.setSuccess": (key) => `${key} updated
|
|
15877
|
+
"cli.config.setSuccess": (key) => `${key} updated.`,
|
|
15878
|
+
"cli.licenseNotice": (licensedTo) => `Licensed to ${licensedTo}`,
|
|
15879
|
+
"cli.error.debugHint": (logPath) => `Re-run with --debug to write a diagnostic log: ${logPath}
|
|
15880
|
+
Report persistent issues: npx seaworthycode bugreport`,
|
|
15881
|
+
"cli.bugreport.intro": "Seaworthy Bug Report \u2014 send an anonymized diagnostic to the Seaworthy team.",
|
|
15882
|
+
"cli.bugreport.noLog": (logFile) => `No diagnostic log found at ${logFile}.
|
|
15883
|
+
Re-run the failing command with --debug first, then run bugreport again.`,
|
|
15884
|
+
"cli.bugreport.preview": "The following diagnostic data will be sent (home directory paths have been anonymized):",
|
|
15885
|
+
"cli.bugreport.confirm": "Send this report to the Seaworthy team?",
|
|
15886
|
+
"cli.bugreport.cancelled": "Cancelled \u2014 no data was sent.",
|
|
15887
|
+
"cli.bugreport.sent": (eventId) => `Report sent. Reference ID: ${eventId}
|
|
15888
|
+
Mention this ID when contacting support at ${SITE_URL}/support`,
|
|
15889
|
+
"cli.bugreport.failed": "Could not reach the error reporting service. Check your connection and try again."
|
|
15872
15890
|
};
|
|
15873
15891
|
function getCopy2(key, ...args) {
|
|
15874
15892
|
const value = copy3[key];
|
|
@@ -15893,7 +15911,7 @@ function createCommand(definition, options) {
|
|
|
15893
15911
|
return {
|
|
15894
15912
|
ok: false,
|
|
15895
15913
|
code: "errors.unexpected",
|
|
15896
|
-
detail: err instanceof Error ? err.message : String(err)
|
|
15914
|
+
detail: err instanceof Error ? err.stack ?? err.message : String(err)
|
|
15897
15915
|
};
|
|
15898
15916
|
}
|
|
15899
15917
|
}
|
|
@@ -15955,6 +15973,11 @@ async function executeScan(args, ctx) {
|
|
|
15955
15973
|
}
|
|
15956
15974
|
const showProgress = args.format === "terminal" && !args.sarifStdout;
|
|
15957
15975
|
if (showProgress) {
|
|
15976
|
+
if (ctx.tier === "free") {
|
|
15977
|
+
const skippedCount = registry.getSkippedChecks("free").length;
|
|
15978
|
+
process.stderr.write(getCopy("report.preScanNotice", skippedCount) + "\n");
|
|
15979
|
+
process.stderr.write(getCopy2("cli.licenseNotice", ctx.licensedTo ?? "free tier") + "\n");
|
|
15980
|
+
}
|
|
15958
15981
|
process.stderr.write(getCopy("report.scanProgress.starting", args.targetDir) + "\n");
|
|
15959
15982
|
}
|
|
15960
15983
|
const runner = new ScanRunner();
|
|
@@ -16340,7 +16363,21 @@ async function runCli(options) {
|
|
|
16340
16363
|
return { code: resolveExitCode("scan.failed"), error: message };
|
|
16341
16364
|
}
|
|
16342
16365
|
if (!result.ok) {
|
|
16343
|
-
|
|
16366
|
+
let message = resolveErrorMessage(result.code);
|
|
16367
|
+
if (result.code === "errors.unexpected") {
|
|
16368
|
+
const logFile = join15(LOG_DIR, "seaworthy.log");
|
|
16369
|
+
message += "\n" + getCopy2("cli.error.debugHint", logFile);
|
|
16370
|
+
if (options.debug && result.detail) {
|
|
16371
|
+
try {
|
|
16372
|
+
await mkdir(LOG_DIR, { recursive: true });
|
|
16373
|
+
await appendFile(logFile, `[${(/* @__PURE__ */ new Date()).toISOString()}] unexpected error
|
|
16374
|
+
${result.detail}
|
|
16375
|
+
|
|
16376
|
+
`);
|
|
16377
|
+
} catch {
|
|
16378
|
+
}
|
|
16379
|
+
}
|
|
16380
|
+
}
|
|
16344
16381
|
return { code: resolveExitCode(result.code), error: message };
|
|
16345
16382
|
}
|
|
16346
16383
|
const remaining = Math.max(0, 3e3 - (Date.now() - updateStart));
|
|
@@ -16405,13 +16442,89 @@ var configCommand = createCommand(
|
|
|
16405
16442
|
{ requireLicense: false }
|
|
16406
16443
|
);
|
|
16407
16444
|
|
|
16445
|
+
// src/commands/bugreport.ts
|
|
16446
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
16447
|
+
import { randomUUID } from "crypto";
|
|
16448
|
+
import { platform, arch, homedir as homedir4 } from "os";
|
|
16449
|
+
import { join as join16 } from "path";
|
|
16450
|
+
import { createRequire as createRequire3 } from "module";
|
|
16451
|
+
import { intro as intro2, outro as outro2, confirm, isCancel as isCancel2 } from "@clack/prompts";
|
|
16452
|
+
var _require2 = createRequire3(import.meta.url);
|
|
16453
|
+
var { version: CLI_VERSION2 } = _require2("../../package.json");
|
|
16454
|
+
var LOG_FILE = join16(LOG_DIR, "seaworthy.log");
|
|
16455
|
+
var SENTRY_PROJECT_ID = "4511394796666960";
|
|
16456
|
+
var SENTRY_INGEST = "https://o4508597942026240.ingest.de.sentry.io";
|
|
16457
|
+
var SENTRY_KEY = "3c8dc912769032c73a306cb571805658";
|
|
16458
|
+
function sanitize(text3) {
|
|
16459
|
+
return text3.split(homedir4()).join("<home>");
|
|
16460
|
+
}
|
|
16461
|
+
async function sendReport(log) {
|
|
16462
|
+
const eventId = randomUUID().replace(/-/g, "");
|
|
16463
|
+
const payload = {
|
|
16464
|
+
event_id: eventId,
|
|
16465
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16466
|
+
platform: "node",
|
|
16467
|
+
level: "error",
|
|
16468
|
+
environment: "cli",
|
|
16469
|
+
release: CLI_VERSION2,
|
|
16470
|
+
message: log.split("\n")[0] ?? "Unexpected error",
|
|
16471
|
+
extra: { log },
|
|
16472
|
+
contexts: {
|
|
16473
|
+
runtime: { name: "node", version: process.version },
|
|
16474
|
+
os: { name: platform(), arch: arch() }
|
|
16475
|
+
},
|
|
16476
|
+
tags: { source: "cli-bugreport" }
|
|
16477
|
+
};
|
|
16478
|
+
try {
|
|
16479
|
+
const res = await fetch(`${SENTRY_INGEST}/api/${SENTRY_PROJECT_ID}/store/`, {
|
|
16480
|
+
method: "POST",
|
|
16481
|
+
headers: {
|
|
16482
|
+
"Content-Type": "application/json",
|
|
16483
|
+
"X-Sentry-Auth": `Sentry sentry_version=7, sentry_key=${SENTRY_KEY}, sentry_client=seaworthycode/${CLI_VERSION2}`
|
|
16484
|
+
},
|
|
16485
|
+
body: JSON.stringify(payload),
|
|
16486
|
+
signal: AbortSignal.timeout(1e4)
|
|
16487
|
+
});
|
|
16488
|
+
return res.ok ? eventId : null;
|
|
16489
|
+
} catch {
|
|
16490
|
+
return null;
|
|
16491
|
+
}
|
|
16492
|
+
}
|
|
16493
|
+
async function executeBugReport() {
|
|
16494
|
+
intro2(getCopy2("cli.bugreport.intro"));
|
|
16495
|
+
let log;
|
|
16496
|
+
try {
|
|
16497
|
+
const raw = await readFile2(LOG_FILE, "utf-8");
|
|
16498
|
+
if (!raw.trim()) throw new Error("empty");
|
|
16499
|
+
log = sanitize(raw.length > 8192 ? raw.slice(-8192) : raw);
|
|
16500
|
+
} catch {
|
|
16501
|
+
return { ok: true, output: getCopy2("cli.bugreport.noLog", LOG_FILE) };
|
|
16502
|
+
}
|
|
16503
|
+
process.stderr.write(getCopy2("cli.bugreport.preview") + "\n---\n" + log + "---\n\n");
|
|
16504
|
+
const confirmed = await confirm({ message: getCopy2("cli.bugreport.confirm") });
|
|
16505
|
+
if (isCancel2(confirmed) || !confirmed) {
|
|
16506
|
+
outro2(getCopy2("cli.bugreport.cancelled"));
|
|
16507
|
+
return { ok: true };
|
|
16508
|
+
}
|
|
16509
|
+
const eventId = await sendReport(log);
|
|
16510
|
+
outro2(eventId ? getCopy2("cli.bugreport.sent", eventId) : getCopy2("cli.bugreport.failed"));
|
|
16511
|
+
return { ok: true };
|
|
16512
|
+
}
|
|
16513
|
+
var bugReportCommand = createCommand(
|
|
16514
|
+
{
|
|
16515
|
+
name: "bugreport",
|
|
16516
|
+
description: "Send an anonymized bug report to the Seaworthy team",
|
|
16517
|
+
handler: async () => executeBugReport()
|
|
16518
|
+
},
|
|
16519
|
+
{ requireLicense: false }
|
|
16520
|
+
);
|
|
16521
|
+
|
|
16408
16522
|
// src/index.ts
|
|
16409
|
-
loadEnv({ path: resolve4(process.cwd(), ".env") });
|
|
16410
16523
|
var repoRootEnv = resolve4(dirname7(fileURLToPath3(import.meta.url)), "../../..", ".env");
|
|
16411
16524
|
if (existsSync6(repoRootEnv)) {
|
|
16412
16525
|
loadEnv({ path: repoRootEnv, override: false });
|
|
16413
16526
|
}
|
|
16414
|
-
var require5 =
|
|
16527
|
+
var require5 = createRequire4(import.meta.url);
|
|
16415
16528
|
var { version } = require5("../package.json");
|
|
16416
16529
|
var cli = cac("seaworthy");
|
|
16417
16530
|
cli.version(version);
|
|
@@ -16451,6 +16564,16 @@ cli.command("setup", "Interactive setup for license key and LLM provider").actio
|
|
|
16451
16564
|
process.exit(2);
|
|
16452
16565
|
}
|
|
16453
16566
|
});
|
|
16567
|
+
cli.command("bugreport", "Send an anonymized bug report to the Seaworthy team").action(async () => {
|
|
16568
|
+
const result = await bugReportCommand.handler({ action: "run" }, { tier: "free", licensedTo: "free tier" });
|
|
16569
|
+
if (!result.ok) {
|
|
16570
|
+
console.error(resolveErrorMessage(result.code));
|
|
16571
|
+
process.exit(2);
|
|
16572
|
+
}
|
|
16573
|
+
if (result.output) {
|
|
16574
|
+
console.log(result.output);
|
|
16575
|
+
}
|
|
16576
|
+
});
|
|
16454
16577
|
cli.help(() => {
|
|
16455
16578
|
return [
|
|
16456
16579
|
{
|
|
@@ -16463,7 +16586,8 @@ cli.help(() => {
|
|
|
16463
16586
|
},
|
|
16464
16587
|
{
|
|
16465
16588
|
title: "Commands",
|
|
16466
|
-
body: ` npx seaworthycode setup
|
|
16589
|
+
body: ` npx seaworthycode setup Interactive setup for license key and LLM provider
|
|
16590
|
+
npx seaworthycode bugreport Send an anonymized bug report to the Seaworthy team`
|
|
16467
16591
|
},
|
|
16468
16592
|
{
|
|
16469
16593
|
title: "Options",
|