@quantakrypto/qscan 0.4.3 → 0.5.0
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/README.md +39 -10
- package/dist/args.d.ts +30 -8
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +38 -5
- package/dist/args.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +18 -9
- package/dist/cli.js.map +1 -1
- package/dist/color.d.ts +38 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +44 -0
- package/dist/color.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +41 -1
- package/dist/config.js.map +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +24 -2
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +90 -20
- package/dist/index.js.map +1 -1
- package/dist/remediate-cli.d.ts +3 -4
- package/dist/remediate-cli.d.ts.map +1 -1
- package/dist/remediate-cli.js +1 -1
- package/dist/remediate-cli.js.map +1 -1
- package/dist/report.d.ts +13 -2
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +59 -6
- package/dist/report.js.map +1 -1
- package/dist/sign.d.ts +4 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +54 -0
- package/dist/sign.js.map +1 -0
- package/dist/triage-run.d.ts +3 -4
- package/dist/triage-run.d.ts.map +1 -1
- package/dist/triage-run.js +13 -6
- package/dist/triage-run.js.map +1 -1
- package/package.json +3 -3
package/dist/remediate-cli.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface RemediateRun {
|
|
|
21
21
|
written: string[];
|
|
22
22
|
}
|
|
23
23
|
/** A verified fix set ready to become a draft PR. */
|
|
24
|
-
|
|
24
|
+
interface DraftPrPlan {
|
|
25
25
|
branch: string;
|
|
26
26
|
title: string;
|
|
27
27
|
body: string;
|
|
@@ -31,7 +31,7 @@ export interface DraftPrPlan {
|
|
|
31
31
|
}[];
|
|
32
32
|
}
|
|
33
33
|
/** Open a draft PR for the plan (injectable; default shells git + gh in a worktree). */
|
|
34
|
-
|
|
34
|
+
type OpenDraftPr = (plan: DraftPrPlan) => Promise<{
|
|
35
35
|
url?: string;
|
|
36
36
|
}>;
|
|
37
37
|
export interface RemediateHooks {
|
|
@@ -52,8 +52,6 @@ export declare const REMEDIATE_EXIT: {
|
|
|
52
52
|
readonly CHANGES: 0;
|
|
53
53
|
readonly ERROR: 2;
|
|
54
54
|
};
|
|
55
|
-
/** Default per-run cap on paid LLM fix proposals (spend/DoS guard; override with --max-llm). */
|
|
56
|
-
export declare const DEFAULT_MAX_LLM = 25;
|
|
57
55
|
/** Minimal unified diff for a localized change (3 lines of context). */
|
|
58
56
|
export declare function unifiedDiff(relPath: string, before: string, after: string): string;
|
|
59
57
|
/** Run a complete qremediate pass. Pure w.r.t. process; the bin prints + exits. */
|
|
@@ -71,4 +69,5 @@ export declare function parseRemediateArgs(argv: readonly string[]): {
|
|
|
71
69
|
message: string;
|
|
72
70
|
};
|
|
73
71
|
export declare const REMEDIATE_HELP = "qremediate \u2014 apply verified codemod fixes (and, with --llm, crypto-verified LLM proposals) for insecure crypto findings\n\nUSAGE\n qremediate [path] [--mode diff|apply|pr] [--llm] [--apply-llm] [--max-llm N]\n [--llm-provider <p>] [--llm-model <m>]\n\nOPTIONS\n --mode diff Print a unified diff of every candidate fix (default; writes nothing)\n --mode apply Write deterministic codemod fixes into the working tree\n (LLM fixes are held back as diffs unless --apply-llm is given)\n --mode pr Commit fixes to a new branch and open a DRAFT PR (never merges)\n --llm Also let a BYOK LLM propose fixes codemods can't (needs an API key)\n --apply-llm In apply mode, also write LLM fixes (only after you've read them)\n --max-llm N Cap paid LLM proposals per run (default 25; spend guard)\n --llm-provider anthropic | openai-compatible (default: anthropic)\n --llm-model Model id for the BYOK provider\n -h, --help Show this help\n -v, --version Show version\n\nEvery fix must clear the verify_fix gate (target finding gone, no new finding) and\nthe patch policy (only files with findings + dependency manifests). Codemod fixes\nare deterministic; LLM fixes are **crypto-verified, not security-reviewed** \u2014 the\ngate proves the crypto is gone, not that the rewrite is safe, and the pipeline\nrejects any LLM patch that adds a network/exec sink or rewrites too much. Review\nLLM diffs before applying. Never merges.\n";
|
|
72
|
+
export {};
|
|
74
73
|
//# sourceMappingURL=remediate-cli.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remediate-cli.d.ts","sourceRoot":"","sources":["../src/remediate-cli.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAgC,MAAM,oBAAoB,CAAC;AAEnG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI7C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,sDAAsD;IACtD,GAAG,EAAE,OAAO,CAAC;IACb;sFACkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,qDAAqD;AACrD,
|
|
1
|
+
{"version":3,"file":"remediate-cli.d.ts","sourceRoot":"","sources":["../src/remediate-cli.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAgC,MAAM,oBAAoB,CAAC;AAEnG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI7C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,sDAAsD;IACtD,GAAG,EAAE,OAAO,CAAC;IACb;sFACkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,qDAAqD;AACrD,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAED,wFAAwF;AACxF,KAAK,WAAW,GAAG,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,2EAA2E;IAC3E,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC9E,qDAAqD;IACrD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,cAAc;;;;CAA2C,CAAC;AAgBvE,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAuBlF;AAmED,mFAAmF;AACnF,wBAAsB,YAAY,CAChC,OAAO,EAAE,gBAAgB,EACzB,KAAK,GAAE,cAAmB,GACzB,OAAO,CAAC,YAAY,CAAC,CAgKvB;AAkDD,6BAA6B;AAC7B,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,MAAM,EAAE,GAErB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAkErC;AAED,eAAO,MAAM,cAAc,y9CAyB1B,CAAC"}
|
package/dist/remediate-cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import { scan, codemodFor, remediateFindings, isManifestFile, fingerprintFinding
|
|
|
15
15
|
const exec = promisify(execFile);
|
|
16
16
|
export const REMEDIATE_EXIT = { OK: 0, CHANGES: 0, ERROR: 2 };
|
|
17
17
|
/** Default per-run cap on paid LLM fix proposals (spend/DoS guard; override with --max-llm). */
|
|
18
|
-
|
|
18
|
+
const DEFAULT_MAX_LLM = 25;
|
|
19
19
|
function envKey(provider) {
|
|
20
20
|
return (process.env.QK_LLM_API_KEY ??
|
|
21
21
|
(provider === "anthropic" ? process.env.ANTHROPIC_API_KEY : process.env.OPENAI_API_KEY));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remediate-cli.js","sourceRoot":"","sources":["../src/remediate-cli.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `qremediate` — apply deterministic, verified fixes to quantum-vulnerable /\n * insecure crypto findings.\n *\n * Pipeline: scan → for each finding, a codemod (then optionally the LLM with\n * `--llm`) proposes a patch → the core remediation pipeline gates it\n * (patch-policy + verify_fix) → verified patches are shown (`diff`), written\n * (`apply`), or opened as a DRAFT PR (`pr`). Never auto-merges.\n */\nimport { readFile as fsReadFile, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport path from \"node:path\";\n\nimport {\n scan,\n codemodFor,\n remediateFindings,\n isManifestFile,\n fingerprintFinding,\n withWorktree,\n} from \"@quantakrypto/core\";\nimport type { Finding, Patch, ScanResult, VerifiedPatch, RejectedPatch } from \"@quantakrypto/core\";\n\nimport type { LlmProvider } from \"./args.js\";\n\nconst exec = promisify(execFile);\n\nexport type RemediateMode = \"diff\" | \"apply\" | \"pr\";\n\nexport interface RemediateOptions {\n path: string;\n mode: RemediateMode;\n /** Use a BYOK LLM to propose fixes codemods can't. */\n llm: boolean;\n /** Actually write LLM-proposed fixes in `apply` mode (they are otherwise shown\n * as diffs and held back — an LLM rewrite must be reviewed, not auto-applied). */\n applyLlm?: boolean;\n /** Cap how many findings are sent to the LLM (spend/DoS guard). */\n maxLlm?: number;\n provider?: LlmProvider;\n model?: string;\n}\n\nexport interface RemediateRun {\n output: string;\n exitCode: number;\n /** Relative paths written (apply mode) or included in the PR. */\n written: string[];\n}\n\n/** A verified fix set ready to become a draft PR. */\nexport interface DraftPrPlan {\n branch: string;\n title: string;\n body: string;\n patches: { path: string; newContent: string }[];\n}\n\n/** Open a draft PR for the plan (injectable; default shells git + gh in a worktree). */\nexport type OpenDraftPr = (plan: DraftPrPlan) => Promise<{ url?: string }>;\n\nexport interface RemediateHooks {\n scanFn?: (root: string) => Promise<ScanResult>;\n readFile?: (abs: string) => Promise<string>;\n writeFile?: (abs: string, content: string) => Promise<void>;\n /** LLM patch source (default wraps `@quantakrypto/agent`'s proposeFix). */\n llmPatchSource?: (finding: Finding, content: string) => Promise<Patch | null>;\n /** Draft-PR backend (default: git worktree + gh). */\n openDraftPr?: OpenDraftPr;\n resolveKey?: () => string | undefined;\n stderr?: (s: string) => void;\n /** Branch suffix (injected for deterministic tests). */\n branchSuffix?: string;\n}\n\nexport const REMEDIATE_EXIT = { OK: 0, CHANGES: 0, ERROR: 2 } as const;\n\n/** Default per-run cap on paid LLM fix proposals (spend/DoS guard; override with --max-llm). */\nexport const DEFAULT_MAX_LLM = 25;\n\nfunction envKey(provider: LlmProvider): string | undefined {\n return (\n process.env.QK_LLM_API_KEY ??\n (provider === \"anthropic\" ? process.env.ANTHROPIC_API_KEY : process.env.OPENAI_API_KEY)\n );\n}\n\nfunction defaultModel(provider: LlmProvider): string {\n return provider === \"anthropic\" ? \"claude-sonnet-5\" : \"gpt-4o-mini\";\n}\n\n/** Minimal unified diff for a localized change (3 lines of context). */\nexport function unifiedDiff(relPath: string, before: string, after: string): string {\n if (before === after) return \"\";\n const a = before.split(\"\\n\");\n const b = after.split(\"\\n\");\n let start = 0;\n while (start < a.length && start < b.length && a[start] === b[start]) start++;\n let endA = a.length - 1;\n let endB = b.length - 1;\n while (endA >= start && endB >= start && a[endA] === b[endB]) {\n endA--;\n endB--;\n }\n const ctx = 3;\n const from = Math.max(0, start - ctx);\n const toA = Math.min(a.length - 1, endA + ctx);\n const toB = Math.min(b.length - 1, endB + ctx);\n const lines: string[] = [`--- a/${relPath}`, `+++ b/${relPath}`];\n lines.push(`@@ -${from + 1},${toA - from + 1} +${from + 1},${toB - from + 1} @@`);\n for (let i = from; i < start; i++) lines.push(` ${a[i]}`);\n for (let i = start; i <= endA; i++) lines.push(`-${a[i]}`);\n for (let i = start; i <= endB; i++) lines.push(`+${b[i]}`);\n for (let i = endA + 1; i <= toA; i++) lines.push(` ${a[i]}`);\n return lines.join(\"\\n\");\n}\n\n/**\n * Default draft-PR backend: apply the patches inside an ephemeral worktree\n * (never touching the user's checkout), commit on a new branch, push it, and\n * `gh pr create --draft`. NEVER merges.\n */\nasync function defaultOpenDraftPr(root: string, plan: DraftPrPlan): Promise<{ url?: string }> {\n return withWorktree(root, async (dir) => {\n await exec(\"git\", [\"-C\", dir, \"checkout\", \"-b\", plan.branch]);\n for (const p of plan.patches) {\n await fsWriteFile(path.resolve(dir, p.path), p.newContent, \"utf8\");\n }\n await exec(\"git\", [\"-C\", dir, \"add\", \"--\", ...plan.patches.map((p) => p.path)]);\n await exec(\"git\", [\"-C\", dir, \"commit\", \"-m\", plan.title]);\n await exec(\"git\", [\"-C\", dir, \"push\", \"-u\", \"origin\", plan.branch]);\n const { stdout } = await exec(\n \"gh\",\n [\n \"pr\",\n \"create\",\n \"--draft\",\n \"--head\",\n plan.branch,\n \"--title\",\n plan.title,\n \"--body\",\n plan.body,\n ],\n { cwd: dir },\n );\n return { url: stdout.trim() };\n });\n}\n\nfunction prBody(\n patches: VerifiedPatch[],\n rejected: RejectedPatch[],\n options: RemediateOptions,\n): string {\n const codemodN = patches.filter((p) => p.patch.source === \"codemod\").length;\n const llmN = patches.length - codemodN;\n const lines: string[] = [\n \"Automated post-quantum remediation from `qremediate`.\",\n \"\",\n `**${patches.length} fix(es)** — ${codemodN} deterministic codemod fix(es) and ${llmN} LLM-proposed. ` +\n `Each cleared the verify_fix gate (target finding gone, no new finding) and the patch policy.`,\n \"\",\n ];\n for (const vp of patches) {\n lines.push(`- \\`${vp.patch.path}\\` — ${vp.finding.ruleId} (${vp.patch.source})`);\n }\n if (llmN > 0) {\n lines.push(\n \"\",\n `⚠️ The ${llmN} LLM-proposed fix(es) are **crypto-verified, not security-reviewed**: the gate ` +\n `only proves the crypto finding is gone, not that the rest of the rewrite is safe. Read every ` +\n `LLM diff before merging. Context was shared at the \\`file\\` level with secrets redacted (best-effort).`,\n );\n }\n if (rejected.length) {\n lines.push(\"\", `${rejected.length} finding(s) were not auto-fixable and need review.`);\n }\n lines.push(\"\", \"This is a **draft** PR and was NOT merged. Review every change before merging.\");\n return lines.join(\"\\n\");\n}\n\n/** Run a complete qremediate pass. Pure w.r.t. process; the bin prints + exits. */\nexport async function runRemediate(\n options: RemediateOptions,\n hooks: RemediateHooks = {},\n): Promise<RemediateRun> {\n const root = path.resolve(options.path);\n const readFile = hooks.readFile ?? ((abs: string) => fsReadFile(abs, \"utf8\"));\n const writeFile =\n hooks.writeFile ?? ((abs: string, content: string) => fsWriteFile(abs, content, \"utf8\"));\n const scanFn = hooks.scanFn ?? ((r: string) => scan({ root: r }));\n const stderr = hooks.stderr ?? ((s: string) => void process.stderr.write(s));\n\n const result = await scanFn(root);\n const findings = result.findings;\n\n const findingFiles = new Set(findings.map((f) => f.location.file));\n const manifestFiles = new Set(\n findings.filter((f) => isManifestFile(f.location.file)).map((f) => f.location.file),\n );\n\n // Build the optional LLM patch source (codemods always run first).\n const provider: LlmProvider = options.provider ?? \"anthropic\";\n const model = options.model ?? defaultModel(provider);\n const maxLlm = options.maxLlm ?? DEFAULT_MAX_LLM;\n let llmCalls = 0;\n let llmCapHit = false;\n let baseLlmSource = hooks.llmPatchSource;\n if (!baseLlmSource && options.llm) {\n const key = hooks.resolveKey ? hooks.resolveKey() : envKey(provider);\n if (!key) {\n stderr(\n \"qremediate: --llm needs an API key (QK_LLM_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY). Using codemods only.\\n\",\n );\n } else {\n baseLlmSource = async (finding) => {\n const agent = await import(\"@quantakrypto/agent\");\n const client = agent.resolveClient({ provider, model, apiKey: key });\n const proposal = await agent.proposeFix(finding, {\n client,\n readFile: (rel) => readFile(path.resolve(root, rel)),\n fingerprint: fingerprintFinding,\n });\n if (!proposal) return null;\n return {\n path: proposal.path,\n newContent: proposal.newContent,\n ruleId: finding.ruleId,\n source: \"llm\",\n };\n };\n }\n }\n // Spend/DoS guard: cap the number of paid LLM proposals per run.\n const llmSource: ((finding: Finding, content: string) => Promise<Patch | null>) | undefined =\n baseLlmSource\n ? async (finding, content) => {\n if (llmCalls >= maxLlm) {\n llmCapHit = true;\n return null;\n }\n llmCalls++;\n return baseLlmSource!(finding, content);\n }\n : undefined;\n\n const patchSource = async (finding: Finding, content: string): Promise<Patch | null> => {\n const codemod = codemodFor(finding);\n if (codemod) return codemod.apply(content, finding);\n if (llmSource) return llmSource(finding, content);\n return null;\n };\n\n const contentCache = new Map<string, string>();\n const readContent = async (finding: Finding): Promise<string> => {\n const abs = path.resolve(root, finding.location.file);\n let c = contentCache.get(abs);\n if (c === undefined) {\n c = await readFile(abs);\n contentCache.set(abs, c);\n }\n return c;\n };\n\n const rem = await remediateFindings(findings, {\n readContent,\n patchSource,\n policy: { findingFiles, manifestFiles },\n });\n\n const byPath = new Map<string, VerifiedPatch>();\n for (const vp of rem.applied) if (!byPath.has(vp.patch.path)) byPath.set(vp.patch.path, vp);\n const patches = [...byPath.values()];\n\n if (patches.length === 0) {\n return {\n output: summarize(findings, patches, rem.rejected, options.mode, []),\n exitCode: REMEDIATE_EXIT.OK,\n written: [],\n };\n }\n\n if (options.mode === \"pr\") {\n const openPr = hooks.openDraftPr ?? ((plan: DraftPrPlan) => defaultOpenDraftPr(root, plan));\n const suffix = hooks.branchSuffix ?? `${Date.now()}`;\n const plan: DraftPrPlan = {\n branch: `quantakrypto/remediate-${suffix}`,\n title: `qremediate: migrate ${patches.length} quantum-vulnerable finding(s)`,\n body: prBody(patches, rem.rejected, options),\n patches: patches.map((vp) => ({ path: vp.patch.path, newContent: vp.patch.newContent })),\n };\n try {\n const { url } = await openPr(plan);\n return {\n output: `qremediate: opened a DRAFT PR${url ? ` (${url})` : \"\"} on branch ${plan.branch} with ${patches.length} verified fix(es). Nothing was merged — review it.`,\n exitCode: REMEDIATE_EXIT.OK,\n written: plan.patches.map((p) => p.path),\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n output: `qremediate: could not open a draft PR (${msg}). No changes were pushed; run with --mode diff/apply instead.`,\n exitCode: REMEDIATE_EXIT.ERROR,\n written: [],\n };\n }\n }\n\n const written: string[] = [];\n const diffs: string[] = [];\n const heldBack: VerifiedPatch[] = [];\n for (const vp of patches) {\n const abs = path.resolve(root, vp.patch.path);\n const before = contentCache.get(abs) ?? (await readContent(vp.finding));\n // An LLM rewrite is only crypto-verified, not security-reviewed — never write\n // it in `apply` mode without an explicit `--apply-llm` acknowledgement. It is\n // shown as a diff to review instead. Deterministic codemods write normally.\n const holdForReview =\n options.mode === \"apply\" && vp.patch.source === \"llm\" && !options.applyLlm;\n if (options.mode === \"apply\" && !holdForReview) {\n await writeFile(abs, vp.patch.newContent);\n written.push(vp.patch.path);\n } else {\n if (holdForReview) heldBack.push(vp);\n diffs.push(unifiedDiff(vp.patch.path, before, vp.patch.newContent));\n }\n }\n\n const showDiffs = options.mode === \"diff\" || heldBack.length > 0;\n const body = showDiffs ? `${diffs.join(\"\\n\\n\")}\\n\\n` : \"\";\n return {\n output:\n body +\n summarize(\n findings,\n patches,\n rem.rejected,\n options.mode,\n written,\n heldBack,\n llmCapHit ? maxLlm : 0,\n ),\n exitCode: REMEDIATE_EXIT.OK,\n written,\n };\n}\n\nfunction summarize(\n findings: readonly Finding[],\n patches: VerifiedPatch[],\n rejected: RejectedPatch[],\n mode: RemediateMode,\n written: string[],\n heldBack: VerifiedPatch[] = [],\n llmCapMax = 0,\n): string {\n const codemodN = patches.filter((p) => p.patch.source === \"codemod\").length;\n const llmN = patches.length - codemodN;\n const lines: string[] = [];\n lines.push(\n `qremediate: ${findings.length} finding(s), ${patches.length} candidate fix(es) ` +\n `(${codemodN} codemod-verified, ${llmN} LLM-proposed), ${rejected.length} not auto-fixable.`,\n );\n if (mode === \"apply\" && written.length) {\n lines.push(`Wrote: ${written.join(\", \")}`);\n }\n if (heldBack.length) {\n lines.push(\n `Held back ${heldBack.length} LLM fix(es) (shown as diffs above): crypto-verified but ` +\n `NOT security-reviewed — read them, then re-run with --apply-llm to write them.`,\n );\n } else if (mode === \"diff\" && patches.length) {\n lines.push(\n llmN > 0\n ? \"Review the diff above — codemod fixes are deterministic; LLM fixes need a human read. Then --mode apply (add --apply-llm for the LLM ones).\"\n : \"Review the diff above, then re-run with --mode apply to write it.\",\n );\n }\n if (llmCapMax) {\n lines.push(\n `Note: hit the --max-llm cap (${llmCapMax}); some findings were not sent to the LLM — raise it to cover more.`,\n );\n }\n if (rejected.length) {\n lines.push(\"Not auto-fixed (needs review or the LLM layer):\");\n for (const r of rejected.slice(0, 10)) {\n lines.push(\n ` - ${r.finding.ruleId} ${r.finding.location.file}:${r.finding.location.line} — ${r.reason}`,\n );\n }\n if (rejected.length > 10) lines.push(` … and ${rejected.length - 10} more.`);\n }\n return lines.join(\"\\n\");\n}\n\n/** Parse qremediate argv. */\nexport function parseRemediateArgs(\n argv: readonly string[],\n):\n | { kind: \"run\"; options: RemediateOptions }\n | { kind: \"help\" }\n | { kind: \"version\" }\n | { kind: \"error\"; message: string } {\n const options: RemediateOptions = { path: \".\", mode: \"diff\", llm: false };\n let positional: string | undefined;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i] as string;\n let flag = arg;\n let inline: string | undefined;\n if (arg.startsWith(\"--\") && arg.includes(\"=\")) {\n const eq = arg.indexOf(\"=\");\n flag = arg.slice(0, eq);\n inline = arg.slice(eq + 1);\n }\n const take = (): string | undefined => inline ?? argv[++i];\n switch (flag) {\n case \"-h\":\n case \"--help\":\n return { kind: \"help\" };\n case \"-v\":\n case \"--version\":\n return { kind: \"version\" };\n case \"--mode\": {\n const v = take();\n if (v !== \"diff\" && v !== \"apply\" && v !== \"pr\") {\n return { kind: \"error\", message: `invalid --mode \"${v ?? \"\"}\" (expected diff|apply|pr)` };\n }\n options.mode = v;\n break;\n }\n case \"--llm\":\n options.llm = true;\n break;\n case \"--apply-llm\":\n options.applyLlm = true;\n break;\n case \"--max-llm\": {\n const v = take();\n const n = Number(v);\n if (!Number.isInteger(n) || n < 0) {\n return {\n kind: \"error\",\n message: `invalid --max-llm \"${v ?? \"\"}\" (expected a non-negative integer)`,\n };\n }\n options.maxLlm = n;\n break;\n }\n case \"--llm-provider\": {\n const v = take();\n if (v !== \"anthropic\" && v !== \"openai-compatible\") {\n return { kind: \"error\", message: `invalid --llm-provider \"${v ?? \"\"}\"` };\n }\n options.provider = v;\n break;\n }\n case \"--llm-model\":\n options.model = take();\n break;\n default:\n if (flag.startsWith(\"-\")) return { kind: \"error\", message: `unknown option \"${flag}\"` };\n if (positional !== undefined)\n return { kind: \"error\", message: `unexpected argument \"${arg}\"` };\n positional = arg;\n }\n }\n if (positional !== undefined) options.path = positional;\n return { kind: \"run\", options };\n}\n\nexport const REMEDIATE_HELP = `qremediate — apply verified codemod fixes (and, with --llm, crypto-verified LLM proposals) for insecure crypto findings\n\nUSAGE\n qremediate [path] [--mode diff|apply|pr] [--llm] [--apply-llm] [--max-llm N]\n [--llm-provider <p>] [--llm-model <m>]\n\nOPTIONS\n --mode diff Print a unified diff of every candidate fix (default; writes nothing)\n --mode apply Write deterministic codemod fixes into the working tree\n (LLM fixes are held back as diffs unless --apply-llm is given)\n --mode pr Commit fixes to a new branch and open a DRAFT PR (never merges)\n --llm Also let a BYOK LLM propose fixes codemods can't (needs an API key)\n --apply-llm In apply mode, also write LLM fixes (only after you've read them)\n --max-llm N Cap paid LLM proposals per run (default ${DEFAULT_MAX_LLM}; spend guard)\n --llm-provider anthropic | openai-compatible (default: anthropic)\n --llm-model Model id for the BYOK provider\n -h, --help Show this help\n -v, --version Show version\n\nEvery fix must clear the verify_fix gate (target finding gone, no new finding) and\nthe patch policy (only files with findings + dependency manifests). Codemod fixes\nare deterministic; LLM fixes are **crypto-verified, not security-reviewed** — the\ngate proves the crypto is gone, not that the rewrite is safe, and the pipeline\nrejects any LLM patch that adds a network/exec sink or rewrites too much. Review\nLLM diffs before applying. Never merges.\n`;\n"]}
|
|
1
|
+
{"version":3,"file":"remediate-cli.js","sourceRoot":"","sources":["../src/remediate-cli.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `qremediate` — apply deterministic, verified fixes to quantum-vulnerable /\n * insecure crypto findings.\n *\n * Pipeline: scan → for each finding, a codemod (then optionally the LLM with\n * `--llm`) proposes a patch → the core remediation pipeline gates it\n * (patch-policy + verify_fix) → verified patches are shown (`diff`), written\n * (`apply`), or opened as a DRAFT PR (`pr`). Never auto-merges.\n */\nimport { readFile as fsReadFile, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport path from \"node:path\";\n\nimport {\n scan,\n codemodFor,\n remediateFindings,\n isManifestFile,\n fingerprintFinding,\n withWorktree,\n} from \"@quantakrypto/core\";\nimport type { Finding, Patch, ScanResult, VerifiedPatch, RejectedPatch } from \"@quantakrypto/core\";\n\nimport type { LlmProvider } from \"./args.js\";\n\nconst exec = promisify(execFile);\n\nexport type RemediateMode = \"diff\" | \"apply\" | \"pr\";\n\nexport interface RemediateOptions {\n path: string;\n mode: RemediateMode;\n /** Use a BYOK LLM to propose fixes codemods can't. */\n llm: boolean;\n /** Actually write LLM-proposed fixes in `apply` mode (they are otherwise shown\n * as diffs and held back — an LLM rewrite must be reviewed, not auto-applied). */\n applyLlm?: boolean;\n /** Cap how many findings are sent to the LLM (spend/DoS guard). */\n maxLlm?: number;\n provider?: LlmProvider;\n model?: string;\n}\n\nexport interface RemediateRun {\n output: string;\n exitCode: number;\n /** Relative paths written (apply mode) or included in the PR. */\n written: string[];\n}\n\n/** A verified fix set ready to become a draft PR. */\ninterface DraftPrPlan {\n branch: string;\n title: string;\n body: string;\n patches: { path: string; newContent: string }[];\n}\n\n/** Open a draft PR for the plan (injectable; default shells git + gh in a worktree). */\ntype OpenDraftPr = (plan: DraftPrPlan) => Promise<{ url?: string }>;\n\nexport interface RemediateHooks {\n scanFn?: (root: string) => Promise<ScanResult>;\n readFile?: (abs: string) => Promise<string>;\n writeFile?: (abs: string, content: string) => Promise<void>;\n /** LLM patch source (default wraps `@quantakrypto/agent`'s proposeFix). */\n llmPatchSource?: (finding: Finding, content: string) => Promise<Patch | null>;\n /** Draft-PR backend (default: git worktree + gh). */\n openDraftPr?: OpenDraftPr;\n resolveKey?: () => string | undefined;\n stderr?: (s: string) => void;\n /** Branch suffix (injected for deterministic tests). */\n branchSuffix?: string;\n}\n\nexport const REMEDIATE_EXIT = { OK: 0, CHANGES: 0, ERROR: 2 } as const;\n\n/** Default per-run cap on paid LLM fix proposals (spend/DoS guard; override with --max-llm). */\nconst DEFAULT_MAX_LLM = 25;\n\nfunction envKey(provider: LlmProvider): string | undefined {\n return (\n process.env.QK_LLM_API_KEY ??\n (provider === \"anthropic\" ? process.env.ANTHROPIC_API_KEY : process.env.OPENAI_API_KEY)\n );\n}\n\nfunction defaultModel(provider: LlmProvider): string {\n return provider === \"anthropic\" ? \"claude-sonnet-5\" : \"gpt-4o-mini\";\n}\n\n/** Minimal unified diff for a localized change (3 lines of context). */\nexport function unifiedDiff(relPath: string, before: string, after: string): string {\n if (before === after) return \"\";\n const a = before.split(\"\\n\");\n const b = after.split(\"\\n\");\n let start = 0;\n while (start < a.length && start < b.length && a[start] === b[start]) start++;\n let endA = a.length - 1;\n let endB = b.length - 1;\n while (endA >= start && endB >= start && a[endA] === b[endB]) {\n endA--;\n endB--;\n }\n const ctx = 3;\n const from = Math.max(0, start - ctx);\n const toA = Math.min(a.length - 1, endA + ctx);\n const toB = Math.min(b.length - 1, endB + ctx);\n const lines: string[] = [`--- a/${relPath}`, `+++ b/${relPath}`];\n lines.push(`@@ -${from + 1},${toA - from + 1} +${from + 1},${toB - from + 1} @@`);\n for (let i = from; i < start; i++) lines.push(` ${a[i]}`);\n for (let i = start; i <= endA; i++) lines.push(`-${a[i]}`);\n for (let i = start; i <= endB; i++) lines.push(`+${b[i]}`);\n for (let i = endA + 1; i <= toA; i++) lines.push(` ${a[i]}`);\n return lines.join(\"\\n\");\n}\n\n/**\n * Default draft-PR backend: apply the patches inside an ephemeral worktree\n * (never touching the user's checkout), commit on a new branch, push it, and\n * `gh pr create --draft`. NEVER merges.\n */\nasync function defaultOpenDraftPr(root: string, plan: DraftPrPlan): Promise<{ url?: string }> {\n return withWorktree(root, async (dir) => {\n await exec(\"git\", [\"-C\", dir, \"checkout\", \"-b\", plan.branch]);\n for (const p of plan.patches) {\n await fsWriteFile(path.resolve(dir, p.path), p.newContent, \"utf8\");\n }\n await exec(\"git\", [\"-C\", dir, \"add\", \"--\", ...plan.patches.map((p) => p.path)]);\n await exec(\"git\", [\"-C\", dir, \"commit\", \"-m\", plan.title]);\n await exec(\"git\", [\"-C\", dir, \"push\", \"-u\", \"origin\", plan.branch]);\n const { stdout } = await exec(\n \"gh\",\n [\n \"pr\",\n \"create\",\n \"--draft\",\n \"--head\",\n plan.branch,\n \"--title\",\n plan.title,\n \"--body\",\n plan.body,\n ],\n { cwd: dir },\n );\n return { url: stdout.trim() };\n });\n}\n\nfunction prBody(\n patches: VerifiedPatch[],\n rejected: RejectedPatch[],\n options: RemediateOptions,\n): string {\n const codemodN = patches.filter((p) => p.patch.source === \"codemod\").length;\n const llmN = patches.length - codemodN;\n const lines: string[] = [\n \"Automated post-quantum remediation from `qremediate`.\",\n \"\",\n `**${patches.length} fix(es)** — ${codemodN} deterministic codemod fix(es) and ${llmN} LLM-proposed. ` +\n `Each cleared the verify_fix gate (target finding gone, no new finding) and the patch policy.`,\n \"\",\n ];\n for (const vp of patches) {\n lines.push(`- \\`${vp.patch.path}\\` — ${vp.finding.ruleId} (${vp.patch.source})`);\n }\n if (llmN > 0) {\n lines.push(\n \"\",\n `⚠️ The ${llmN} LLM-proposed fix(es) are **crypto-verified, not security-reviewed**: the gate ` +\n `only proves the crypto finding is gone, not that the rest of the rewrite is safe. Read every ` +\n `LLM diff before merging. Context was shared at the \\`file\\` level with secrets redacted (best-effort).`,\n );\n }\n if (rejected.length) {\n lines.push(\"\", `${rejected.length} finding(s) were not auto-fixable and need review.`);\n }\n lines.push(\"\", \"This is a **draft** PR and was NOT merged. Review every change before merging.\");\n return lines.join(\"\\n\");\n}\n\n/** Run a complete qremediate pass. Pure w.r.t. process; the bin prints + exits. */\nexport async function runRemediate(\n options: RemediateOptions,\n hooks: RemediateHooks = {},\n): Promise<RemediateRun> {\n const root = path.resolve(options.path);\n const readFile = hooks.readFile ?? ((abs: string) => fsReadFile(abs, \"utf8\"));\n const writeFile =\n hooks.writeFile ?? ((abs: string, content: string) => fsWriteFile(abs, content, \"utf8\"));\n const scanFn = hooks.scanFn ?? ((r: string) => scan({ root: r }));\n const stderr = hooks.stderr ?? ((s: string) => void process.stderr.write(s));\n\n const result = await scanFn(root);\n const findings = result.findings;\n\n const findingFiles = new Set(findings.map((f) => f.location.file));\n const manifestFiles = new Set(\n findings.filter((f) => isManifestFile(f.location.file)).map((f) => f.location.file),\n );\n\n // Build the optional LLM patch source (codemods always run first).\n const provider: LlmProvider = options.provider ?? \"anthropic\";\n const model = options.model ?? defaultModel(provider);\n const maxLlm = options.maxLlm ?? DEFAULT_MAX_LLM;\n let llmCalls = 0;\n let llmCapHit = false;\n let baseLlmSource = hooks.llmPatchSource;\n if (!baseLlmSource && options.llm) {\n const key = hooks.resolveKey ? hooks.resolveKey() : envKey(provider);\n if (!key) {\n stderr(\n \"qremediate: --llm needs an API key (QK_LLM_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY). Using codemods only.\\n\",\n );\n } else {\n baseLlmSource = async (finding) => {\n const agent = await import(\"@quantakrypto/agent\");\n const client = agent.resolveClient({ provider, model, apiKey: key });\n const proposal = await agent.proposeFix(finding, {\n client,\n readFile: (rel) => readFile(path.resolve(root, rel)),\n fingerprint: fingerprintFinding,\n });\n if (!proposal) return null;\n return {\n path: proposal.path,\n newContent: proposal.newContent,\n ruleId: finding.ruleId,\n source: \"llm\",\n };\n };\n }\n }\n // Spend/DoS guard: cap the number of paid LLM proposals per run.\n const llmSource: ((finding: Finding, content: string) => Promise<Patch | null>) | undefined =\n baseLlmSource\n ? async (finding, content) => {\n if (llmCalls >= maxLlm) {\n llmCapHit = true;\n return null;\n }\n llmCalls++;\n return baseLlmSource!(finding, content);\n }\n : undefined;\n\n const patchSource = async (finding: Finding, content: string): Promise<Patch | null> => {\n const codemod = codemodFor(finding);\n if (codemod) return codemod.apply(content, finding);\n if (llmSource) return llmSource(finding, content);\n return null;\n };\n\n const contentCache = new Map<string, string>();\n const readContent = async (finding: Finding): Promise<string> => {\n const abs = path.resolve(root, finding.location.file);\n let c = contentCache.get(abs);\n if (c === undefined) {\n c = await readFile(abs);\n contentCache.set(abs, c);\n }\n return c;\n };\n\n const rem = await remediateFindings(findings, {\n readContent,\n patchSource,\n policy: { findingFiles, manifestFiles },\n });\n\n const byPath = new Map<string, VerifiedPatch>();\n for (const vp of rem.applied) if (!byPath.has(vp.patch.path)) byPath.set(vp.patch.path, vp);\n const patches = [...byPath.values()];\n\n if (patches.length === 0) {\n return {\n output: summarize(findings, patches, rem.rejected, options.mode, []),\n exitCode: REMEDIATE_EXIT.OK,\n written: [],\n };\n }\n\n if (options.mode === \"pr\") {\n const openPr = hooks.openDraftPr ?? ((plan: DraftPrPlan) => defaultOpenDraftPr(root, plan));\n const suffix = hooks.branchSuffix ?? `${Date.now()}`;\n const plan: DraftPrPlan = {\n branch: `quantakrypto/remediate-${suffix}`,\n title: `qremediate: migrate ${patches.length} quantum-vulnerable finding(s)`,\n body: prBody(patches, rem.rejected, options),\n patches: patches.map((vp) => ({ path: vp.patch.path, newContent: vp.patch.newContent })),\n };\n try {\n const { url } = await openPr(plan);\n return {\n output: `qremediate: opened a DRAFT PR${url ? ` (${url})` : \"\"} on branch ${plan.branch} with ${patches.length} verified fix(es). Nothing was merged — review it.`,\n exitCode: REMEDIATE_EXIT.OK,\n written: plan.patches.map((p) => p.path),\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n output: `qremediate: could not open a draft PR (${msg}). No changes were pushed; run with --mode diff/apply instead.`,\n exitCode: REMEDIATE_EXIT.ERROR,\n written: [],\n };\n }\n }\n\n const written: string[] = [];\n const diffs: string[] = [];\n const heldBack: VerifiedPatch[] = [];\n for (const vp of patches) {\n const abs = path.resolve(root, vp.patch.path);\n const before = contentCache.get(abs) ?? (await readContent(vp.finding));\n // An LLM rewrite is only crypto-verified, not security-reviewed — never write\n // it in `apply` mode without an explicit `--apply-llm` acknowledgement. It is\n // shown as a diff to review instead. Deterministic codemods write normally.\n const holdForReview =\n options.mode === \"apply\" && vp.patch.source === \"llm\" && !options.applyLlm;\n if (options.mode === \"apply\" && !holdForReview) {\n await writeFile(abs, vp.patch.newContent);\n written.push(vp.patch.path);\n } else {\n if (holdForReview) heldBack.push(vp);\n diffs.push(unifiedDiff(vp.patch.path, before, vp.patch.newContent));\n }\n }\n\n const showDiffs = options.mode === \"diff\" || heldBack.length > 0;\n const body = showDiffs ? `${diffs.join(\"\\n\\n\")}\\n\\n` : \"\";\n return {\n output:\n body +\n summarize(\n findings,\n patches,\n rem.rejected,\n options.mode,\n written,\n heldBack,\n llmCapHit ? maxLlm : 0,\n ),\n exitCode: REMEDIATE_EXIT.OK,\n written,\n };\n}\n\nfunction summarize(\n findings: readonly Finding[],\n patches: VerifiedPatch[],\n rejected: RejectedPatch[],\n mode: RemediateMode,\n written: string[],\n heldBack: VerifiedPatch[] = [],\n llmCapMax = 0,\n): string {\n const codemodN = patches.filter((p) => p.patch.source === \"codemod\").length;\n const llmN = patches.length - codemodN;\n const lines: string[] = [];\n lines.push(\n `qremediate: ${findings.length} finding(s), ${patches.length} candidate fix(es) ` +\n `(${codemodN} codemod-verified, ${llmN} LLM-proposed), ${rejected.length} not auto-fixable.`,\n );\n if (mode === \"apply\" && written.length) {\n lines.push(`Wrote: ${written.join(\", \")}`);\n }\n if (heldBack.length) {\n lines.push(\n `Held back ${heldBack.length} LLM fix(es) (shown as diffs above): crypto-verified but ` +\n `NOT security-reviewed — read them, then re-run with --apply-llm to write them.`,\n );\n } else if (mode === \"diff\" && patches.length) {\n lines.push(\n llmN > 0\n ? \"Review the diff above — codemod fixes are deterministic; LLM fixes need a human read. Then --mode apply (add --apply-llm for the LLM ones).\"\n : \"Review the diff above, then re-run with --mode apply to write it.\",\n );\n }\n if (llmCapMax) {\n lines.push(\n `Note: hit the --max-llm cap (${llmCapMax}); some findings were not sent to the LLM — raise it to cover more.`,\n );\n }\n if (rejected.length) {\n lines.push(\"Not auto-fixed (needs review or the LLM layer):\");\n for (const r of rejected.slice(0, 10)) {\n lines.push(\n ` - ${r.finding.ruleId} ${r.finding.location.file}:${r.finding.location.line} — ${r.reason}`,\n );\n }\n if (rejected.length > 10) lines.push(` … and ${rejected.length - 10} more.`);\n }\n return lines.join(\"\\n\");\n}\n\n/** Parse qremediate argv. */\nexport function parseRemediateArgs(\n argv: readonly string[],\n):\n | { kind: \"run\"; options: RemediateOptions }\n | { kind: \"help\" }\n | { kind: \"version\" }\n | { kind: \"error\"; message: string } {\n const options: RemediateOptions = { path: \".\", mode: \"diff\", llm: false };\n let positional: string | undefined;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i] as string;\n let flag = arg;\n let inline: string | undefined;\n if (arg.startsWith(\"--\") && arg.includes(\"=\")) {\n const eq = arg.indexOf(\"=\");\n flag = arg.slice(0, eq);\n inline = arg.slice(eq + 1);\n }\n const take = (): string | undefined => inline ?? argv[++i];\n switch (flag) {\n case \"-h\":\n case \"--help\":\n return { kind: \"help\" };\n case \"-v\":\n case \"--version\":\n return { kind: \"version\" };\n case \"--mode\": {\n const v = take();\n if (v !== \"diff\" && v !== \"apply\" && v !== \"pr\") {\n return { kind: \"error\", message: `invalid --mode \"${v ?? \"\"}\" (expected diff|apply|pr)` };\n }\n options.mode = v;\n break;\n }\n case \"--llm\":\n options.llm = true;\n break;\n case \"--apply-llm\":\n options.applyLlm = true;\n break;\n case \"--max-llm\": {\n const v = take();\n const n = Number(v);\n if (!Number.isInteger(n) || n < 0) {\n return {\n kind: \"error\",\n message: `invalid --max-llm \"${v ?? \"\"}\" (expected a non-negative integer)`,\n };\n }\n options.maxLlm = n;\n break;\n }\n case \"--llm-provider\": {\n const v = take();\n if (v !== \"anthropic\" && v !== \"openai-compatible\") {\n return { kind: \"error\", message: `invalid --llm-provider \"${v ?? \"\"}\"` };\n }\n options.provider = v;\n break;\n }\n case \"--llm-model\":\n options.model = take();\n break;\n default:\n if (flag.startsWith(\"-\")) return { kind: \"error\", message: `unknown option \"${flag}\"` };\n if (positional !== undefined)\n return { kind: \"error\", message: `unexpected argument \"${arg}\"` };\n positional = arg;\n }\n }\n if (positional !== undefined) options.path = positional;\n return { kind: \"run\", options };\n}\n\nexport const REMEDIATE_HELP = `qremediate — apply verified codemod fixes (and, with --llm, crypto-verified LLM proposals) for insecure crypto findings\n\nUSAGE\n qremediate [path] [--mode diff|apply|pr] [--llm] [--apply-llm] [--max-llm N]\n [--llm-provider <p>] [--llm-model <m>]\n\nOPTIONS\n --mode diff Print a unified diff of every candidate fix (default; writes nothing)\n --mode apply Write deterministic codemod fixes into the working tree\n (LLM fixes are held back as diffs unless --apply-llm is given)\n --mode pr Commit fixes to a new branch and open a DRAFT PR (never merges)\n --llm Also let a BYOK LLM propose fixes codemods can't (needs an API key)\n --apply-llm In apply mode, also write LLM fixes (only after you've read them)\n --max-llm N Cap paid LLM proposals per run (default ${DEFAULT_MAX_LLM}; spend guard)\n --llm-provider anthropic | openai-compatible (default: anthropic)\n --llm-model Model id for the BYOK provider\n -h, --help Show this help\n -v, --version Show version\n\nEvery fix must clear the verify_fix gate (target finding gone, no new finding) and\nthe patch policy (only files with findings + dependency manifests). Codemod fixes\nare deterministic; LLM fixes are **crypto-verified, not security-reviewed** — the\ngate proves the crypto is gone, not that the rewrite is safe, and the pipeline\nrejects any LLM patch that adds a network/exec sink or rewrites too much. Review\nLLM diffs before applying. Never merges.\n`;\n"]}
|
package/dist/report.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Only `human` lives here; `json`/`sarif` delegate to `@quantakrypto/core` so the
|
|
11
11
|
* serialized shape stays consistent across every tool in the monorepo.
|
|
12
12
|
*/
|
|
13
|
-
import type { ReportOptions, ScanResult, SecurityTier } from "@quantakrypto/core";
|
|
13
|
+
import type { CycloneDxBom, ReportOptions, ScanResult, SecurityTier } from "@quantakrypto/core";
|
|
14
14
|
/**
|
|
15
15
|
* Render the JSON report (pretty-printed, no trailing newline).
|
|
16
16
|
*
|
|
@@ -30,8 +30,18 @@ export declare function renderSarif(result: ScanResult, opts?: ReportOptions): s
|
|
|
30
30
|
* Render a CycloneDX 1.6 CBOM (cryptographic bill of materials) for the scan,
|
|
31
31
|
* pretty-printed with no trailing newline. Delegates to core's `toCbom` so the
|
|
32
32
|
* serialized shape stays consistent across every tool in the monorepo.
|
|
33
|
+
*
|
|
34
|
+
* When `extra` CBOMs are supplied (e.g. a qProbe endpoint CBOM), they are merged
|
|
35
|
+
* with the scan CBOM via core's `mergeCboms`, producing a single combined
|
|
36
|
+
* code + infrastructure bill of materials linked by CycloneDX bom-link.
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderCbom(result: ScanResult, extra?: readonly CycloneDxBom[]): string;
|
|
39
|
+
/**
|
|
40
|
+
* Render an OpenVEX 0.2.0 document for the scan (pretty-printed, no trailing
|
|
41
|
+
* newline). Delegates to core's `toOpenVex` so the VEX shape stays consistent
|
|
42
|
+
* across the monorepo. Carries any `--triage` verdicts into `status_notes`.
|
|
33
43
|
*/
|
|
34
|
-
export declare function
|
|
44
|
+
export declare function renderVex(result: ScanResult): string;
|
|
35
45
|
/**
|
|
36
46
|
* Render the human-readable banner.
|
|
37
47
|
*
|
|
@@ -43,5 +53,6 @@ export declare function renderHuman(result: ScanResult, opts?: {
|
|
|
43
53
|
color?: boolean;
|
|
44
54
|
topN?: number;
|
|
45
55
|
tier?: SecurityTier;
|
|
56
|
+
profile?: string;
|
|
46
57
|
}): string;
|
|
47
58
|
//# sourceMappingURL=report.d.ts.map
|
package/dist/report.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH,OAAO,KAAK,EACV,YAAY,EAEZ,aAAa,EACb,UAAU,EACV,YAAY,EAGb,MAAM,oBAAoB,CAAC;AAqC5B;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAE3E;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAS5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,GAAE,SAAS,YAAY,EAAO,GAAG,MAAM,CAI1F;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,YAAY,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACnF,MAAM,CAkIR"}
|
package/dist/report.js
CHANGED
|
@@ -10,7 +10,20 @@
|
|
|
10
10
|
* Only `human` lives here; `json`/`sarif` delegate to `@quantakrypto/core` so the
|
|
11
11
|
* serialized shape stays consistent across every tool in the monorepo.
|
|
12
12
|
*/
|
|
13
|
-
import { ANALYZABLE_LANGUAGES_LABEL, defaultRegistry, DEP_VULNERABLE_RULE,
|
|
13
|
+
import { ANALYZABLE_LANGUAGES_LABEL, defaultRegistry, DEP_VULNERABLE_RULE, formatProfileGuidance, getStandardsProfile, PQC_TRANSITION_NOTE, SEVERITY_ORDER, severityRank, STATEFUL_HBS_NOTE, toCbom, toJson, toOpenVex, toSarif, mergeCboms, } from "@quantakrypto/core";
|
|
14
|
+
/** Map the legacy `--tier` to its equivalent standards profile (back-compat alias). */
|
|
15
|
+
const TIER_TO_PROFILE = {
|
|
16
|
+
"category-3": "nist",
|
|
17
|
+
"category-5": "cnsa-2.0",
|
|
18
|
+
};
|
|
19
|
+
/** Resolve the effective standards profile from `--profile` or the `--tier` alias. */
|
|
20
|
+
function resolveProfile(profileId, tier) {
|
|
21
|
+
if (profileId)
|
|
22
|
+
return getStandardsProfile(profileId);
|
|
23
|
+
if (tier)
|
|
24
|
+
return getStandardsProfile(TIER_TO_PROFILE[tier]);
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
14
27
|
const PLAIN = { reset: "", bold: "", dim: "", red: "", yellow: "", green: "", cyan: "" };
|
|
15
28
|
const COLOR = {
|
|
16
29
|
reset: "\x1b[0m",
|
|
@@ -51,9 +64,23 @@ export function renderSarif(result, opts) {
|
|
|
51
64
|
* Render a CycloneDX 1.6 CBOM (cryptographic bill of materials) for the scan,
|
|
52
65
|
* pretty-printed with no trailing newline. Delegates to core's `toCbom` so the
|
|
53
66
|
* serialized shape stays consistent across every tool in the monorepo.
|
|
67
|
+
*
|
|
68
|
+
* When `extra` CBOMs are supplied (e.g. a qProbe endpoint CBOM), they are merged
|
|
69
|
+
* with the scan CBOM via core's `mergeCboms`, producing a single combined
|
|
70
|
+
* code + infrastructure bill of materials linked by CycloneDX bom-link.
|
|
71
|
+
*/
|
|
72
|
+
export function renderCbom(result, extra = []) {
|
|
73
|
+
const scanBom = toCbom(result);
|
|
74
|
+
const bom = extra.length > 0 ? mergeCboms([scanBom, ...extra]) : scanBom;
|
|
75
|
+
return JSON.stringify(bom, null, 2);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Render an OpenVEX 0.2.0 document for the scan (pretty-printed, no trailing
|
|
79
|
+
* newline). Delegates to core's `toOpenVex` so the VEX shape stays consistent
|
|
80
|
+
* across the monorepo. Carries any `--triage` verdicts into `status_notes`.
|
|
54
81
|
*/
|
|
55
|
-
export function
|
|
56
|
-
return JSON.stringify(
|
|
82
|
+
export function renderVex(result) {
|
|
83
|
+
return JSON.stringify(toOpenVex(result), null, 2);
|
|
57
84
|
}
|
|
58
85
|
/**
|
|
59
86
|
* Render the human-readable banner.
|
|
@@ -71,6 +98,18 @@ export function renderHuman(result, opts = {}) {
|
|
|
71
98
|
// readiness score reflects no analyzable code — say so rather than imply safe.
|
|
72
99
|
const analyzedFiles = result.analyzedFiles;
|
|
73
100
|
const noAnalyzable = analyzedFiles === 0;
|
|
101
|
+
// Partial-coverage honesty: when the analyzable subset is only a small slice of
|
|
102
|
+
// what was scanned, a high score reflects that slice, not the whole tree. We
|
|
103
|
+
// surface a one-line caveat next to the score so the number isn't over-trusted.
|
|
104
|
+
// Skips the zero case (handled explicitly below) and normal repos where most
|
|
105
|
+
// files are analyzable.
|
|
106
|
+
const lowCoverage = analyzedFiles !== undefined &&
|
|
107
|
+
analyzedFiles > 0 &&
|
|
108
|
+
filesScanned > 0 &&
|
|
109
|
+
analyzedFiles / filesScanned < 0.25;
|
|
110
|
+
const coverageCaveat = lowCoverage
|
|
111
|
+
? `${c.dim}Note: the score covers only ${analyzedFiles} analyzable of ${filesScanned} scanned files (${ANALYZABLE_LANGUAGES_LABEL}); crypto in unsupported languages is not reflected.${c.reset}`
|
|
112
|
+
: "";
|
|
74
113
|
const lines = [];
|
|
75
114
|
lines.push(`${c.bold}qScan — quantum-vulnerable cryptography report${c.reset}`);
|
|
76
115
|
const coverage = analyzedFiles === undefined
|
|
@@ -103,6 +142,8 @@ export function renderHuman(result, opts = {}) {
|
|
|
103
142
|
}
|
|
104
143
|
lines.push(`${c.green}No quantum-vulnerable cryptography detected.${c.reset}`);
|
|
105
144
|
lines.push(`${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100${c.reset}`);
|
|
145
|
+
if (coverageCaveat)
|
|
146
|
+
lines.push(coverageCaveat);
|
|
106
147
|
lines.push("");
|
|
107
148
|
lines.push(`${c.dim}Next step:${c.reset} keep scanning in CI to catch regressions.`);
|
|
108
149
|
return lines.join("\n");
|
|
@@ -117,6 +158,8 @@ export function renderHuman(result, opts = {}) {
|
|
|
117
158
|
lines.push(`${c.yellow}${inventory.hndlCount}${c.reset} exposed to harvest-now-decrypt-later (HNDL).`);
|
|
118
159
|
}
|
|
119
160
|
lines.push(`${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100${c.reset}`);
|
|
161
|
+
if (coverageCaveat)
|
|
162
|
+
lines.push(coverageCaveat);
|
|
120
163
|
lines.push("");
|
|
121
164
|
// Top findings, sorted by severity then file/line for determinism.
|
|
122
165
|
const top = [...findings].sort(compareFindings).slice(0, topN);
|
|
@@ -134,14 +177,24 @@ export function renderHuman(result, opts = {}) {
|
|
|
134
177
|
}
|
|
135
178
|
lines.push("");
|
|
136
179
|
lines.push(`${c.dim}Next step:${c.reset} ${nextStep(findings)}`);
|
|
137
|
-
//
|
|
138
|
-
|
|
180
|
+
// Regime-tailored migration targets (`--profile`, or the `--tier` alias). Surfaces
|
|
181
|
+
// the parameter sets AND the regime's hybrid stance so guidance isn't regime-wrong.
|
|
182
|
+
const profile = resolveProfile(opts.profile, opts.tier);
|
|
183
|
+
if (profile) {
|
|
139
184
|
lines.push("");
|
|
140
|
-
const g =
|
|
185
|
+
const g = formatProfileGuidance(inventory.byAlgorithm, profile);
|
|
141
186
|
lines.push(`${c.bold}${g[0]}${c.reset}`);
|
|
142
187
|
for (const t of g.slice(1))
|
|
143
188
|
lines.push(`${c.cyan}${t}${c.reset}`);
|
|
144
189
|
}
|
|
190
|
+
// Forward-looking standards + the IR 8547 migration deadline (HQC / FN-DSA /
|
|
191
|
+
// X-Wing) — the long-horizon guidance behind anything flagged above.
|
|
192
|
+
lines.push("");
|
|
193
|
+
lines.push(`${c.bold}Standards & timeline${c.reset}`);
|
|
194
|
+
lines.push(`${c.dim}${PQC_TRANSITION_NOTE}${c.reset}`);
|
|
195
|
+
if (findings.some((f) => f.category === "signature")) {
|
|
196
|
+
lines.push(`${c.dim}${STATEFUL_HBS_NOTE}${c.reset}`);
|
|
197
|
+
}
|
|
145
198
|
return lines.join("\n");
|
|
146
199
|
}
|
|
147
200
|
/** Suggest a single concrete next action based on the worst finding. */
|
package/dist/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * qScan report rendering.\n *\n * Produces the three output formats the CLI supports:\n * - `human` — a tasteful plain-text banner (counts, top findings, readiness\n * score, and a one-line next step). Optional raw ANSI color.\n * - `json` — the structured scan result via core's `toJson`.\n * - `sarif` — SARIF 2.1.0 via core's `toSarif`.\n *\n * Only `human` lives here; `json`/`sarif` delegate to `@quantakrypto/core` so the\n * serialized shape stays consistent across every tool in the monorepo.\n */\n\nimport {\n ANALYZABLE_LANGUAGES_LABEL,\n defaultRegistry,\n DEP_VULNERABLE_RULE,\n formatTierGuidance,\n SEVERITY_ORDER,\n severityRank,\n toCbom,\n toJson,\n toSarif,\n} from \"@quantakrypto/core\";\nimport type {\n Finding,\n ReportOptions,\n ScanResult,\n SecurityTier,\n Severity,\n} from \"@quantakrypto/core\";\n\n/** Minimal ANSI palette. Empty strings when color is disabled. */\ninterface Palette {\n reset: string;\n bold: string;\n dim: string;\n red: string;\n yellow: string;\n green: string;\n cyan: string;\n}\n\nconst PLAIN: Palette = { reset: \"\", bold: \"\", dim: \"\", red: \"\", yellow: \"\", green: \"\", cyan: \"\" };\nconst COLOR: Palette = {\n reset: \"\\x1b[0m\",\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n red: \"\\x1b[31m\",\n yellow: \"\\x1b[33m\",\n green: \"\\x1b[32m\",\n cyan: \"\\x1b[36m\",\n};\n\n/**\n * Render the JSON report (pretty-printed, no trailing newline).\n *\n * Delegates to core's `toJson` for a monorepo-consistent shape. `opts` is passed\n * straight through (e.g. `{ redactSnippets: true }` for `--no-snippets`).\n */\nexport function renderJson(result: ScanResult, opts?: ReportOptions): string {\n return JSON.stringify(toJson(result, opts), null, 2);\n}\n\n/**\n * Render the SARIF 2.1.0 report (pretty-printed, no trailing newline).\n *\n * Delegates to core's `toSarif` — the monorepo's single source of truth for the\n * SARIF shape (schema, tool driver, rules, taxonomies). `opts` is passed through\n * (e.g. `{ redactSnippets: true }` for `--no-snippets`).\n */\nexport function renderSarif(result: ScanResult, opts?: ReportOptions): string {\n // Advertise the full rule catalog (not just the rules that fired) so SARIF\n // consumers see complete metadata for every rule qScan can emit. The detector\n // registry's catalog is source/config rules only; `dep-vulnerable` comes from\n // the manifest scanner, so add its generic entry — otherwise SARIF would build\n // that rule from the first dependency finding and leak one package's specifics\n // into the shared rule description.\n const catalog = [...defaultRegistry.ruleCatalog(), DEP_VULNERABLE_RULE];\n return JSON.stringify(toSarif(result, { catalog, ...opts }), null, 2);\n}\n\n/**\n * Render a CycloneDX 1.6 CBOM (cryptographic bill of materials) for the scan,\n * pretty-printed with no trailing newline. Delegates to core's `toCbom` so the\n * serialized shape stays consistent across every tool in the monorepo.\n */\nexport function renderCbom(result: ScanResult): string {\n return JSON.stringify(toCbom(result), null, 2);\n}\n\n/**\n * Render the human-readable banner.\n *\n * @param result The scan result.\n * @param opts.color Emit raw ANSI escapes (default: false / plain text).\n * @param opts.topN How many findings to list (default: 5).\n */\nexport function renderHuman(\n result: ScanResult,\n opts: { color?: boolean; topN?: number; tier?: SecurityTier } = {},\n): string {\n const c = opts.color ? COLOR : PLAIN;\n const topN = opts.topN ?? 5;\n const { findings, inventory, filesScanned } = result;\n // `analyzedFiles`: of the scanned files, how many were in a language the\n // scanner can actually inspect for crypto (JS/TS, Python, Go, Java). When it's 0 the\n // readiness score reflects no analyzable code — say so rather than imply safe.\n const analyzedFiles = result.analyzedFiles;\n const noAnalyzable = analyzedFiles === 0;\n const lines: string[] = [];\n\n lines.push(`${c.bold}qScan — quantum-vulnerable cryptography report${c.reset}`);\n const coverage =\n analyzedFiles === undefined\n ? \"\"\n : ` • analyzed: ${analyzedFiles} (${ANALYZABLE_LANGUAGES_LABEL})`;\n lines.push(\n `${c.dim}root: ${result.root} • files scanned: ${filesScanned}${coverage} • qscan v${result.toolVersion}${c.reset}`,\n );\n // Coverage diagnostics: warn when files were skipped, so a low finding count\n // isn't mistaken for a clean scan of the whole tree.\n const diag = result.diagnostics;\n if (diag && (diag.unreadable > 0 || diag.skippedMinified > 0)) {\n const parts: string[] = [];\n if (diag.unreadable > 0) parts.push(`${diag.unreadable} unreadable`);\n if (diag.skippedMinified > 0) parts.push(`${diag.skippedMinified} skipped (minified)`);\n lines.push(`${c.yellow}Coverage: ${parts.join(\", \")} — results may be incomplete.${c.reset}`);\n }\n lines.push(\"\");\n\n if (findings.length === 0) {\n if (noAnalyzable && filesScanned > 0) {\n // Honesty guard: don't let a 100/100 read as \"safe\" when nothing the\n // scanner understands was analyzed — the crypto may live in an\n // unsupported language (Go, Java, Rust, C#, …).\n lines.push(\n `${c.yellow}No analyzable source found.${c.reset} Scanned ${filesScanned} file${\n filesScanned === 1 ? \"\" : \"s\"\n }, but none were in a supported language (${ANALYZABLE_LANGUAGES_LABEL}).`,\n );\n lines.push(\n `${c.dim}The score below covers only what qScan can read today — it is NOT a clean bill of health for this codebase.${c.reset}`,\n );\n lines.push(\n `${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100 (no analyzable source)${c.reset}`,\n );\n lines.push(\"\");\n lines.push(\n `${c.dim}Next step:${c.reset} multi-language support is expanding; track coverage before relying on the score.`,\n );\n return lines.join(\"\\n\");\n }\n lines.push(`${c.green}No quantum-vulnerable cryptography detected.${c.reset}`);\n lines.push(`${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100${c.reset}`);\n lines.push(\"\");\n lines.push(`${c.dim}Next step:${c.reset} keep scanning in CI to catch regressions.`);\n return lines.join(\"\\n\");\n }\n\n // Severity counts, most-severe first.\n const counts = SEVERITY_ORDER.map((sev) => {\n const n = inventory.bySeverity[sev] ?? 0;\n return n > 0 ? `${severityColor(sev, c)}${n} ${sev}${c.reset}` : null;\n }).filter((s): s is string => s !== null);\n\n lines.push(\n `${c.bold}${findings.length} finding${findings.length === 1 ? \"\" : \"s\"}${c.reset} (${counts.join(\", \")})`,\n );\n if (inventory.hndlCount > 0) {\n lines.push(\n `${c.yellow}${inventory.hndlCount}${c.reset} exposed to harvest-now-decrypt-later (HNDL).`,\n );\n }\n lines.push(`${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100${c.reset}`);\n lines.push(\"\");\n\n // Top findings, sorted by severity then file/line for determinism.\n const top = [...findings].sort(compareFindings).slice(0, topN);\n lines.push(`${c.bold}Top findings${c.reset}`);\n for (const f of top) {\n const loc = `${f.location.file}:${f.location.line}`;\n lines.push(\n ` ${severityColor(f.severity, c)}${f.severity.padEnd(8)}${c.reset} ${c.cyan}${f.ruleId}${c.reset} ${loc}`,\n );\n lines.push(` ${f.message}`);\n if (f.remediation) {\n lines.push(` ${c.dim}→ ${f.remediation}${c.reset}`);\n }\n }\n if (findings.length > top.length) {\n lines.push(` ${c.dim}…and ${findings.length - top.length} more${c.reset}`);\n }\n lines.push(\"\");\n lines.push(`${c.dim}Next step:${c.reset} ${nextStep(findings)}`);\n\n // CNSA security-tier migration targets (`--tier`), surfacing remediationForTier.\n if (opts.tier) {\n lines.push(\"\");\n const g = formatTierGuidance(inventory.byAlgorithm, opts.tier);\n lines.push(`${c.bold}${g[0]}${c.reset}`);\n for (const t of g.slice(1)) lines.push(`${c.cyan}${t}${c.reset}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/** Suggest a single concrete next action based on the worst finding. */\nfunction nextStep(findings: Finding[]): string {\n const worst = [...findings].sort(compareFindings)[0];\n if (!worst) return \"review the findings above.\";\n // A dependency finding points at a manifest — you replace the *package*, not\n // \"migrate package.json\". Phrase it as a dependency swap.\n if (worst.category === \"dependency\") {\n return worst.remediation\n ? `replace the vulnerable dependency in ${worst.location.file} — ${worst.remediation}`\n : `replace the vulnerable dependency in ${worst.location.file}.`;\n }\n if (worst.remediation) {\n return `migrate ${worst.location.file} — ${worst.remediation}`;\n }\n return `triage ${worst.ruleId} in ${worst.location.file}:${worst.location.line}.`;\n}\n\n/** Deterministic ordering: most severe first, then file, then line. */\nfunction compareFindings(a: Finding, b: Finding): number {\n const bySev = severityRank(a.severity) - severityRank(b.severity);\n if (bySev !== 0) return bySev;\n const byFile = a.location.file.localeCompare(b.location.file);\n if (byFile !== 0) return byFile;\n return a.location.line - b.location.line;\n}\n\n/** Color the readiness score green/yellow/red by band. */\nfunction readiness(score: number, c: Palette): string {\n const color = score >= 80 ? c.green : score >= 50 ? c.yellow : c.red;\n return `${color}${score}${c.reset}`;\n}\n\n/** Map a severity to its palette color. */\nfunction severityColor(severity: Severity, c: Palette): string {\n switch (severity) {\n case \"critical\":\n case \"high\":\n return c.red;\n case \"medium\":\n return c.yellow;\n default:\n return c.dim;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * qScan report rendering.\n *\n * Produces the three output formats the CLI supports:\n * - `human` — a tasteful plain-text banner (counts, top findings, readiness\n * score, and a one-line next step). Optional raw ANSI color.\n * - `json` — the structured scan result via core's `toJson`.\n * - `sarif` — SARIF 2.1.0 via core's `toSarif`.\n *\n * Only `human` lives here; `json`/`sarif` delegate to `@quantakrypto/core` so the\n * serialized shape stays consistent across every tool in the monorepo.\n */\n\nimport {\n ANALYZABLE_LANGUAGES_LABEL,\n defaultRegistry,\n DEP_VULNERABLE_RULE,\n formatProfileGuidance,\n getStandardsProfile,\n PQC_TRANSITION_NOTE,\n SEVERITY_ORDER,\n severityRank,\n STATEFUL_HBS_NOTE,\n toCbom,\n toJson,\n toOpenVex,\n toSarif,\n mergeCboms,\n} from \"@quantakrypto/core\";\nimport type {\n CycloneDxBom,\n Finding,\n ReportOptions,\n ScanResult,\n SecurityTier,\n Severity,\n StandardsProfile,\n} from \"@quantakrypto/core\";\n\n/** Map the legacy `--tier` to its equivalent standards profile (back-compat alias). */\nconst TIER_TO_PROFILE: Record<SecurityTier, string> = {\n \"category-3\": \"nist\",\n \"category-5\": \"cnsa-2.0\",\n};\n\n/** Resolve the effective standards profile from `--profile` or the `--tier` alias. */\nfunction resolveProfile(profileId?: string, tier?: SecurityTier): StandardsProfile | undefined {\n if (profileId) return getStandardsProfile(profileId);\n if (tier) return getStandardsProfile(TIER_TO_PROFILE[tier]);\n return undefined;\n}\n\n/** Minimal ANSI palette. Empty strings when color is disabled. */\ninterface Palette {\n reset: string;\n bold: string;\n dim: string;\n red: string;\n yellow: string;\n green: string;\n cyan: string;\n}\n\nconst PLAIN: Palette = { reset: \"\", bold: \"\", dim: \"\", red: \"\", yellow: \"\", green: \"\", cyan: \"\" };\nconst COLOR: Palette = {\n reset: \"\\x1b[0m\",\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n red: \"\\x1b[31m\",\n yellow: \"\\x1b[33m\",\n green: \"\\x1b[32m\",\n cyan: \"\\x1b[36m\",\n};\n\n/**\n * Render the JSON report (pretty-printed, no trailing newline).\n *\n * Delegates to core's `toJson` for a monorepo-consistent shape. `opts` is passed\n * straight through (e.g. `{ redactSnippets: true }` for `--no-snippets`).\n */\nexport function renderJson(result: ScanResult, opts?: ReportOptions): string {\n return JSON.stringify(toJson(result, opts), null, 2);\n}\n\n/**\n * Render the SARIF 2.1.0 report (pretty-printed, no trailing newline).\n *\n * Delegates to core's `toSarif` — the monorepo's single source of truth for the\n * SARIF shape (schema, tool driver, rules, taxonomies). `opts` is passed through\n * (e.g. `{ redactSnippets: true }` for `--no-snippets`).\n */\nexport function renderSarif(result: ScanResult, opts?: ReportOptions): string {\n // Advertise the full rule catalog (not just the rules that fired) so SARIF\n // consumers see complete metadata for every rule qScan can emit. The detector\n // registry's catalog is source/config rules only; `dep-vulnerable` comes from\n // the manifest scanner, so add its generic entry — otherwise SARIF would build\n // that rule from the first dependency finding and leak one package's specifics\n // into the shared rule description.\n const catalog = [...defaultRegistry.ruleCatalog(), DEP_VULNERABLE_RULE];\n return JSON.stringify(toSarif(result, { catalog, ...opts }), null, 2);\n}\n\n/**\n * Render a CycloneDX 1.6 CBOM (cryptographic bill of materials) for the scan,\n * pretty-printed with no trailing newline. Delegates to core's `toCbom` so the\n * serialized shape stays consistent across every tool in the monorepo.\n *\n * When `extra` CBOMs are supplied (e.g. a qProbe endpoint CBOM), they are merged\n * with the scan CBOM via core's `mergeCboms`, producing a single combined\n * code + infrastructure bill of materials linked by CycloneDX bom-link.\n */\nexport function renderCbom(result: ScanResult, extra: readonly CycloneDxBom[] = []): string {\n const scanBom = toCbom(result);\n const bom = extra.length > 0 ? mergeCboms([scanBom, ...extra]) : scanBom;\n return JSON.stringify(bom, null, 2);\n}\n\n/**\n * Render an OpenVEX 0.2.0 document for the scan (pretty-printed, no trailing\n * newline). Delegates to core's `toOpenVex` so the VEX shape stays consistent\n * across the monorepo. Carries any `--triage` verdicts into `status_notes`.\n */\nexport function renderVex(result: ScanResult): string {\n return JSON.stringify(toOpenVex(result), null, 2);\n}\n\n/**\n * Render the human-readable banner.\n *\n * @param result The scan result.\n * @param opts.color Emit raw ANSI escapes (default: false / plain text).\n * @param opts.topN How many findings to list (default: 5).\n */\nexport function renderHuman(\n result: ScanResult,\n opts: { color?: boolean; topN?: number; tier?: SecurityTier; profile?: string } = {},\n): string {\n const c = opts.color ? COLOR : PLAIN;\n const topN = opts.topN ?? 5;\n const { findings, inventory, filesScanned } = result;\n // `analyzedFiles`: of the scanned files, how many were in a language the\n // scanner can actually inspect for crypto (JS/TS, Python, Go, Java). When it's 0 the\n // readiness score reflects no analyzable code — say so rather than imply safe.\n const analyzedFiles = result.analyzedFiles;\n const noAnalyzable = analyzedFiles === 0;\n // Partial-coverage honesty: when the analyzable subset is only a small slice of\n // what was scanned, a high score reflects that slice, not the whole tree. We\n // surface a one-line caveat next to the score so the number isn't over-trusted.\n // Skips the zero case (handled explicitly below) and normal repos where most\n // files are analyzable.\n const lowCoverage =\n analyzedFiles !== undefined &&\n analyzedFiles > 0 &&\n filesScanned > 0 &&\n analyzedFiles / filesScanned < 0.25;\n const coverageCaveat = lowCoverage\n ? `${c.dim}Note: the score covers only ${analyzedFiles} analyzable of ${filesScanned} scanned files (${ANALYZABLE_LANGUAGES_LABEL}); crypto in unsupported languages is not reflected.${c.reset}`\n : \"\";\n const lines: string[] = [];\n\n lines.push(`${c.bold}qScan — quantum-vulnerable cryptography report${c.reset}`);\n const coverage =\n analyzedFiles === undefined\n ? \"\"\n : ` • analyzed: ${analyzedFiles} (${ANALYZABLE_LANGUAGES_LABEL})`;\n lines.push(\n `${c.dim}root: ${result.root} • files scanned: ${filesScanned}${coverage} • qscan v${result.toolVersion}${c.reset}`,\n );\n // Coverage diagnostics: warn when files were skipped, so a low finding count\n // isn't mistaken for a clean scan of the whole tree.\n const diag = result.diagnostics;\n if (diag && (diag.unreadable > 0 || diag.skippedMinified > 0)) {\n const parts: string[] = [];\n if (diag.unreadable > 0) parts.push(`${diag.unreadable} unreadable`);\n if (diag.skippedMinified > 0) parts.push(`${diag.skippedMinified} skipped (minified)`);\n lines.push(`${c.yellow}Coverage: ${parts.join(\", \")} — results may be incomplete.${c.reset}`);\n }\n lines.push(\"\");\n\n if (findings.length === 0) {\n if (noAnalyzable && filesScanned > 0) {\n // Honesty guard: don't let a 100/100 read as \"safe\" when nothing the\n // scanner understands was analyzed — the crypto may live in an\n // unsupported language (Go, Java, Rust, C#, …).\n lines.push(\n `${c.yellow}No analyzable source found.${c.reset} Scanned ${filesScanned} file${\n filesScanned === 1 ? \"\" : \"s\"\n }, but none were in a supported language (${ANALYZABLE_LANGUAGES_LABEL}).`,\n );\n lines.push(\n `${c.dim}The score below covers only what qScan can read today — it is NOT a clean bill of health for this codebase.${c.reset}`,\n );\n lines.push(\n `${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100 (no analyzable source)${c.reset}`,\n );\n lines.push(\"\");\n lines.push(\n `${c.dim}Next step:${c.reset} multi-language support is expanding; track coverage before relying on the score.`,\n );\n return lines.join(\"\\n\");\n }\n lines.push(`${c.green}No quantum-vulnerable cryptography detected.${c.reset}`);\n lines.push(`${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100${c.reset}`);\n if (coverageCaveat) lines.push(coverageCaveat);\n lines.push(\"\");\n lines.push(`${c.dim}Next step:${c.reset} keep scanning in CI to catch regressions.`);\n return lines.join(\"\\n\");\n }\n\n // Severity counts, most-severe first.\n const counts = SEVERITY_ORDER.map((sev) => {\n const n = inventory.bySeverity[sev] ?? 0;\n return n > 0 ? `${severityColor(sev, c)}${n} ${sev}${c.reset}` : null;\n }).filter((s): s is string => s !== null);\n\n lines.push(\n `${c.bold}${findings.length} finding${findings.length === 1 ? \"\" : \"s\"}${c.reset} (${counts.join(\", \")})`,\n );\n if (inventory.hndlCount > 0) {\n lines.push(\n `${c.yellow}${inventory.hndlCount}${c.reset} exposed to harvest-now-decrypt-later (HNDL).`,\n );\n }\n lines.push(`${c.bold}Readiness score: ${readiness(inventory.readinessScore, c)}/100${c.reset}`);\n if (coverageCaveat) lines.push(coverageCaveat);\n lines.push(\"\");\n\n // Top findings, sorted by severity then file/line for determinism.\n const top = [...findings].sort(compareFindings).slice(0, topN);\n lines.push(`${c.bold}Top findings${c.reset}`);\n for (const f of top) {\n const loc = `${f.location.file}:${f.location.line}`;\n lines.push(\n ` ${severityColor(f.severity, c)}${f.severity.padEnd(8)}${c.reset} ${c.cyan}${f.ruleId}${c.reset} ${loc}`,\n );\n lines.push(` ${f.message}`);\n if (f.remediation) {\n lines.push(` ${c.dim}→ ${f.remediation}${c.reset}`);\n }\n }\n if (findings.length > top.length) {\n lines.push(` ${c.dim}…and ${findings.length - top.length} more${c.reset}`);\n }\n lines.push(\"\");\n lines.push(`${c.dim}Next step:${c.reset} ${nextStep(findings)}`);\n\n // Regime-tailored migration targets (`--profile`, or the `--tier` alias). Surfaces\n // the parameter sets AND the regime's hybrid stance so guidance isn't regime-wrong.\n const profile = resolveProfile(opts.profile, opts.tier);\n if (profile) {\n lines.push(\"\");\n const g = formatProfileGuidance(inventory.byAlgorithm, profile);\n lines.push(`${c.bold}${g[0]}${c.reset}`);\n for (const t of g.slice(1)) lines.push(`${c.cyan}${t}${c.reset}`);\n }\n\n // Forward-looking standards + the IR 8547 migration deadline (HQC / FN-DSA /\n // X-Wing) — the long-horizon guidance behind anything flagged above.\n lines.push(\"\");\n lines.push(`${c.bold}Standards & timeline${c.reset}`);\n lines.push(`${c.dim}${PQC_TRANSITION_NOTE}${c.reset}`);\n if (findings.some((f) => f.category === \"signature\")) {\n lines.push(`${c.dim}${STATEFUL_HBS_NOTE}${c.reset}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/** Suggest a single concrete next action based on the worst finding. */\nfunction nextStep(findings: Finding[]): string {\n const worst = [...findings].sort(compareFindings)[0];\n if (!worst) return \"review the findings above.\";\n // A dependency finding points at a manifest — you replace the *package*, not\n // \"migrate package.json\". Phrase it as a dependency swap.\n if (worst.category === \"dependency\") {\n return worst.remediation\n ? `replace the vulnerable dependency in ${worst.location.file} — ${worst.remediation}`\n : `replace the vulnerable dependency in ${worst.location.file}.`;\n }\n if (worst.remediation) {\n return `migrate ${worst.location.file} — ${worst.remediation}`;\n }\n return `triage ${worst.ruleId} in ${worst.location.file}:${worst.location.line}.`;\n}\n\n/** Deterministic ordering: most severe first, then file, then line. */\nfunction compareFindings(a: Finding, b: Finding): number {\n const bySev = severityRank(a.severity) - severityRank(b.severity);\n if (bySev !== 0) return bySev;\n const byFile = a.location.file.localeCompare(b.location.file);\n if (byFile !== 0) return byFile;\n return a.location.line - b.location.line;\n}\n\n/** Color the readiness score green/yellow/red by band. */\nfunction readiness(score: number, c: Palette): string {\n const color = score >= 80 ? c.green : score >= 50 ? c.yellow : c.red;\n return `${color}${score}${c.reset}`;\n}\n\n/** Map a severity to its palette color. */\nfunction severityColor(severity: Severity, c: Palette): string {\n switch (severity) {\n case \"critical\":\n case \"high\":\n return c.red;\n case \"medium\":\n return c.yellow;\n default:\n return c.dim;\n }\n}\n"]}
|
package/dist/sign.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAiBzD,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CA+B7D"}
|
package/dist/sign.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build an {@link EvidenceSigner} from an operator-provided shell command, for the
|
|
3
|
+
* evidence attestation's detached signature / RFC-3161 timestamp (`--sign` /
|
|
4
|
+
* `--timestamp`). Per ADR-0004 the tool implements NO cryptography: it pipes the
|
|
5
|
+
* report's `contentHash` to the operator's own command (an `openssl` / `cosign`
|
|
6
|
+
* invocation, a TSA client, …) on stdin and records that command's stdout verbatim.
|
|
7
|
+
*
|
|
8
|
+
* The command is run through the platform shell so it can pipe (`… | base64`). The
|
|
9
|
+
* payload is passed on STDIN, never interpolated into the command string, so the
|
|
10
|
+
* (tool-controlled) contentHash can't alter the (operator-controlled) command.
|
|
11
|
+
*/
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
const SIGN_TIMEOUT_MS = 30_000;
|
|
14
|
+
const SIGN_MAX_BUFFER = 1 << 20; // 1 MiB — a signature / timestamp token is small
|
|
15
|
+
/** Non-sensitive provenance label: the program name of the command (no args/paths). */
|
|
16
|
+
function signerLabel(command) {
|
|
17
|
+
// Skip any leading `KEY=value` env-assignment prefix (e.g. `AWS_SECRET=… cosign …`)
|
|
18
|
+
// so a secret in it never lands in the recorded `signedWith`, then take the program.
|
|
19
|
+
const prog = command
|
|
20
|
+
.trim()
|
|
21
|
+
.split(/\s+/)
|
|
22
|
+
.find((t) => !/^[A-Za-z_][A-Za-z0-9_]*=/.test(t)) || "external-signer";
|
|
23
|
+
return prog.replace(/^.*[/\\]/, ""); // basename, so a key path in args never leaks
|
|
24
|
+
}
|
|
25
|
+
/** An EvidenceSigner that shells out to `command`, piping the payload on stdin. */
|
|
26
|
+
export function commandSigner(command) {
|
|
27
|
+
const label = signerLabel(command);
|
|
28
|
+
return {
|
|
29
|
+
label,
|
|
30
|
+
sign(payload) {
|
|
31
|
+
const res = spawnSync(command, {
|
|
32
|
+
shell: true,
|
|
33
|
+
input: payload,
|
|
34
|
+
encoding: "utf8",
|
|
35
|
+
timeout: SIGN_TIMEOUT_MS,
|
|
36
|
+
maxBuffer: SIGN_MAX_BUFFER,
|
|
37
|
+
});
|
|
38
|
+
if (res.error) {
|
|
39
|
+
throw new Error(`--sign/--timestamp: command "${label}" failed to run: ${res.error.message}`);
|
|
40
|
+
}
|
|
41
|
+
if (res.status !== 0) {
|
|
42
|
+
const how = res.status !== null ? `exited ${res.status}` : `terminated on ${res.signal}`;
|
|
43
|
+
const detail = (res.stderr || "").trim().slice(0, 200);
|
|
44
|
+
throw new Error(`--sign/--timestamp: command "${label}" ${how}${detail ? `: ${detail}` : ""}`);
|
|
45
|
+
}
|
|
46
|
+
const out = (res.stdout || "").trim();
|
|
47
|
+
if (!out) {
|
|
48
|
+
throw new Error(`--sign/--timestamp: command "${label}" produced no output`);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=sign.js.map
|
package/dist/sign.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.js","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI/C,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,iDAAiD;AAElF,uFAAuF;AACvF,SAAS,WAAW,CAAC,OAAe;IAClC,oFAAoF;IACpF,qFAAqF;IACrF,MAAM,IAAI,GACR,OAAO;SACJ,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC;IAC3E,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,8CAA8C;AACrF,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO;QACL,KAAK;QACL,IAAI,CAAC,OAAe;YAClB,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,EAAE;gBAC7B,KAAK,EAAE,IAAI;gBACX,KAAK,EAAE,OAAO;gBACd,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,eAAe;gBACxB,SAAS,EAAE,eAAe;aAC3B,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CACb,gCAAgC,KAAK,oBAAoB,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAC7E,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrB,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,iBAAiB,GAAG,CAAC,MAAM,EAAE,CAAC;gBACzF,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBACvD,MAAM,IAAI,KAAK,CACb,gCAAgC,KAAK,KAAK,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9E,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,sBAAsB,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Build an {@link EvidenceSigner} from an operator-provided shell command, for the\n * evidence attestation's detached signature / RFC-3161 timestamp (`--sign` /\n * `--timestamp`). Per ADR-0004 the tool implements NO cryptography: it pipes the\n * report's `contentHash` to the operator's own command (an `openssl` / `cosign`\n * invocation, a TSA client, …) on stdin and records that command's stdout verbatim.\n *\n * The command is run through the platform shell so it can pipe (`… | base64`). The\n * payload is passed on STDIN, never interpolated into the command string, so the\n * (tool-controlled) contentHash can't alter the (operator-controlled) command.\n */\nimport { spawnSync } from \"node:child_process\";\n\nimport type { EvidenceSigner } from \"@quantakrypto/core\";\n\nconst SIGN_TIMEOUT_MS = 30_000;\nconst SIGN_MAX_BUFFER = 1 << 20; // 1 MiB — a signature / timestamp token is small\n\n/** Non-sensitive provenance label: the program name of the command (no args/paths). */\nfunction signerLabel(command: string): string {\n // Skip any leading `KEY=value` env-assignment prefix (e.g. `AWS_SECRET=… cosign …`)\n // so a secret in it never lands in the recorded `signedWith`, then take the program.\n const prog =\n command\n .trim()\n .split(/\\s+/)\n .find((t) => !/^[A-Za-z_][A-Za-z0-9_]*=/.test(t)) || \"external-signer\";\n return prog.replace(/^.*[/\\\\]/, \"\"); // basename, so a key path in args never leaks\n}\n\n/** An EvidenceSigner that shells out to `command`, piping the payload on stdin. */\nexport function commandSigner(command: string): EvidenceSigner {\n const label = signerLabel(command);\n return {\n label,\n sign(payload: string): string {\n const res = spawnSync(command, {\n shell: true,\n input: payload,\n encoding: \"utf8\",\n timeout: SIGN_TIMEOUT_MS,\n maxBuffer: SIGN_MAX_BUFFER,\n });\n if (res.error) {\n throw new Error(\n `--sign/--timestamp: command \"${label}\" failed to run: ${res.error.message}`,\n );\n }\n if (res.status !== 0) {\n const how = res.status !== null ? `exited ${res.status}` : `terminated on ${res.signal}`;\n const detail = (res.stderr || \"\").trim().slice(0, 200);\n throw new Error(\n `--sign/--timestamp: command \"${label}\" ${how}${detail ? `: ${detail}` : \"\"}`,\n );\n }\n const out = (res.stdout || \"\").trim();\n if (!out) {\n throw new Error(`--sign/--timestamp: command \"${label}\" produced no output`);\n }\n return out;\n },\n };\n}\n"]}
|
package/dist/triage-run.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { ContextLevel, Finding, ScanResult, Severity, TriageVerdict } from "@quantakrypto/core";
|
|
2
2
|
import type { LlmProvider } from "./args.js";
|
|
3
|
-
/** Default cap on findings sent to the LLM per triage run (spend/DoS guard). */
|
|
4
|
-
export declare const DEFAULT_MAX_TRIAGE = 100;
|
|
5
3
|
/** Injectable triage function (default wraps `@quantakrypto/agent`). */
|
|
6
4
|
export type TriageFn = (findings: readonly Finding[]) => Promise<Map<string, TriageVerdict>>;
|
|
7
|
-
|
|
5
|
+
interface RunTriageOptions {
|
|
8
6
|
level: ContextLevel;
|
|
9
7
|
floor?: Severity;
|
|
10
8
|
dryRun?: boolean;
|
|
@@ -20,7 +18,7 @@ export interface RunTriageOptions {
|
|
|
20
18
|
readFile?: (relPath: string) => Promise<string>;
|
|
21
19
|
stderr?: (s: string) => void;
|
|
22
20
|
}
|
|
23
|
-
|
|
21
|
+
interface RunTriageResult {
|
|
24
22
|
/** When `--dry-run`, the preflight text to show instead of a normal report. */
|
|
25
23
|
preflight?: string;
|
|
26
24
|
}
|
|
@@ -30,4 +28,5 @@ export interface RunTriageResult {
|
|
|
30
28
|
* degrade gracefully: the scan/report proceed without triage.
|
|
31
29
|
*/
|
|
32
30
|
export declare function runTriage(result: ScanResult, opts: RunTriageOptions): Promise<RunTriageResult>;
|
|
31
|
+
export {};
|
|
33
32
|
//# sourceMappingURL=triage-run.d.ts.map
|
package/dist/triage-run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triage-run.d.ts","sourceRoot":"","sources":["../src/triage-run.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"triage-run.d.ts","sourceRoot":"","sources":["../src/triage-run.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,YAAY,EACZ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAsB7C,wEAAwE;AACxE,MAAM,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAE7F,UAAU,gBAAgB;IACxB,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAaD;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,eAAe,CAAC,CA8F1B"}
|