mirror of
https://github.com/prompt-security/clawsec.git
synced 2026-06-13 13:38:03 +03:00
63de5ce08d
* auto-claude: subtask-1-1 - Create config loading utility with multi-path fallback Created load_suppression_config.mjs with: - Multi-path fallback: ~/.openclaw/security-audit.json -> .clawsec/allowlist.json - Environment variable support (OPENCLAW_AUDIT_CONFIG) - Custom path support via CLI argument - Schema validation (checkId, skill, reason, suppressedAt required) - Malformed JSON error handling - Graceful fallback to empty suppressions when no config exists - ISO 8601 date format validation with warnings Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * auto-claude: subtask-1-2 - Create example config file template - Added security-audit-config.example.json with two suppression examples - Included examples for clawsec-suite and openclaw-audit-watchdog - Created comprehensive README.md explaining configuration format - All required fields documented (checkId, skill, reason, suppressedAt) - ISO 8601 date format demonstrated - JSON validated successfully * auto-claude: subtask-1-3 - Add unit tests for config loading Added comprehensive unit tests for suppression config loading: - Valid config with all required fields - Malformed date warning (non-blocking) - Missing required field validation - Malformed JSON error handling - File not found graceful fallback - Custom path priority - Environment variable override - Missing/empty suppressions array handling All 10 tests passing. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * auto-claude: subtask-2-1 - Add suppression filtering to render_report.mjs Implements suppression filtering logic for security audit findings: - Import loadSuppressionConfig for config loading - Add --config CLI argument for custom config paths - Create extractSkillName() to extract skill names from findings (tries multiple fields) - Create filterFindings() to split findings into active/suppressed - Match suppressions by BOTH checkId AND skill name (exact match required) - Attach suppression metadata (reason, suppressedAt) to suppressed findings - Modify render() to accept suppressedFindings parameter - Apply filtering in main execution before rendering Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * auto-claude: subtask-2-2 - Add INFO-SUPPRESSED section to report output - Added lineForSuppressedFinding() to format suppressed findings - Added INFO-SUPPRESSED section showing suppressed findings with reason and date - Suppressed findings are not counted in summary (already filtered) - Follows existing code patterns for report sections Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * auto-claude: subtask-3-1 - Add --config flag to run_audit_and_format.sh - Added --config flag to accept path to config file - Added --help flag with usage documentation - Config flag is passed to openclaw audit commands when provided - Follows existing pattern for --label flag * auto-claude: subtask-4-1 - Create integration tests for render_report with suppressions Created comprehensive integration tests covering: - Suppressed findings appear in INFO-SUPPRESSED section - Active findings appear in CRITICAL/WARN section - Summary counts exclude suppressed findings - Backward compatibility (no config) - Partial matches don't suppress (checkId or skill alone) - Multiple suppressions work correctly - Skill name extraction from path field - Skill name extraction from title field - Empty suppressions array behaves like no config Bug fix in render_report.mjs: - Summary counts now recalculated after filtering suppressed findings - Previously summary showed original counts instead of filtered counts All 10 tests passing. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * auto-claude: subtask-4-2 - Manual E2E test with real openclaw audit - Fixed run_audit_and_format.sh to pass --config flag to render_report.mjs - Enhanced lineForFinding() to display skill names for better clarity - Enhanced lineForSuppressedFinding() to display skill names consistently - Created comprehensive E2E test documentation in E2E-TEST-RESULTS.md - All E2E verification points passed: * Config loading from custom paths * Suppression matching by checkId + skill name * INFO-SUPPRESSED section display * Suppression reason and date display * Summary count accuracy (excludes suppressed findings) * Non-suppressed findings preservation * Skill name display in all findings - All integration tests still passing (10/10) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * auto-claude: subtask-5-1 - Update README.md with suppression feature * auto-claude: subtask-5-2 - Update SKILL.md with usage examples * - Add backslash escaping before quote escaping in oneline() function - Prevents incomplete string escaping vulnerability - Resolves CodeQL alert: https://github.com/prompt-security/clawsec/security/code-scanning/16 * Fix regex in extractSkillName function and simplify error handling in suppression config tests * Enhance suppression mechanism in OpenClaw Audit Watchdog - Updated README.md to clarify suppression configuration and activation requirements. - Improved SKILL.md with examples for suppressing known findings. - Refactored load_suppression_config.mjs to implement opt-in gating for suppressions. - Modified render_report.mjs to support suppression flag in report generation. - Enhanced run_audit_and_format.sh and runner.sh scripts to accept --enable-suppressions flag. - Added test cases for suppression configuration, including validation for enabledFor sentinel and opt-in behavior. - Introduced new test files for empty and invalid suppression configurations. * Fix type assertion for checksums file entries in Checksums component * Update ESLint configuration and dependencies to pin @eslint/js to version 9.28.0 * Update CHANGELOG.md for advisory suppression module and OpenClaw Audit Watchdog enhancements * Refactor finding comparison logic in render_report.mjs to simplify equality checks * chore(clawsec-suite): bump version to 0.1.2 * chore(openclaw-audit-watchdog): bump version to 0.1.0 * Remove suppressed matches tracking from state to prevent re-evaluation alerts --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
207 lines
8.3 KiB
TypeScript
207 lines
8.3 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Shield, Copy, Download, CheckCircle2 } from 'lucide-react';
|
|
import { CodeBlock } from '../components/CodeBlock';
|
|
|
|
interface FileChecksum {
|
|
sha256: string;
|
|
size: number;
|
|
url: string;
|
|
}
|
|
|
|
interface ChecksumsData {
|
|
version: string;
|
|
generated_at: string;
|
|
repository: string;
|
|
files: Record<string, FileChecksum>;
|
|
}
|
|
|
|
export default function Checksums() {
|
|
const [checksums, setChecksums] = useState<ChecksumsData | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [copied, setCopied] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
fetch('./checksums.json')
|
|
.then(res => {
|
|
if (!res.ok) throw new Error('Not found');
|
|
return res.json();
|
|
})
|
|
.then(data => {
|
|
setChecksums(data);
|
|
setLoading(false);
|
|
})
|
|
.catch(() => {
|
|
setLoading(false);
|
|
});
|
|
}, []);
|
|
|
|
const copyToClipboard = (text: string, id: string) => {
|
|
navigator.clipboard.writeText(text);
|
|
setCopied(id);
|
|
setTimeout(() => setCopied(null), 2000);
|
|
};
|
|
|
|
const fileDescriptions: Record<string, string> = {
|
|
'SKILL.md': 'Main ClawSec skill documentation',
|
|
'heartbeat.md': 'Heartbeat monitoring and update instructions',
|
|
'reporting.md': 'Security incident reporting guidelines',
|
|
'skill.json': 'Skill metadata and configuration',
|
|
'feed.json': 'Community security advisory feed'
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="max-w-5xl mx-auto">
|
|
{/* Header */}
|
|
<div className="mb-12 text-center">
|
|
<div className="flex items-center justify-center gap-3 mb-4">
|
|
<Shield className="w-12 h-12 text-clawd-accent" />
|
|
<h1 className="text-4xl font-bold">File Checksums</h1>
|
|
</div>
|
|
<p className="text-xl text-gray-300">
|
|
Verify the integrity of ClawSec files before use
|
|
</p>
|
|
</div>
|
|
|
|
{loading ? (
|
|
<div className="text-center py-12">
|
|
<div className="inline-block animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-clawd-accent"></div>
|
|
<p className="mt-4 text-gray-400">Loading checksums...</p>
|
|
</div>
|
|
) : checksums ? (
|
|
<>
|
|
{/* Version Info */}
|
|
<div className="bg-clawd-800 rounded-lg p-6 mb-8">
|
|
<div className="grid md:grid-cols-3 gap-4 text-sm">
|
|
<div>
|
|
<div className="text-gray-400 mb-1">Version</div>
|
|
<div className="font-mono text-clawd-accent">{checksums.version}</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-gray-400 mb-1">Generated</div>
|
|
<div className="font-mono">{new Date(checksums.generated_at).toLocaleString()}</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-gray-400 mb-1">Repository</div>
|
|
<div className="font-mono text-sm">{checksums.repository}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Files Table */}
|
|
<div className="bg-clawd-800 rounded-lg overflow-hidden mb-8">
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full">
|
|
<thead className="bg-clawd-900">
|
|
<tr>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold">File</th>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold">Size</th>
|
|
<th className="px-6 py-4 text-left text-sm font-semibold">SHA256 Checksum</th>
|
|
<th className="px-6 py-4 text-right text-sm font-semibold">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-clawd-700">
|
|
{(Object.entries(checksums.files) as [string, FileChecksum][]).map(([filename, data]) => (
|
|
<tr key={filename} className="hover:bg-clawd-700/50 transition-colors">
|
|
<td className="px-6 py-4">
|
|
<div className="font-mono text-sm text-clawd-accent">{filename}</div>
|
|
<div className="text-xs text-gray-400 mt-1">
|
|
{fileDescriptions[filename] || 'ClawSec file'}
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<div className="text-sm">{(data.size / 1024).toFixed(1)} KB</div>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<div className="font-mono text-xs break-all max-w-md">
|
|
{data.sha256}
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4">
|
|
<div className="flex items-center justify-end gap-2">
|
|
<button
|
|
onClick={() => copyToClipboard(data.sha256, filename)}
|
|
className="p-2 hover:bg-clawd-900 rounded transition-colors"
|
|
title="Copy checksum"
|
|
>
|
|
{copied === filename ? (
|
|
<CheckCircle2 className="w-4 h-4 text-green-400" />
|
|
) : (
|
|
<Copy className="w-4 h-4" />
|
|
)}
|
|
</button>
|
|
<a
|
|
href={data.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="p-2 hover:bg-clawd-900 rounded transition-colors"
|
|
title="Download file"
|
|
>
|
|
<Download className="w-4 h-4" />
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Verification Instructions */}
|
|
<div className="bg-clawd-800 rounded-lg p-6">
|
|
<h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
|
|
<Shield className="w-6 h-6 text-clawd-accent" />
|
|
Verification Instructions
|
|
</h2>
|
|
|
|
<p className="text-gray-300 mb-4">
|
|
Always verify file integrity before using ClawSec files. Here's how:
|
|
</p>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h3 className="font-semibold mb-2">1. Download a file</h3>
|
|
<CodeBlock
|
|
code={`curl -sL https://github.com/${checksums.repository}/releases/download/${checksums.version}/SKILL.md -o SKILL.md`}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-semibold mb-2">2. Generate its checksum</h3>
|
|
<CodeBlock code="sha256sum SKILL.md" />
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-semibold mb-2">3. Compare with the checksum above</h3>
|
|
<p className="text-sm text-gray-400">
|
|
The output should exactly match the SHA256 value shown in the table.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="mt-6 p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
|
|
<p className="text-yellow-200 text-sm">
|
|
<strong>Security Warning:</strong> Never use files with mismatched checksums.
|
|
This could indicate tampering or a compromised download.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
) : (
|
|
<div className="bg-clawd-800 rounded-lg p-12 text-center">
|
|
<Shield className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
|
<p className="text-gray-400">
|
|
Checksums not available. Create a release to generate checksums.
|
|
</p>
|
|
<CodeBlock
|
|
code={`# Create a release to generate checksums:\ngit tag v1.0.0 && git push origin v1.0.0`}
|
|
className="mt-4 text-left"
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|