pqcheck 0.7.4 → 0.7.6
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 +9 -1
- package/bin/pqcheck.js +35 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,12 @@ The same scanner that powers [quantapact.com](https://quantapact.com), the brows
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
## What's new in 0.7.6
|
|
16
|
+
|
|
17
|
+
User-Agent now consistently tags the subcommand on every request (`pqcheck-cli/0.7.6 (scan)`, `(lock)`, `(deps)`, `(history)`, `(watch)`). Lets the server aggregate adoption by subcommand. No new data collected — the subcommand token rides inside the User-Agent header that has always been logged anonymously. See [privacy](https://quantapact.com/privacy) and [CHANGELOG.md](./CHANGELOG.md).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
15
21
|
## What it does
|
|
16
22
|
|
|
17
23
|
`pqcheck` scans any HTTPS domain and computes its **Decryption Blast Radius score** — the first continuous metric for harvest-now-decrypt-later (HNDL) risk. Every other TLS scanner answers "is post-quantum cryptography enabled?" with yes/no. `pqcheck` answers the question that actually matters: *if an adversary harvests this traffic today and decrypts it in 2035, how much past + future data unlocks?*
|
|
@@ -233,6 +239,8 @@ MIT. © 2026 Quantapact.
|
|
|
233
239
|
|
|
234
240
|
---
|
|
235
241
|
|
|
236
|
-
**Source:** [github.com/quantapact/pqcheck](https://github.com/quantapact/pqcheck)
|
|
242
|
+
**Source:** [github.com/quantapact/pqcheck](https://github.com/quantapact/pqcheck)
|
|
243
|
+
|
|
244
|
+
**Changelog:** [CHANGELOG.md](./CHANGELOG.md) for version-by-version release notes.
|
|
237
245
|
|
|
238
246
|
**Issues / feedback:** [quantapact.com/feedback](https://quantapact.com/feedback) or open an issue on the repo.
|
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.
|
|
10
|
+
const VERSION = "0.7.6";
|
|
11
11
|
|
|
12
12
|
const ANSI = {
|
|
13
13
|
reset: "\x1b[0m",
|
|
@@ -122,7 +122,7 @@ async function runOneScan({ domain, format, quiet, threshold, webhookUrl, multi
|
|
|
122
122
|
try {
|
|
123
123
|
const resp = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(domain)}`, {
|
|
124
124
|
method: "GET",
|
|
125
|
-
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION}` },
|
|
125
|
+
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION} (scan)` },
|
|
126
126
|
});
|
|
127
127
|
if (!quiet && format === "text") process.stderr.write("\r\x1b[K");
|
|
128
128
|
if (!resp.ok) {
|
|
@@ -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) {
|
|
@@ -208,7 +215,7 @@ async function runWatch({ domains, format, quiet, threshold, webhookUrl, interva
|
|
|
208
215
|
try {
|
|
209
216
|
const resp = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(domain)}`, {
|
|
210
217
|
method: "GET",
|
|
211
|
-
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION}` },
|
|
218
|
+
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION} (watch)` },
|
|
212
219
|
});
|
|
213
220
|
if (!resp.ok) continue;
|
|
214
221
|
const report = await resp.json();
|
|
@@ -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
|
-
|
|
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})`) : ""}`);
|
|
@@ -1198,6 +1220,13 @@ function printGitHubActionAnnotations(report) {
|
|
|
1198
1220
|
// GitHub Actions workflow command syntax: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
|
|
1199
1221
|
const findings = Array.isArray(report.findings) ? report.findings : [];
|
|
1200
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
|
+
}
|
|
1201
1230
|
// Top-line score/grade as a notice
|
|
1202
1231
|
console.log(`::notice title=Quantapact: ${report.domain}::Grade ${report.grade || "?"} · score ${report.score ?? "?"} / 10`);
|
|
1203
1232
|
for (const f of findings) {
|