mirror of
https://github.com/prompt-security/clawsec.git
synced 2026-06-13 05:28:02 +03:00
26af277afd
* feat(hermes-attestation-guardian): release v0.0.2 hardening * docs(wiki): add v0.0.2 hardening update note * docs: add Hermes support coverage to README and compatibility report * fix(hermes-attestation-guardian): address baz review on crontab detection and doc dedup * feat(wiki): add PR-200 skill feature/platform matrix * docs(wiki): rewrite PR-200 matrix as narrative capability mapping * docs(readme): add skill feature matrix with requested headers * docs(readme): replace unknowns with mapped yes/no feature matrix * docs: move NanoClaw and CI/CD details from README to wiki modules * docs(readme): remove platform/suite sections and keep wiki module pointers * docs(readme): refresh project structure to match current repo * feat(hermes-attestation-guardian): add signed advisory feed verification pipeline * feat(hermes-attestation-guardian): add advisory-gated guarded skill verification * feat(hermes-attestation-guardian): add advisory scheduler helper and phase-3 parity docs * docs(wiki): expand hermes attestation guardian capability coverage * fix(pr-200): address Baz review findings across Hermes parity rollout * test(sandbox): extend Hermes regression to cover feed, guarded verify, and advisory scheduler * fix(pr-200): address Baz semver parsing and feed-state fallback visibility * fix(ci): suppress shellcheck false positives in sandbox inline docker script * fix(hermes-attestation-guardian): fail closed on unsupported advisory ranges * fix(hermes-attestation-guardian): restore safe install verdict in sandbox * fix(sandbox): capture guarded verify exit under set -e * fix(semver): fail closed on malformed affected specifiers * docs(readme): clarify hermes capability matrix wording * refactor(feed): share signed artifact verification flow * refactor(cron): share managed block helpers across setup scripts * fix(feed): require checksum manifest artifacts when enabled * chore(hermes-skill): relocate sandbox test, refresh docs, and add v0.1.0 release notes * chore(docs): remove remaining hermes parity plan file * chore(release): roll hermes-attestation-guardian to v0.1.0 * chore(release): remove standalone v0.1.0 release notes file * docs(hermes): update README status to v0.1.0 --------- Co-authored-by: David Abutbul <David.a@prompt.security>
102 lines
3.1 KiB
JavaScript
102 lines
3.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import fs from "node:fs";
|
|
import { defaultCachedFeedPath, defaultFeedStatePath, loadFeedVerificationState, resolveFeedConfig } from "../lib/feed.mjs";
|
|
|
|
function usage() {
|
|
process.stdout.write(
|
|
[
|
|
"Usage: node scripts/check_advisories.mjs",
|
|
"",
|
|
"Prints human-readable advisory feed verification status and cached feed summary.",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
}
|
|
|
|
function summarizeBySeverity(feed) {
|
|
const advisories = Array.isArray(feed?.advisories) ? feed.advisories : [];
|
|
const counts = {};
|
|
for (const advisory of advisories) {
|
|
const severity = String(advisory?.severity || "unknown").trim().toLowerCase() || "unknown";
|
|
counts[severity] = (counts[severity] || 0) + 1;
|
|
}
|
|
return counts;
|
|
}
|
|
|
|
function printSeveritySummary(counts) {
|
|
const entries = Object.entries(counts);
|
|
if (entries.length === 0) {
|
|
process.stdout.write("Advisory severities: (none)\n");
|
|
return;
|
|
}
|
|
const sorted = entries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
process.stdout.write(
|
|
`Advisory severities: ${sorted.map(([severity, count]) => `${severity}=${count}`).join(", ")}\n`,
|
|
);
|
|
}
|
|
|
|
function main() {
|
|
const argv = process.argv.slice(2);
|
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
const config = resolveFeedConfig({});
|
|
const statePath = config.statePath || defaultFeedStatePath();
|
|
const cachedFeedPath = config.cachedFeedPath || defaultCachedFeedPath();
|
|
|
|
const state = loadFeedVerificationState(statePath);
|
|
if (!state) {
|
|
process.stdout.write(`Feed verification state: unknown (missing state file: ${statePath})\n`);
|
|
process.exitCode = 2;
|
|
return;
|
|
}
|
|
|
|
process.stdout.write(`Feed verification state: ${state.status || "unknown"}\n`);
|
|
process.stdout.write(`Source: ${state.source || "unknown"}\n`);
|
|
process.stdout.write(`Last checked: ${state.checked_at || "unknown"}\n`);
|
|
process.stdout.write(`State file: ${statePath}\n`);
|
|
process.stdout.write(`Cached feed: ${cachedFeedPath}\n`);
|
|
|
|
if (state.error) {
|
|
process.stdout.write(`Last error: ${state.error}\n`);
|
|
}
|
|
|
|
if (state.allow_unsigned_bypass) {
|
|
process.stdout.write("WARNING: unsigned advisory feed bypass is active.\n");
|
|
}
|
|
|
|
if (!fs.existsSync(cachedFeedPath)) {
|
|
process.stdout.write("Cached advisory feed: unavailable\n");
|
|
process.exitCode = state.status === "verified" ? 1 : 0;
|
|
return;
|
|
}
|
|
|
|
let feed;
|
|
try {
|
|
feed = JSON.parse(fs.readFileSync(cachedFeedPath, "utf8"));
|
|
} catch (error) {
|
|
process.stdout.write(`Cached advisory feed JSON parse error: ${error?.message || String(error)}\n`);
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
process.stdout.write(`Feed version: ${feed?.version || "unknown"}\n`);
|
|
process.stdout.write(`Feed updated: ${feed?.updated || "unknown"}\n`);
|
|
process.stdout.write(`Advisory count: ${Array.isArray(feed?.advisories) ? feed.advisories.length : 0}\n`);
|
|
printSeveritySummary(summarizeBySeverity(feed));
|
|
|
|
if (state.status === "unverified") {
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
try {
|
|
main();
|
|
} catch (error) {
|
|
process.stderr.write(`CRITICAL: ${error?.message || String(error)}\n`);
|
|
process.exit(1);
|
|
}
|