kastell 2.2.4 → 2.2.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/.claude-plugin/marketplace.json +18 -18
- package/.claude-plugin/plugin.json +45 -38
- package/CHANGELOG.md +1313 -1266
- package/LICENSE +201 -201
- package/NOTICE +5 -5
- package/README.md +1 -1
- package/README.tr.md +1 -1
- package/bin/kastell +2 -2
- package/bin/kastell-mcp +5 -5
- package/dist/adapters/coolify.js +92 -92
- package/dist/adapters/dokploy.js +99 -99
- package/dist/commands/fix.d.ts +2 -0
- package/dist/commands/fix.d.ts.map +1 -1
- package/dist/commands/fix.js +26 -1
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/interactive/plugins.d.ts.map +1 -1
- package/dist/commands/interactive/plugins.js +26 -2
- package/dist/commands/interactive/plugins.js.map +1 -1
- package/dist/commands/plugin.d.ts.map +1 -1
- package/dist/commands/plugin.js +6 -2
- package/dist/commands/plugin.js.map +1 -1
- package/dist/core/audit/commands.d.ts +13 -2
- package/dist/core/audit/commands.d.ts.map +1 -1
- package/dist/core/audit/commands.js +39 -2
- package/dist/core/audit/commands.js.map +1 -1
- package/dist/core/audit/explainCheck.d.ts +1 -0
- package/dist/core/audit/explainCheck.d.ts.map +1 -1
- package/dist/core/audit/explainCheck.js +1 -1
- package/dist/core/audit/explainCheck.js.map +1 -1
- package/dist/core/audit/fix-history.d.ts +3 -1
- package/dist/core/audit/fix-history.d.ts.map +1 -1
- package/dist/core/audit/fix-history.js +6 -2
- package/dist/core/audit/fix-history.js.map +1 -1
- package/dist/core/audit/fix.d.ts.map +1 -1
- package/dist/core/audit/fix.js +22 -0
- package/dist/core/audit/fix.js.map +1 -1
- package/dist/core/audit/formatters/badge.js +20 -20
- package/dist/core/audit/index.d.ts.map +1 -1
- package/dist/core/audit/index.js +12 -3
- package/dist/core/audit/index.js.map +1 -1
- package/dist/core/audit/listChecks.d.ts.map +1 -1
- package/dist/core/audit/listChecks.js +24 -0
- package/dist/core/audit/listChecks.js.map +1 -1
- package/dist/core/audit/pluginAudit.d.ts +8 -0
- package/dist/core/audit/pluginAudit.d.ts.map +1 -0
- package/dist/core/audit/pluginAudit.js +134 -0
- package/dist/core/audit/pluginAudit.js.map +1 -0
- package/dist/core/audit/pluginFix.d.ts +19 -0
- package/dist/core/audit/pluginFix.d.ts.map +1 -0
- package/dist/core/audit/pluginFix.js +122 -0
- package/dist/core/audit/pluginFix.js.map +1 -0
- package/dist/core/audit/snapshot.d.ts +4 -4
- package/dist/core/audit/types.d.ts +2 -1
- package/dist/core/audit/types.d.ts.map +1 -1
- package/dist/core/completions.js +631 -631
- package/dist/core/plugin.d.ts +6 -0
- package/dist/core/plugin.d.ts.map +1 -1
- package/dist/core/plugin.js +2 -0
- package/dist/core/plugin.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/pluginTools.d.ts +5 -0
- package/dist/mcp/pluginTools.d.ts.map +1 -0
- package/dist/mcp/pluginTools.js +54 -0
- package/dist/mcp/pluginTools.js.map +1 -0
- package/dist/mcp/prompts/workflows.d.ts +17 -0
- package/dist/mcp/prompts/workflows.d.ts.map +1 -0
- package/dist/mcp/prompts/workflows.js +73 -0
- package/dist/mcp/prompts/workflows.js.map +1 -0
- package/dist/mcp/resources/checks.d.ts +4 -0
- package/dist/mcp/resources/checks.d.ts.map +1 -0
- package/dist/mcp/resources/checks.js +49 -0
- package/dist/mcp/resources/checks.js.map +1 -0
- package/dist/mcp/resources/servers.d.ts +4 -0
- package/dist/mcp/resources/servers.d.ts.map +1 -0
- package/dist/mcp/resources/servers.js +59 -0
- package/dist/mcp/resources/servers.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +68 -35
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/serverAudit.d.ts +1 -1
- package/dist/mcp/tools/serverExplain.d.ts.map +1 -1
- package/dist/mcp/tools/serverExplain.js.map +1 -1
- package/dist/mcp/tools/serverFix.d.ts.map +1 -1
- package/dist/mcp/tools/serverFix.js +7 -1
- package/dist/mcp/tools/serverFix.js.map +1 -1
- package/dist/mcp/tools/serverFleet.d.ts.map +1 -1
- package/dist/mcp/tools/serverFleet.js.map +1 -1
- package/dist/mcp/tools/serverInfo.d.ts +1 -1
- package/dist/mcp/tools/serverInfo.js +1 -1
- package/dist/mcp/tools/serverManage.d.ts +2 -1
- package/dist/mcp/tools/serverManage.d.ts.map +1 -1
- package/dist/mcp/tools/serverManage.js +50 -5
- package/dist/mcp/tools/serverManage.js.map +1 -1
- package/dist/mcp/tools/serverPlugin.d.ts +3 -0
- package/dist/mcp/tools/serverPlugin.d.ts.map +1 -1
- package/dist/mcp/tools/serverPlugin.js +11 -1
- package/dist/mcp/tools/serverPlugin.js.map +1 -1
- package/dist/mcp/tools/serverProvision.d.ts +5 -5
- package/dist/mcp/tools/serverProvision.d.ts.map +1 -1
- package/dist/mcp/tools/serverProvision.js +31 -9
- package/dist/mcp/tools/serverProvision.js.map +1 -1
- package/dist/mcp/tools/serverSecure.d.ts.map +1 -1
- package/dist/mcp/tools/serverSecure.js +30 -1
- package/dist/mcp/tools/serverSecure.js.map +1 -1
- package/dist/mcp/utils.d.ts +25 -0
- package/dist/mcp/utils.d.ts.map +1 -1
- package/dist/mcp/utils.js +36 -0
- package/dist/mcp/utils.js.map +1 -1
- package/dist/mcp-bundle.mjs +102301 -0
- package/dist/plugin/handlerResolver.d.ts +2 -0
- package/dist/plugin/handlerResolver.d.ts.map +1 -0
- package/dist/plugin/handlerResolver.js +16 -0
- package/dist/plugin/handlerResolver.js.map +1 -0
- package/dist/plugin/loader.d.ts.map +1 -1
- package/dist/plugin/loader.js +41 -4
- package/dist/plugin/loader.js.map +1 -1
- package/dist/plugin/registerCommands.d.ts +4 -0
- package/dist/plugin/registerCommands.d.ts.map +1 -0
- package/dist/plugin/registerCommands.js +45 -0
- package/dist/plugin/registerCommands.js.map +1 -0
- package/dist/plugin/registry.d.ts +20 -1
- package/dist/plugin/registry.d.ts.map +1 -1
- package/dist/plugin/registry.js +51 -1
- package/dist/plugin/registry.js.map +1 -1
- package/dist/plugin/sdk/constants.d.ts +2 -0
- package/dist/plugin/sdk/constants.d.ts.map +1 -1
- package/dist/plugin/sdk/constants.js +2 -0
- package/dist/plugin/sdk/constants.js.map +1 -1
- package/dist/plugin/sdk/types.d.ts +74 -1
- package/dist/plugin/sdk/types.d.ts.map +1 -1
- package/dist/plugin/validate.d.ts +2 -1
- package/dist/plugin/validate.d.ts.map +1 -1
- package/dist/plugin/validate.js +106 -1
- package/dist/plugin/validate.js.map +1 -1
- package/dist/utils/cloudInit.js +58 -58
- package/dist/utils/fileLock.d.ts +5 -1
- package/dist/utils/fileLock.d.ts.map +1 -1
- package/dist/utils/fileLock.js +70 -15
- package/dist/utils/fileLock.js.map +1 -1
- package/dist/utils/paths.d.ts +0 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +1 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/secureWrite.d.ts.map +1 -1
- package/dist/utils/secureWrite.js +3 -38
- package/dist/utils/secureWrite.js.map +1 -1
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +19 -4
- package/dist/utils/version.js.map +1 -1
- package/kastell-plugin/.claude-plugin/plugin.json +20 -20
- package/kastell-plugin/.mcp.json +15 -8
- package/kastell-plugin/README.md +113 -113
- package/kastell-plugin/agents/kastell-auditor.md +77 -77
- package/kastell-plugin/agents/scripts/bucket_mapper.sh +101 -101
- package/kastell-plugin/agents/scripts/trend_report.sh +91 -91
- package/kastell-plugin/hooks/destroy-block.cjs +31 -31
- package/kastell-plugin/hooks/hooks.json +57 -57
- package/kastell-plugin/hooks/pre-commit-audit-guard.cjs +75 -75
- package/kastell-plugin/hooks/session-audit.cjs +86 -86
- package/kastell-plugin/hooks/session-log.cjs +56 -56
- package/kastell-plugin/hooks/stop-quality-check.cjs +72 -72
- package/kastell-plugin/skills/kastell-careful/SKILL.md +64 -64
- package/kastell-plugin/skills/kastell-ops/SKILL.md +139 -139
- package/kastell-plugin/skills/kastell-ops/references/commands.md +45 -45
- package/kastell-plugin/skills/kastell-ops/references/mcp-tools.md +50 -50
- package/kastell-plugin/skills/kastell-ops/references/patterns.md +145 -145
- package/kastell-plugin/skills/kastell-ops/references/pitfalls.md +136 -136
- package/kastell-plugin/skills/kastell-ops/scripts/check_coverage.sh +101 -101
- package/kastell-plugin/skills/kastell-ops/scripts/fleet_report.sh +73 -73
- package/kastell-plugin/skills/kastell-ops/scripts/parse_audit.sh +76 -76
- package/kastell-plugin/skills/kastell-research/SKILL.md +90 -90
- package/kastell-plugin/skills/kastell-scaffold/SKILL.md +104 -104
- package/kastell-plugin/skills/kastell-scaffold/references/template-audit-check.md +150 -150
- package/kastell-plugin/skills/kastell-scaffold/references/template-command.md +80 -80
- package/kastell-plugin/skills/kastell-scaffold/references/template-mcp-tool.md +72 -72
- package/kastell-plugin/skills/kastell-scaffold/references/template-provider.md +67 -67
- package/kastell-plugin/skills/kastell-scaffold/scripts/scaffold.sh +180 -180
- package/kastell-plugin/skills/kastell-scaffold/templates/check-test.ts.tpl +27 -27
- package/kastell-plugin/skills/kastell-scaffold/templates/check.ts.tpl +50 -50
- package/kastell-plugin/skills/kastell-scaffold/templates/command-core.ts.tpl +18 -18
- package/kastell-plugin/skills/kastell-scaffold/templates/command-test.ts.tpl +17 -17
- package/kastell-plugin/skills/kastell-scaffold/templates/command.ts.tpl +25 -25
- package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool-test.ts.tpl +30 -30
- package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool.ts.tpl +29 -29
- package/kastell-plugin/skills/kastell-scaffold/templates/provider-test.ts.tpl +34 -34
- package/kastell-plugin/skills/kastell-scaffold/templates/provider.ts.tpl +32 -32
- package/package.json +125 -122
- package/dist/commands/interactive.d.ts +0 -11
- package/dist/commands/interactive.d.ts.map +0 -1
- package/dist/commands/interactive.js +0 -1079
- package/dist/commands/interactive.js.map +0 -1
- package/dist/core/lock.d.ts +0 -66
- package/dist/core/lock.d.ts.map +0 -1
- package/dist/core/lock.js +0 -556
- package/dist/core/lock.js.map +0 -1
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# check_coverage.sh — Compare audit check count vs test count.
|
|
3
|
-
# Usage: bash check_coverage.sh [project-root]
|
|
4
|
-
#
|
|
5
|
-
# Counts audit checks defined in src/core/audit/checks/ and matching tests.
|
|
6
|
-
# Requires: node
|
|
7
|
-
|
|
8
|
-
set -euo pipefail
|
|
9
|
-
|
|
10
|
-
PROJECT_ROOT="${1:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
|
11
|
-
|
|
12
|
-
node -e "
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
|
|
16
|
-
const root = process.argv[1];
|
|
17
|
-
const checksDir = path.join(root, 'src', 'core', 'audit', 'checks');
|
|
18
|
-
|
|
19
|
-
if (!fs.existsSync(checksDir)) {
|
|
20
|
-
console.error('Error: ' + checksDir + ' not found');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Read all .ts files in checks dir (excluding index.ts)
|
|
25
|
-
const checkFiles = fs.readdirSync(checksDir)
|
|
26
|
-
.filter(f => f.endsWith('.ts') && f !== 'index.ts');
|
|
27
|
-
|
|
28
|
-
let totalChecks = 0;
|
|
29
|
-
const uniqueIds = new Set();
|
|
30
|
-
const perFile = [];
|
|
31
|
-
|
|
32
|
-
for (const file of checkFiles) {
|
|
33
|
-
const content = fs.readFileSync(path.join(checksDir, file), 'utf8');
|
|
34
|
-
const ids = [...content.matchAll(/id:\s*['\"]([^'\"]+)['\"]/g)].map(m => m[1]);
|
|
35
|
-
totalChecks += ids.length;
|
|
36
|
-
ids.forEach(id => uniqueIds.add(id));
|
|
37
|
-
perFile.push({ name: file.replace('.ts', ''), checks: ids.length });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Count categories from index.ts
|
|
41
|
-
let categories = 0;
|
|
42
|
-
const indexPath = path.join(checksDir, 'index.ts');
|
|
43
|
-
if (fs.existsSync(indexPath)) {
|
|
44
|
-
const indexContent = fs.readFileSync(indexPath, 'utf8');
|
|
45
|
-
categories = (indexContent.match(/sectionName/g) || []).length;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Find audit test files recursively
|
|
49
|
-
function findTests(dir) {
|
|
50
|
-
let count = 0;
|
|
51
|
-
if (!fs.existsSync(dir)) return 0;
|
|
52
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
53
|
-
const full = path.join(dir, entry.name);
|
|
54
|
-
if (entry.isDirectory()) count += findTests(full);
|
|
55
|
-
else if (entry.name.endsWith('.test.ts') && entry.name.includes('audit') ||
|
|
56
|
-
full.includes('audit') && entry.name.endsWith('.test.ts')) {
|
|
57
|
-
count++;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return count;
|
|
61
|
-
}
|
|
62
|
-
const testCount = findTests(path.join(root, 'src'));
|
|
63
|
-
|
|
64
|
-
// Pre-index all test file contents once (avoids N+1 file reads)
|
|
65
|
-
const testIndex = new Set();
|
|
66
|
-
function indexTests(dir) {
|
|
67
|
-
try {
|
|
68
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
69
|
-
const full = path.join(dir, entry.name);
|
|
70
|
-
if (entry.isDirectory()) indexTests(full);
|
|
71
|
-
else if (entry.name.endsWith('.test.ts')) {
|
|
72
|
-
const content = fs.readFileSync(full, 'utf8');
|
|
73
|
-
// Extract referenced module names from imports and test descriptions
|
|
74
|
-
for (const match of content.matchAll(/['\"]([^'\"]*audit[^'\"]*)['\"]|from\s+['\"]([^'\"]+)['\"]/g)) {
|
|
75
|
-
const ref = match[1] || match[2] || '';
|
|
76
|
-
ref.split('/').forEach(part => testIndex.add(part.replace('.js', '').replace('.ts', '')));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
} catch {}
|
|
81
|
-
}
|
|
82
|
-
indexTests(path.join(root, 'src'));
|
|
83
|
-
function hasTestFor(name) { return testIndex.has(name); }
|
|
84
|
-
|
|
85
|
-
console.log('=== Audit Check Coverage ===');
|
|
86
|
-
console.log('Project: ' + root);
|
|
87
|
-
console.log('');
|
|
88
|
-
console.log('Check source files: ' + checkFiles.length);
|
|
89
|
-
console.log('Categories: ' + categories);
|
|
90
|
-
console.log('Checks defined: ' + totalChecks);
|
|
91
|
-
console.log('Unique check IDs: ' + uniqueIds.size);
|
|
92
|
-
console.log('Audit test files: ' + testCount);
|
|
93
|
-
console.log('');
|
|
94
|
-
console.log('Per-file breakdown:');
|
|
95
|
-
|
|
96
|
-
perFile.sort((a, b) => a.name.localeCompare(b.name));
|
|
97
|
-
for (const f of perFile) {
|
|
98
|
-
const tested = hasTestFor(f.name) ? '✓' : '✗';
|
|
99
|
-
console.log(' ' + tested + ' ' + f.name.padEnd(20) + ' ' + String(f.checks).padStart(3) + ' checks');
|
|
100
|
-
}
|
|
101
|
-
" "$PROJECT_ROOT"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check_coverage.sh — Compare audit check count vs test count.
|
|
3
|
+
# Usage: bash check_coverage.sh [project-root]
|
|
4
|
+
#
|
|
5
|
+
# Counts audit checks defined in src/core/audit/checks/ and matching tests.
|
|
6
|
+
# Requires: node
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
PROJECT_ROOT="${1:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
|
11
|
+
|
|
12
|
+
node -e "
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const root = process.argv[1];
|
|
17
|
+
const checksDir = path.join(root, 'src', 'core', 'audit', 'checks');
|
|
18
|
+
|
|
19
|
+
if (!fs.existsSync(checksDir)) {
|
|
20
|
+
console.error('Error: ' + checksDir + ' not found');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Read all .ts files in checks dir (excluding index.ts)
|
|
25
|
+
const checkFiles = fs.readdirSync(checksDir)
|
|
26
|
+
.filter(f => f.endsWith('.ts') && f !== 'index.ts');
|
|
27
|
+
|
|
28
|
+
let totalChecks = 0;
|
|
29
|
+
const uniqueIds = new Set();
|
|
30
|
+
const perFile = [];
|
|
31
|
+
|
|
32
|
+
for (const file of checkFiles) {
|
|
33
|
+
const content = fs.readFileSync(path.join(checksDir, file), 'utf8');
|
|
34
|
+
const ids = [...content.matchAll(/id:\s*['\"]([^'\"]+)['\"]/g)].map(m => m[1]);
|
|
35
|
+
totalChecks += ids.length;
|
|
36
|
+
ids.forEach(id => uniqueIds.add(id));
|
|
37
|
+
perFile.push({ name: file.replace('.ts', ''), checks: ids.length });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Count categories from index.ts
|
|
41
|
+
let categories = 0;
|
|
42
|
+
const indexPath = path.join(checksDir, 'index.ts');
|
|
43
|
+
if (fs.existsSync(indexPath)) {
|
|
44
|
+
const indexContent = fs.readFileSync(indexPath, 'utf8');
|
|
45
|
+
categories = (indexContent.match(/sectionName/g) || []).length;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Find audit test files recursively
|
|
49
|
+
function findTests(dir) {
|
|
50
|
+
let count = 0;
|
|
51
|
+
if (!fs.existsSync(dir)) return 0;
|
|
52
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
53
|
+
const full = path.join(dir, entry.name);
|
|
54
|
+
if (entry.isDirectory()) count += findTests(full);
|
|
55
|
+
else if (entry.name.endsWith('.test.ts') && entry.name.includes('audit') ||
|
|
56
|
+
full.includes('audit') && entry.name.endsWith('.test.ts')) {
|
|
57
|
+
count++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return count;
|
|
61
|
+
}
|
|
62
|
+
const testCount = findTests(path.join(root, 'src'));
|
|
63
|
+
|
|
64
|
+
// Pre-index all test file contents once (avoids N+1 file reads)
|
|
65
|
+
const testIndex = new Set();
|
|
66
|
+
function indexTests(dir) {
|
|
67
|
+
try {
|
|
68
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
69
|
+
const full = path.join(dir, entry.name);
|
|
70
|
+
if (entry.isDirectory()) indexTests(full);
|
|
71
|
+
else if (entry.name.endsWith('.test.ts')) {
|
|
72
|
+
const content = fs.readFileSync(full, 'utf8');
|
|
73
|
+
// Extract referenced module names from imports and test descriptions
|
|
74
|
+
for (const match of content.matchAll(/['\"]([^'\"]*audit[^'\"]*)['\"]|from\s+['\"]([^'\"]+)['\"]/g)) {
|
|
75
|
+
const ref = match[1] || match[2] || '';
|
|
76
|
+
ref.split('/').forEach(part => testIndex.add(part.replace('.js', '').replace('.ts', '')));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
indexTests(path.join(root, 'src'));
|
|
83
|
+
function hasTestFor(name) { return testIndex.has(name); }
|
|
84
|
+
|
|
85
|
+
console.log('=== Audit Check Coverage ===');
|
|
86
|
+
console.log('Project: ' + root);
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log('Check source files: ' + checkFiles.length);
|
|
89
|
+
console.log('Categories: ' + categories);
|
|
90
|
+
console.log('Checks defined: ' + totalChecks);
|
|
91
|
+
console.log('Unique check IDs: ' + uniqueIds.size);
|
|
92
|
+
console.log('Audit test files: ' + testCount);
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log('Per-file breakdown:');
|
|
95
|
+
|
|
96
|
+
perFile.sort((a, b) => a.name.localeCompare(b.name));
|
|
97
|
+
for (const f of perFile) {
|
|
98
|
+
const tested = hasTestFor(f.name) ? '✓' : '✗';
|
|
99
|
+
console.log(' ' + tested + ' ' + f.name.padEnd(20) + ' ' + String(f.checks).padStart(3) + ' checks');
|
|
100
|
+
}
|
|
101
|
+
" "$PROJECT_ROOT"
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# fleet_report.sh — Generate a fleet-wide server score table.
|
|
3
|
-
# Usage: kastell fleet --json | bash fleet_report.sh
|
|
4
|
-
# OR: bash fleet_report.sh < fleet-output.json
|
|
5
|
-
# OR: bash fleet_report.sh fleet-output.json
|
|
6
|
-
#
|
|
7
|
-
# Requires: node
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
if [[ -n "${1:-}" && -f "$1" ]]; then
|
|
12
|
-
INPUT=$(cat "$1")
|
|
13
|
-
elif [[ ! -t 0 ]]; then
|
|
14
|
-
INPUT=$(cat)
|
|
15
|
-
else
|
|
16
|
-
echo "Usage: kastell fleet --json | bash fleet_report.sh" >&2
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
node -e "
|
|
21
|
-
const data = JSON.parse(process.argv[1]);
|
|
22
|
-
const servers = data.servers || data.fleet || data || [];
|
|
23
|
-
|
|
24
|
-
if (!Array.isArray(servers) || servers.length === 0) {
|
|
25
|
-
console.log('No servers found in fleet data.');
|
|
26
|
-
process.exit(0);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Header
|
|
30
|
-
const cols = { name: 20, ip: 16, provider: 12, mode: 8, score: 6, health: 10 };
|
|
31
|
-
const pad = (s, n) => String(s || '-').slice(0, n).padEnd(n);
|
|
32
|
-
const sep = '-'.repeat(Object.values(cols).reduce((a, b) => a + b + 3, 0));
|
|
33
|
-
|
|
34
|
-
console.log('=== Kastell Fleet Report ===');
|
|
35
|
-
console.log('Servers: ' + servers.length);
|
|
36
|
-
console.log('');
|
|
37
|
-
console.log(
|
|
38
|
-
pad('Name', cols.name) + ' | ' +
|
|
39
|
-
pad('IP', cols.ip) + ' | ' +
|
|
40
|
-
pad('Provider', cols.provider) + ' | ' +
|
|
41
|
-
pad('Mode', cols.mode) + ' | ' +
|
|
42
|
-
pad('Score', cols.score) + ' | ' +
|
|
43
|
-
pad('Health', cols.health)
|
|
44
|
-
);
|
|
45
|
-
console.log(sep);
|
|
46
|
-
|
|
47
|
-
// Sort by score (lowest first = needs attention)
|
|
48
|
-
const sorted = [...servers].sort((a, b) => (a.score ?? 0) - (b.score ?? 0));
|
|
49
|
-
|
|
50
|
-
for (const s of sorted) {
|
|
51
|
-
const score = s.score ?? s.auditScore ?? '-';
|
|
52
|
-
const health = s.health ?? s.status ?? '-';
|
|
53
|
-
const icon = health === 'ONLINE' ? '●' : health === 'DEGRADED' ? '◐' : '○';
|
|
54
|
-
console.log(
|
|
55
|
-
pad(s.name, cols.name) + ' | ' +
|
|
56
|
-
pad(s.ip, cols.ip) + ' | ' +
|
|
57
|
-
pad(s.provider, cols.provider) + ' | ' +
|
|
58
|
-
pad(s.mode, cols.mode) + ' | ' +
|
|
59
|
-
pad(score, cols.score) + ' | ' +
|
|
60
|
-
icon + ' ' + pad(health, cols.health - 2)
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Summary
|
|
65
|
-
const scores = sorted.map(s => s.score ?? s.auditScore).filter(s => typeof s === 'number');
|
|
66
|
-
if (scores.length > 0) {
|
|
67
|
-
const avg = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);
|
|
68
|
-
const min = Math.min(...scores);
|
|
69
|
-
const max = Math.max(...scores);
|
|
70
|
-
console.log('');
|
|
71
|
-
console.log('Avg: ' + avg + ' | Min: ' + min + ' | Max: ' + max);
|
|
72
|
-
}
|
|
73
|
-
" "$INPUT"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fleet_report.sh — Generate a fleet-wide server score table.
|
|
3
|
+
# Usage: kastell fleet --json | bash fleet_report.sh
|
|
4
|
+
# OR: bash fleet_report.sh < fleet-output.json
|
|
5
|
+
# OR: bash fleet_report.sh fleet-output.json
|
|
6
|
+
#
|
|
7
|
+
# Requires: node
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
if [[ -n "${1:-}" && -f "$1" ]]; then
|
|
12
|
+
INPUT=$(cat "$1")
|
|
13
|
+
elif [[ ! -t 0 ]]; then
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
else
|
|
16
|
+
echo "Usage: kastell fleet --json | bash fleet_report.sh" >&2
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
node -e "
|
|
21
|
+
const data = JSON.parse(process.argv[1]);
|
|
22
|
+
const servers = data.servers || data.fleet || data || [];
|
|
23
|
+
|
|
24
|
+
if (!Array.isArray(servers) || servers.length === 0) {
|
|
25
|
+
console.log('No servers found in fleet data.');
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Header
|
|
30
|
+
const cols = { name: 20, ip: 16, provider: 12, mode: 8, score: 6, health: 10 };
|
|
31
|
+
const pad = (s, n) => String(s || '-').slice(0, n).padEnd(n);
|
|
32
|
+
const sep = '-'.repeat(Object.values(cols).reduce((a, b) => a + b + 3, 0));
|
|
33
|
+
|
|
34
|
+
console.log('=== Kastell Fleet Report ===');
|
|
35
|
+
console.log('Servers: ' + servers.length);
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log(
|
|
38
|
+
pad('Name', cols.name) + ' | ' +
|
|
39
|
+
pad('IP', cols.ip) + ' | ' +
|
|
40
|
+
pad('Provider', cols.provider) + ' | ' +
|
|
41
|
+
pad('Mode', cols.mode) + ' | ' +
|
|
42
|
+
pad('Score', cols.score) + ' | ' +
|
|
43
|
+
pad('Health', cols.health)
|
|
44
|
+
);
|
|
45
|
+
console.log(sep);
|
|
46
|
+
|
|
47
|
+
// Sort by score (lowest first = needs attention)
|
|
48
|
+
const sorted = [...servers].sort((a, b) => (a.score ?? 0) - (b.score ?? 0));
|
|
49
|
+
|
|
50
|
+
for (const s of sorted) {
|
|
51
|
+
const score = s.score ?? s.auditScore ?? '-';
|
|
52
|
+
const health = s.health ?? s.status ?? '-';
|
|
53
|
+
const icon = health === 'ONLINE' ? '●' : health === 'DEGRADED' ? '◐' : '○';
|
|
54
|
+
console.log(
|
|
55
|
+
pad(s.name, cols.name) + ' | ' +
|
|
56
|
+
pad(s.ip, cols.ip) + ' | ' +
|
|
57
|
+
pad(s.provider, cols.provider) + ' | ' +
|
|
58
|
+
pad(s.mode, cols.mode) + ' | ' +
|
|
59
|
+
pad(score, cols.score) + ' | ' +
|
|
60
|
+
icon + ' ' + pad(health, cols.health - 2)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Summary
|
|
65
|
+
const scores = sorted.map(s => s.score ?? s.auditScore).filter(s => typeof s === 'number');
|
|
66
|
+
if (scores.length > 0) {
|
|
67
|
+
const avg = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);
|
|
68
|
+
const min = Math.min(...scores);
|
|
69
|
+
const max = Math.max(...scores);
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log('Avg: ' + avg + ' | Min: ' + min + ' | Max: ' + max);
|
|
72
|
+
}
|
|
73
|
+
" "$INPUT"
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# parse_audit.sh — Parse kastell audit JSON into 5 security domain summaries.
|
|
3
|
-
# Usage: kastell audit --server <name> --json | bash parse_audit.sh
|
|
4
|
-
# OR: bash parse_audit.sh < audit-output.json
|
|
5
|
-
# OR: bash parse_audit.sh audit-output.json
|
|
6
|
-
#
|
|
7
|
-
# Requires: node (uses inline JS for JSON parsing — no jq dependency)
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
# Read JSON from file arg, stdin, or pipe
|
|
12
|
-
if [[ -n "${1:-}" && -f "$1" ]]; then
|
|
13
|
-
INPUT=$(cat "$1")
|
|
14
|
-
elif [[ ! -t 0 ]]; then
|
|
15
|
-
INPUT=$(cat)
|
|
16
|
-
else
|
|
17
|
-
echo "Usage: kastell audit --server <name> --json | bash parse_audit.sh" >&2
|
|
18
|
-
echo " OR: bash parse_audit.sh <audit-json-file>" >&2
|
|
19
|
-
exit 1
|
|
20
|
-
fi
|
|
21
|
-
|
|
22
|
-
node -e "
|
|
23
|
-
const data = JSON.parse(process.argv[1]);
|
|
24
|
-
const checks = data.checks || data.results || [];
|
|
25
|
-
|
|
26
|
-
// 5 security domain mapping
|
|
27
|
-
const DOMAINS = {
|
|
28
|
-
'Perimeter': ['Network', 'Firewall', 'DNS Security'],
|
|
29
|
-
'Authentication': ['SSH', 'Auth', 'Crypto', 'Accounts'],
|
|
30
|
-
'Runtime': ['Docker', 'Services', 'Boot', 'Scheduling'],
|
|
31
|
-
'Internals': ['Filesystem', 'Logging', 'Kernel', 'Memory'],
|
|
32
|
-
'Compliance': ['Updates', 'File Integrity', 'Malware', 'MAC', 'Secrets',
|
|
33
|
-
'Cloud Metadata', 'Supply Chain', 'Backup Hygiene',
|
|
34
|
-
'Resource Limits', 'Incident Readiness', 'Banners', 'Time',
|
|
35
|
-
'TLS', 'HTTP Security Headers'],
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// Map categories to domains
|
|
39
|
-
const catToDomain = {};
|
|
40
|
-
for (const [domain, cats] of Object.entries(DOMAINS)) {
|
|
41
|
-
for (const cat of cats) catToDomain[cat.toLowerCase()] = domain;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Bucket checks
|
|
45
|
-
const buckets = {};
|
|
46
|
-
for (const d of Object.keys(DOMAINS)) buckets[d] = { passed: 0, failed: 0, critical: [] };
|
|
47
|
-
|
|
48
|
-
for (const c of checks) {
|
|
49
|
-
const cat = (c.category || '').toLowerCase();
|
|
50
|
-
let domain = 'Compliance'; // default
|
|
51
|
-
for (const [key, val] of Object.entries(catToDomain)) {
|
|
52
|
-
if (cat.includes(key)) { domain = val; break; }
|
|
53
|
-
}
|
|
54
|
-
if (c.passed) buckets[domain].passed++;
|
|
55
|
-
else {
|
|
56
|
-
buckets[domain].failed++;
|
|
57
|
-
if (c.severity === 'critical') buckets[domain].critical.push(c.id || c.name);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Output
|
|
62
|
-
const score = data.score ?? data.overallScore ?? 'N/A';
|
|
63
|
-
console.log('=== Kastell Audit Domain Summary ===');
|
|
64
|
-
console.log('Overall Score: ' + score + '/100');
|
|
65
|
-
console.log('');
|
|
66
|
-
|
|
67
|
-
for (const [domain, b] of Object.entries(buckets)) {
|
|
68
|
-
const total = b.passed + b.failed;
|
|
69
|
-
const pct = total > 0 ? Math.round(b.passed / total * 100) : 0;
|
|
70
|
-
const bar = '█'.repeat(Math.round(pct / 5)) + '░'.repeat(20 - Math.round(pct / 5));
|
|
71
|
-
console.log(domain + ': ' + b.passed + '/' + total + ' (' + pct + '%) ' + bar);
|
|
72
|
-
if (b.critical.length > 0) {
|
|
73
|
-
console.log(' Critical: ' + b.critical.slice(0, 3).join(', '));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
" "$INPUT"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# parse_audit.sh — Parse kastell audit JSON into 5 security domain summaries.
|
|
3
|
+
# Usage: kastell audit --server <name> --json | bash parse_audit.sh
|
|
4
|
+
# OR: bash parse_audit.sh < audit-output.json
|
|
5
|
+
# OR: bash parse_audit.sh audit-output.json
|
|
6
|
+
#
|
|
7
|
+
# Requires: node (uses inline JS for JSON parsing — no jq dependency)
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
# Read JSON from file arg, stdin, or pipe
|
|
12
|
+
if [[ -n "${1:-}" && -f "$1" ]]; then
|
|
13
|
+
INPUT=$(cat "$1")
|
|
14
|
+
elif [[ ! -t 0 ]]; then
|
|
15
|
+
INPUT=$(cat)
|
|
16
|
+
else
|
|
17
|
+
echo "Usage: kastell audit --server <name> --json | bash parse_audit.sh" >&2
|
|
18
|
+
echo " OR: bash parse_audit.sh <audit-json-file>" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
node -e "
|
|
23
|
+
const data = JSON.parse(process.argv[1]);
|
|
24
|
+
const checks = data.checks || data.results || [];
|
|
25
|
+
|
|
26
|
+
// 5 security domain mapping
|
|
27
|
+
const DOMAINS = {
|
|
28
|
+
'Perimeter': ['Network', 'Firewall', 'DNS Security'],
|
|
29
|
+
'Authentication': ['SSH', 'Auth', 'Crypto', 'Accounts'],
|
|
30
|
+
'Runtime': ['Docker', 'Services', 'Boot', 'Scheduling'],
|
|
31
|
+
'Internals': ['Filesystem', 'Logging', 'Kernel', 'Memory'],
|
|
32
|
+
'Compliance': ['Updates', 'File Integrity', 'Malware', 'MAC', 'Secrets',
|
|
33
|
+
'Cloud Metadata', 'Supply Chain', 'Backup Hygiene',
|
|
34
|
+
'Resource Limits', 'Incident Readiness', 'Banners', 'Time',
|
|
35
|
+
'TLS', 'HTTP Security Headers'],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Map categories to domains
|
|
39
|
+
const catToDomain = {};
|
|
40
|
+
for (const [domain, cats] of Object.entries(DOMAINS)) {
|
|
41
|
+
for (const cat of cats) catToDomain[cat.toLowerCase()] = domain;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Bucket checks
|
|
45
|
+
const buckets = {};
|
|
46
|
+
for (const d of Object.keys(DOMAINS)) buckets[d] = { passed: 0, failed: 0, critical: [] };
|
|
47
|
+
|
|
48
|
+
for (const c of checks) {
|
|
49
|
+
const cat = (c.category || '').toLowerCase();
|
|
50
|
+
let domain = 'Compliance'; // default
|
|
51
|
+
for (const [key, val] of Object.entries(catToDomain)) {
|
|
52
|
+
if (cat.includes(key)) { domain = val; break; }
|
|
53
|
+
}
|
|
54
|
+
if (c.passed) buckets[domain].passed++;
|
|
55
|
+
else {
|
|
56
|
+
buckets[domain].failed++;
|
|
57
|
+
if (c.severity === 'critical') buckets[domain].critical.push(c.id || c.name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Output
|
|
62
|
+
const score = data.score ?? data.overallScore ?? 'N/A';
|
|
63
|
+
console.log('=== Kastell Audit Domain Summary ===');
|
|
64
|
+
console.log('Overall Score: ' + score + '/100');
|
|
65
|
+
console.log('');
|
|
66
|
+
|
|
67
|
+
for (const [domain, b] of Object.entries(buckets)) {
|
|
68
|
+
const total = b.passed + b.failed;
|
|
69
|
+
const pct = total > 0 ? Math.round(b.passed / total * 100) : 0;
|
|
70
|
+
const bar = '█'.repeat(Math.round(pct / 5)) + '░'.repeat(20 - Math.round(pct / 5));
|
|
71
|
+
console.log(domain + ': ' + b.passed + '/' + total + ' (' + pct + '%) ' + bar);
|
|
72
|
+
if (b.critical.length > 0) {
|
|
73
|
+
console.log(' Critical: ' + b.critical.slice(0, 3).join(', '));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
" "$INPUT"
|