create-claude-cabinet 0.29.11 → 0.29.13
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/lib/site-audit-setup.js +11 -0
- package/package.json +1 -1
- package/templates/site-audit-runtime/package.json +1 -1
- package/templates/site-audit-runtime/src/checks/nuclei.mjs +4 -1
- package/templates/site-audit-runtime/src/checks/observatory.mjs +57 -14
- package/templates/site-audit-runtime/tests/fixtures/observatory.json +1 -10
- package/templates/skills/onboard/phases/generate-session-loop.md +1 -1
package/lib/site-audit-setup.js
CHANGED
|
@@ -120,6 +120,17 @@ function extractAndInstall(versionDir, tarballPath, results) {
|
|
|
120
120
|
results.push(' ⚠ browser-driver-manager install failed — axe-core will be unavailable');
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// MDN Observatory needs postinstall data files (HSTS preload list, TLD list)
|
|
124
|
+
const obsDir = path.join(pkgDir, 'node_modules', '@mdn', 'mdn-http-observatory');
|
|
125
|
+
if (fs.existsSync(obsDir)) {
|
|
126
|
+
try {
|
|
127
|
+
execSync('node src/retrieve-hsts.js && node src/retrieve-tld-list.js', { cwd: obsDir, encoding: 'utf8', timeout: 60_000 });
|
|
128
|
+
results.push(' Initialized MDN Observatory data files');
|
|
129
|
+
} catch {
|
|
130
|
+
results.push(' ⚠ MDN Observatory postinstall failed — observatory may be unavailable');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
123
134
|
const binDir = path.join(versionDir, 'bin');
|
|
124
135
|
fs.mkdirSync(binDir, { recursive: true });
|
|
125
136
|
const binSrc = path.join(pkgDir, 'bin', 'cc-site-audit');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-cabinet/site-audit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Comprehensive deployed-site quality audit engine for Claude Cabinet. Runs checks across performance, accessibility, security, SEO, content, DNS, and privacy against a deployed URL; single-site and comparison modes; standalone HTML report.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,10 @@ export const defaultTimeoutMs = 300_000;
|
|
|
12
12
|
export async function detect(executor) {
|
|
13
13
|
try {
|
|
14
14
|
const r = await executor.spawn('nuclei', ['-version'], { timeoutMs: 10_000 });
|
|
15
|
-
|
|
15
|
+
if (r.code !== 0) return false;
|
|
16
|
+
// Ensure templates are downloaded (nuclei 3.x ships without them)
|
|
17
|
+
await executor.spawn('nuclei', ['-update-templates'], { timeoutMs: 60_000 });
|
|
18
|
+
return true;
|
|
16
19
|
} catch {
|
|
17
20
|
return false;
|
|
18
21
|
}
|
|
@@ -14,31 +14,74 @@ export async function run(url, executor) {
|
|
|
14
14
|
|
|
15
15
|
const GRADE_SCORES = { 'A+': 100, 'A': 95, 'A-': 90, 'B+': 85, 'B': 80, 'B-': 75, 'C+': 70, 'C': 65, 'C-': 60, 'D+': 55, 'D': 50, 'D-': 45, 'F': 20 };
|
|
16
16
|
|
|
17
|
+
const TEST_LABELS = {
|
|
18
|
+
'content-security-policy': 'Content Security Policy',
|
|
19
|
+
'cookies': 'Cookies',
|
|
20
|
+
'cross-origin-resource-sharing': 'CORS',
|
|
21
|
+
'redirection': 'HTTP Redirection',
|
|
22
|
+
'referrer-policy': 'Referrer Policy',
|
|
23
|
+
'strict-transport-security': 'Strict Transport Security',
|
|
24
|
+
'subresource-integrity': 'Subresource Integrity',
|
|
25
|
+
'x-content-type-options': 'X-Content-Type-Options',
|
|
26
|
+
'x-frame-options': 'X-Frame-Options',
|
|
27
|
+
'cross-origin-resource-policy': 'Cross-Origin Resource Policy',
|
|
28
|
+
};
|
|
29
|
+
|
|
17
30
|
export function normalize(raw, durationMs) {
|
|
18
31
|
if (raw.code !== 0 && !raw.stdout) {
|
|
19
|
-
return { checkId, tool, status: 'error', score: null, grade: null, severity: null, findings: [], durationMs, reason: raw.stderr || 'observatory failed' };
|
|
32
|
+
return { checkId, tool, status: 'error', score: null, grade: null, severity: null, findings: [], durationMs, reason: raw.stderr?.slice(0, 200) || 'observatory failed' };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const text = (raw.stdout || '').trim();
|
|
36
|
+
|
|
37
|
+
// Try JSON parse first (mdn-http-observatory outputs JSON)
|
|
38
|
+
const jsonStart = text.indexOf('{');
|
|
39
|
+
if (jsonStart >= 0) {
|
|
40
|
+
try {
|
|
41
|
+
const data = JSON.parse(text.slice(jsonStart));
|
|
42
|
+
const scan = data.scan || {};
|
|
43
|
+
const tests = data.tests || {};
|
|
44
|
+
const grade = scan.grade || null;
|
|
45
|
+
const score = typeof scan.score === 'number' ? Math.min(100, scan.score) : (grade ? (GRADE_SCORES[grade] ?? null) : null);
|
|
46
|
+
|
|
47
|
+
const findings = [];
|
|
48
|
+
for (const [testId, test] of Object.entries(tests)) {
|
|
49
|
+
if (!test.pass) {
|
|
50
|
+
const label = TEST_LABELS[testId] || testId;
|
|
51
|
+
const severity = (test.scoreModifier != null && test.scoreModifier <= -20) ? 'serious' : 'moderate';
|
|
52
|
+
findings.push({
|
|
53
|
+
severity,
|
|
54
|
+
message: `${label}: ${test.result || 'failed'}`,
|
|
55
|
+
context: test.scoreModifier != null ? `Score impact: ${test.scoreModifier}` : undefined,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const isPass = grade && /^[A-B]/.test(grade);
|
|
61
|
+
const passed = Object.values(tests).filter(t => t.pass).length;
|
|
62
|
+
const total = Object.keys(tests).length;
|
|
63
|
+
const passSummary = isPass
|
|
64
|
+
? `Observatory grade ${grade} (score: ${score}/100, ${passed}/${total} tests passed)`
|
|
65
|
+
: undefined;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
checkId, tool, status: isPass ? 'pass' : 'fail',
|
|
69
|
+
score, grade, severity: findings.length ? findings[0].severity : null,
|
|
70
|
+
findings, durationMs,
|
|
71
|
+
...(passSummary && { passSummary }),
|
|
72
|
+
};
|
|
73
|
+
} catch { /* fall through to text parsing */ }
|
|
20
74
|
}
|
|
21
75
|
|
|
22
|
-
|
|
76
|
+
// Fallback: text parsing
|
|
23
77
|
const gradeMatch = text.match(/Grade:\s*([A-F][+-]?)/i);
|
|
24
78
|
const scoreMatch = text.match(/Score:\s*(\d+)/i);
|
|
25
79
|
const grade = gradeMatch ? gradeMatch[1] : null;
|
|
26
80
|
const score = scoreMatch ? Math.min(100, Number(scoreMatch[1])) : (grade ? (GRADE_SCORES[grade] ?? null) : null);
|
|
27
|
-
|
|
28
|
-
const findings = [];
|
|
29
|
-
const failLines = text.split('\n').filter(l => /fail|warn|not implemented/i.test(l));
|
|
30
|
-
for (const line of failLines.slice(0, 20)) {
|
|
31
|
-
findings.push({ severity: 'moderate', message: line.trim() });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
81
|
const isPass = grade && /^[A-B]/.test(grade);
|
|
35
|
-
const passSummary = isPass
|
|
36
|
-
? `Observatory grade ${grade}${score != null ? ` (score: ${score}/100)` : ''}`
|
|
37
|
-
: undefined;
|
|
38
82
|
|
|
39
83
|
return {
|
|
40
84
|
checkId, tool, status: isPass ? 'pass' : 'fail',
|
|
41
|
-
score, grade, severity:
|
|
42
|
-
...(passSummary && { passSummary }),
|
|
85
|
+
score, grade, severity: null, findings: [], durationMs,
|
|
43
86
|
};
|
|
44
87
|
}
|
|
@@ -1,10 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Score: 85
|
|
3
|
-
Tests passed: 8/11
|
|
4
|
-
content-security-policy: PASS
|
|
5
|
-
strict-transport-security: PASS
|
|
6
|
-
x-content-type-options: PASS
|
|
7
|
-
x-frame-options: FAIL - not implemented
|
|
8
|
-
referrer-policy: PASS
|
|
9
|
-
subresource-integrity: FAIL - not implemented
|
|
10
|
-
cookies: WARN - SameSite not set
|
|
1
|
+
{"scan":{"grade":"B+","score":85,"testsFailed":2,"testsPassed":8,"testsQuantity":10},"tests":{"content-security-policy":{"pass":true,"result":"csp-implemented","scoreModifier":0},"strict-transport-security":{"pass":true,"result":"hsts-implemented","scoreModifier":0},"x-content-type-options":{"pass":true,"result":"x-content-type-options-nosniff","scoreModifier":0},"x-frame-options":{"pass":false,"result":"x-frame-options-not-implemented","scoreModifier":-20},"referrer-policy":{"pass":true,"result":"referrer-policy-private","scoreModifier":0},"subresource-integrity":{"pass":false,"result":"sri-not-implemented","scoreModifier":-5},"cookies":{"pass":true,"result":"cookies-secure","scoreModifier":0}}}
|
|
@@ -19,7 +19,7 @@ this project needs something different from the default.
|
|
|
19
19
|
|
|
20
20
|
**`orient/phases/context.md`** — Point at the briefing files generated in
|
|
21
21
|
the previous phase. At minimum:
|
|
22
|
-
- Read
|
|
22
|
+
- Read `.claude/cabinet/_briefing.md` for project identity and configuration
|
|
23
23
|
- Read `system-status.md` for current state
|
|
24
24
|
- Read `CLAUDE.md` if it has project-specific instructions beyond what
|
|
25
25
|
Claude Code auto-loads
|