pqcheck 0.7.3 → 0.7.5

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.
Files changed (2) hide show
  1. package/bin/pqcheck.js +39 -5
  2. package/package.json +1 -1
package/bin/pqcheck.js CHANGED
@@ -7,7 +7,7 @@
7
7
  // =============================================================================
8
8
 
9
9
  const API_BASE = process.env.PQCHECK_API_BASE || "https://quantapact.com";
10
- const VERSION = "0.7.3";
10
+ const VERSION = "0.7.5";
11
11
 
12
12
  const ANSI = {
13
13
  reset: "\x1b[0m",
@@ -147,6 +147,13 @@ async function runOneScan({ domain, format, quiet, threshold, webhookUrl, multi
147
147
  }).catch(() => { /* best-effort — never fail the scan on webhook delivery */ });
148
148
  }
149
149
 
150
+ // In --quiet mode the score still goes to stdout (script-pipeable), but a
151
+ // degraded warning lands on stderr so silent fallback to cached data can't
152
+ // mislead a CI gate or one-off check.
153
+ if (quiet && report._meta?.degraded) {
154
+ console.error(`pqcheck: ⚠ ${domain} — using cached score (live probe failed: ${report._meta.degradedReason || "unknown"}; last verified ${report._meta.lastUpdated || "?"})`);
155
+ }
156
+
150
157
  // Output dispatch
151
158
  if (quiet) {
152
159
  if (multi) {
@@ -239,10 +246,11 @@ async function runWatch({ domains, format, quiet, threshold, webhookUrl, interva
239
246
  printMarkdown(report, true);
240
247
  } else {
241
248
  const stamp = new Date().toISOString().slice(11, 19);
249
+ const degradedTag = report._meta?.degraded ? color("yellow", ` ⚠ cached (${report._meta.degradedReason || "probe failed"})`) : "";
242
250
  if (changed) {
243
- console.log(color("yellow", `[${stamp}] ${domain}: ${prev} → ${report.score} (${report.scoreLabel}) ${color("yellow", "★ changed")}`));
251
+ console.log(color("yellow", `[${stamp}] ${domain}: ${prev} → ${report.score} (${report.scoreLabel}) ${color("yellow", "★ changed")}${degradedTag}`));
244
252
  } else if (!quiet) {
245
- console.log(color("dim", `[${stamp}] ${domain}: ${report.score} (${report.scoreLabel})`));
253
+ console.log(color("dim", `[${stamp}] ${domain}: ${report.score} (${report.scoreLabel})${degradedTag}`));
246
254
  }
247
255
  }
248
256
  } catch (err) {
@@ -388,7 +396,21 @@ function printReport(r) {
388
396
  console.log("");
389
397
  console.log(` ${color("bold", r.domain)}`);
390
398
  console.log(color("dim", " ─────────────────────────────────────"));
391
- console.log(` ${color("bold", "PUBLIC SURFACE BLAST RADIUS:")} ${color(labelColor, `${r.score} / 10`)} ${color(labelColor, `(${r.scoreLabel})`)}`);
399
+ // Loud warning when the API fell back to a cached value because three live
400
+ // probe attempts came up degraded. Devs need to know they may be looking at
401
+ // stale data — silent fallback would erode trust in the tool.
402
+ const meta = r._meta || {};
403
+ if (meta.degraded) {
404
+ const since = meta.lastUpdated ? new Date(meta.lastUpdated).toUTCString() : "unknown";
405
+ console.log("");
406
+ console.log(color("yellow", " ⚠ WARNING: showing last known-good cached score"));
407
+ console.log(color("yellow", ` Reason: ${meta.degradedReason || "unknown"}`));
408
+ console.log(color("yellow", ` Last verified: ${since}`));
409
+ console.log(color("dim", " The live probe failed after 3 retries. Re-run shortly to refresh."));
410
+ console.log("");
411
+ }
412
+ const degradedMark = meta.degraded ? color("yellow", " *") : "";
413
+ console.log(` ${color("bold", "PUBLIC SURFACE BLAST RADIUS:")} ${color(labelColor, `${r.score} / 10`)}${degradedMark} ${color(labelColor, `(${r.scoreLabel})`)}`);
392
414
  console.log("");
393
415
  console.log(color("dim", " Public surface signals:"));
394
416
  console.log(` • TLS: ${r.publicSurface.tlsVersion ?? "?"} ${r.publicSurface.cipher ? color("dim", `(${r.publicSurface.cipher})`) : ""}`);
@@ -1168,9 +1190,13 @@ function reportToSarif(report) {
1168
1190
  ruleId: `pqcheck-${i + 1}`,
1169
1191
  level: sevMap[f.severity] || "note",
1170
1192
  message: { text: `${f.title || "finding"}${f.detail ? ` — ${f.detail}` : ""}` },
1193
+ // GitHub Code Scanning requires file: scheme (or relative path) for
1194
+ // artifactLocation.uri — https:// URIs are rejected. Use a virtual
1195
+ // relative path so findings show up cleanly in the Security tab.
1171
1196
  locations: [{
1172
1197
  physicalLocation: {
1173
- artifactLocation: { uri: `https://${report.domain || ""}` },
1198
+ artifactLocation: { uri: `quantapact-scan/${report.domain || "unknown"}.txt` },
1199
+ region: { startLine: 1, startColumn: 1 },
1174
1200
  },
1175
1201
  }],
1176
1202
  properties: {
@@ -1178,6 +1204,7 @@ function reportToSarif(report) {
1178
1204
  score: report.score,
1179
1205
  grade: report.grade,
1180
1206
  severity: f.severity,
1207
+ reportUrl: `https://www.quantapact.com/r/${report.domain || ""}`,
1181
1208
  },
1182
1209
  })),
1183
1210
  properties: {
@@ -1193,6 +1220,13 @@ function printGitHubActionAnnotations(report) {
1193
1220
  // GitHub Actions workflow command syntax: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
1194
1221
  const findings = Array.isArray(report.findings) ? report.findings : [];
1195
1222
  const sevMap = { critical: "error", high: "error", medium: "warning", low: "notice" };
1223
+ // Surface degraded-cache state as a workflow warning so it lands in the
1224
+ // PR check summary — devs need to know they may be gating on stale data.
1225
+ if (report._meta?.degraded) {
1226
+ const reason = String(report._meta.degradedReason || "live probe failed").replace(/[\r\n]/g, " ").replace(/::/g, ":");
1227
+ const since = String(report._meta.lastUpdated || "unknown");
1228
+ console.log(`::warning title=Quantapact: cached score (live probe failed)::Showing last known-good score from ${since}. Reason: ${reason}. Re-run shortly for a fresh probe.`);
1229
+ }
1196
1230
  // Top-line score/grade as a notice
1197
1231
  console.log(`::notice title=Quantapact: ${report.domain}::Grade ${report.grade || "?"} · score ${report.score ?? "?"} / 10`);
1198
1232
  for (const f of findings) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pqcheck",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "Decryption Blast Radius scanner — find out how much of your data unlocks when quantum decryption arrives.",
5
5
  "keywords": [
6
6
  "post-quantum",