@quantakrypto/qscan 0.4.4 → 0.6.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 +37 -9
- package/dist/args.d.ts +69 -9
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +123 -5
- package/dist/args.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +98 -10
- 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 +50 -2
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +60 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +178 -23
- 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 +14 -2
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +61 -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 +61 -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
|
@@ -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, HndlReport, 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,7 @@ export declare function renderHuman(result: ScanResult, opts?: {
|
|
|
43
53
|
color?: boolean;
|
|
44
54
|
topN?: number;
|
|
45
55
|
tier?: SecurityTier;
|
|
56
|
+
profile?: string;
|
|
57
|
+
hndl?: HndlReport;
|
|
46
58
|
}): string;
|
|
47
59
|
//# 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,UAAU,EACV,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;IACJ,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;CACd,GACL,MAAM,CAsIR"}
|
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.
|
|
@@ -150,14 +177,20 @@ export function renderHuman(result, opts = {}) {
|
|
|
150
177
|
}
|
|
151
178
|
lines.push("");
|
|
152
179
|
lines.push(`${c.dim}Next step:${c.reset} ${nextStep(findings)}`);
|
|
153
|
-
//
|
|
154
|
-
|
|
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) {
|
|
155
184
|
lines.push("");
|
|
156
|
-
const g =
|
|
185
|
+
const g = formatProfileGuidance(inventory.byAlgorithm, profile);
|
|
157
186
|
lines.push(`${c.bold}${g[0]}${c.reset}`);
|
|
158
187
|
for (const t of g.slice(1))
|
|
159
188
|
lines.push(`${c.cyan}${t}${c.reset}`);
|
|
160
189
|
}
|
|
190
|
+
// HNDL exposure ranking (`--hndl`): the risk-ranked view that reorders the
|
|
191
|
+
// backlog by real exposure rather than finding count.
|
|
192
|
+
if (opts.hndl)
|
|
193
|
+
lines.push("", ...hndlSection(opts.hndl, c));
|
|
161
194
|
// Forward-looking standards + the IR 8547 migration deadline (HQC / FN-DSA /
|
|
162
195
|
// X-Wing) — the long-horizon guidance behind anything flagged above.
|
|
163
196
|
lines.push("");
|
|
@@ -168,6 +201,28 @@ export function renderHuman(result, opts = {}) {
|
|
|
168
201
|
}
|
|
169
202
|
return lines.join("\n");
|
|
170
203
|
}
|
|
204
|
+
/** Render the HNDL exposure section for the human report. */
|
|
205
|
+
function hndlSection(hndl, c) {
|
|
206
|
+
const s = hndl.summary;
|
|
207
|
+
const out = [`${c.bold}HNDL exposure${c.reset}`];
|
|
208
|
+
out.push(`${c.dim}horizon: quantum threat ${hndl.horizon.quantumThreatYears}y · migration ${hndl.horizon.migrationHorizonYears}y · model v${hndl.modelVersion}${c.reset}`);
|
|
209
|
+
out.push(`max exposure ${exposureColor(s.maxExposure, c)}${s.maxExposure}/100${c.reset} • mean ${s.meanExposure}/100 • ${s.moscaBreaches} finding(s) breach Mosca's inequality`);
|
|
210
|
+
if (s.assetsDeclared > 0) {
|
|
211
|
+
out.push(`${c.dim}${s.assetsWithFindings}/${s.assetsDeclared} declared assets carry findings; ${s.assetsOutlivingHorizon} have secrecy outliving the threat horizon.${c.reset}`);
|
|
212
|
+
}
|
|
213
|
+
if (s.topExposures.length > 0) {
|
|
214
|
+
out.push(`${c.bold}Top exposures${c.reset}`);
|
|
215
|
+
for (const e of s.topExposures) {
|
|
216
|
+
const asset = e.dataAsset ? e.dataAsset : `${c.dim}(unbound)${c.reset}`;
|
|
217
|
+
out.push(` ${exposureColor(e.exposureScore, c)}${String(e.exposureScore).padStart(3)}${c.reset} ${c.cyan}${e.ruleId}${c.reset} ${e.file} → ${asset}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
/** Color an exposure score red/yellow/dim by band. */
|
|
223
|
+
function exposureColor(score, c) {
|
|
224
|
+
return score >= 50 ? c.red : score >= 20 ? c.yellow : c.dim;
|
|
225
|
+
}
|
|
171
226
|
/** Suggest a single concrete next action based on the worst finding. */
|
|
172
227
|
function nextStep(findings) {
|
|
173
228
|
const worst = [...findings].sort(compareFindings)[0];
|
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 PQC_TRANSITION_NOTE,\n SEVERITY_ORDER,\n severityRank,\n STATEFUL_HBS_NOTE,\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 // 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 // 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 // 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"]}
|
|
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 HndlReport,\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: {\n color?: boolean;\n topN?: number;\n tier?: SecurityTier;\n profile?: string;\n hndl?: HndlReport;\n } = {},\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 // HNDL exposure ranking (`--hndl`): the risk-ranked view that reorders the\n // backlog by real exposure rather than finding count.\n if (opts.hndl) lines.push(\"\", ...hndlSection(opts.hndl, c));\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/** Render the HNDL exposure section for the human report. */\nfunction hndlSection(hndl: HndlReport, c: Palette): string[] {\n const s = hndl.summary;\n const out: string[] = [`${c.bold}HNDL exposure${c.reset}`];\n out.push(\n `${c.dim}horizon: quantum threat ${hndl.horizon.quantumThreatYears}y · migration ${hndl.horizon.migrationHorizonYears}y · model v${hndl.modelVersion}${c.reset}`,\n );\n out.push(\n `max exposure ${exposureColor(s.maxExposure, c)}${s.maxExposure}/100${c.reset} • mean ${s.meanExposure}/100 • ${s.moscaBreaches} finding(s) breach Mosca's inequality`,\n );\n if (s.assetsDeclared > 0) {\n out.push(\n `${c.dim}${s.assetsWithFindings}/${s.assetsDeclared} declared assets carry findings; ${s.assetsOutlivingHorizon} have secrecy outliving the threat horizon.${c.reset}`,\n );\n }\n if (s.topExposures.length > 0) {\n out.push(`${c.bold}Top exposures${c.reset}`);\n for (const e of s.topExposures) {\n const asset = e.dataAsset ? e.dataAsset : `${c.dim}(unbound)${c.reset}`;\n out.push(\n ` ${exposureColor(e.exposureScore, c)}${String(e.exposureScore).padStart(3)}${c.reset} ${c.cyan}${e.ruleId}${c.reset} ${e.file} → ${asset}`,\n );\n }\n }\n return out;\n}\n\n/** Color an exposure score red/yellow/dim by band. */\nfunction exposureColor(score: number, c: Palette): string {\n return score >= 50 ? c.red : score >= 20 ? c.yellow : c.dim;\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,CAsC7D"}
|
package/dist/sign.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
// A command that exits before draining stdin makes spawnSync surface an EPIPE in
|
|
39
|
+
// res.error even though the child ran and returned an exit status (seen on Linux,
|
|
40
|
+
// not macOS). The exit status is the meaningful failure signal, so report a
|
|
41
|
+
// non-zero exit (or a terminating signal) FIRST, and only treat res.error as
|
|
42
|
+
// fatal when the child never produced a status (a real spawn failure, e.g. ENOENT).
|
|
43
|
+
if (res.status !== null && res.status !== 0) {
|
|
44
|
+
const detail = (res.stderr || "").trim().slice(0, 200);
|
|
45
|
+
throw new Error(`--sign/--timestamp: command "${label}" exited ${res.status}${detail ? `: ${detail}` : ""}`);
|
|
46
|
+
}
|
|
47
|
+
if (res.signal) {
|
|
48
|
+
throw new Error(`--sign/--timestamp: command "${label}" terminated on ${res.signal}`);
|
|
49
|
+
}
|
|
50
|
+
if (res.error) {
|
|
51
|
+
throw new Error(`--sign/--timestamp: command "${label}" failed to run: ${res.error.message}`);
|
|
52
|
+
}
|
|
53
|
+
const out = (res.stdout || "").trim();
|
|
54
|
+
if (!out) {
|
|
55
|
+
throw new Error(`--sign/--timestamp: command "${label}" produced no output`);
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# 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,iFAAiF;YACjF,kFAAkF;YAClF,4EAA4E;YAC5E,6EAA6E;YAC7E,oFAAoF;YACpF,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5C,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,YAAY,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5F,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,mBAAmB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxF,CAAC;YACD,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,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 // A command that exits before draining stdin makes spawnSync surface an EPIPE in\n // res.error even though the child ran and returned an exit status (seen on Linux,\n // not macOS). The exit status is the meaningful failure signal, so report a\n // non-zero exit (or a terminating signal) FIRST, and only treat res.error as\n // fatal when the child never produced a status (a real spawn failure, e.g. ENOENT).\n if (res.status !== null && res.status !== 0) {\n const detail = (res.stderr || \"\").trim().slice(0, 200);\n throw new Error(\n `--sign/--timestamp: command \"${label}\" exited ${res.status}${detail ? `: ${detail}` : \"\"}`,\n );\n }\n if (res.signal) {\n throw new Error(`--sign/--timestamp: command \"${label}\" terminated on ${res.signal}`);\n }\n if (res.error) {\n throw new Error(\n `--sign/--timestamp: command \"${label}\" failed to run: ${res.error.message}`,\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"}
|
package/dist/triage-run.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { readFile as fsReadFile } from "node:fs/promises";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import process from "node:process";
|
|
13
|
-
import { buildContext, compareFindings, fingerprintFinding, renderPreflight, } from "@quantakrypto/core";
|
|
13
|
+
import { buildContext, compareFindings, fingerprintFinding, renderPreflight, severityRank, } from "@quantakrypto/core";
|
|
14
14
|
const SEVERITY_RANK = {
|
|
15
15
|
critical: 0,
|
|
16
16
|
high: 1,
|
|
@@ -19,7 +19,7 @@ const SEVERITY_RANK = {
|
|
|
19
19
|
info: 4,
|
|
20
20
|
};
|
|
21
21
|
/** Default cap on findings sent to the LLM per triage run (spend/DoS guard). */
|
|
22
|
-
|
|
22
|
+
const DEFAULT_MAX_TRIAGE = 100;
|
|
23
23
|
/** The model's `rationale` is untrusted text that lands in JSON/SARIF output.
|
|
24
24
|
* Strip control characters and clamp length so a prompt-injected rationale can't
|
|
25
25
|
* smuggle escape sequences or unbounded content into a downstream consumer. */
|
|
@@ -82,12 +82,19 @@ export async function runTriage(result, opts) {
|
|
|
82
82
|
model,
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
|
-
// Spend/DoS guard: triage only up to maxFindings
|
|
86
|
-
//
|
|
85
|
+
// Spend/DoS guard: triage only up to maxFindings, and when capping, pick the TOP by
|
|
86
|
+
// SEVERITY (most severe first) — not by file-path order — so a critical in a
|
|
87
|
+
// late-sorting file isn't silently dropped from triage and sunk to the bottom of the
|
|
88
|
+
// report. Under the cap, triage exactly the `targets` (findings at/above the floor),
|
|
89
|
+
// not the full result set. Ties fall back to the stable file/line/ruleId order.
|
|
87
90
|
const maxFindings = opts.maxFindings ?? DEFAULT_MAX_TRIAGE;
|
|
91
|
+
const bySeverityThenOrder = (a, b) => {
|
|
92
|
+
const d = severityRank(a.severity) - severityRank(b.severity);
|
|
93
|
+
return d !== 0 ? d : compareFindings(a, b);
|
|
94
|
+
};
|
|
88
95
|
const toTriage = targets.length > maxFindings
|
|
89
|
-
? [...targets].sort(
|
|
90
|
-
:
|
|
96
|
+
? [...targets].sort(bySeverityThenOrder).slice(0, maxFindings)
|
|
97
|
+
: targets;
|
|
91
98
|
if (targets.length > maxFindings) {
|
|
92
99
|
stderr(`qscan: --triage capped at ${maxFindings} findings (${targets.length} at/above floor); raise --max-findings to triage more.\n`);
|
|
93
100
|
}
|