@claude-flow/cli 3.25.5 → 3.25.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/helpers/helpers.manifest.json +2 -2
- package/dist/src/commands/doctor.js +74 -29
- package/dist/src/init/executor.js +16 -16
- package/dist/src/init/mcp-generator.js +11 -6
- package/package.json +5 -2
- package/plugins/ruflo-metaharness/.claude-flow/daemon-state.json +178 -0
- package/plugins/ruflo-metaharness/.claude-flow/daemon.pid +1 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/daemon.log +43 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783604774864_ozbujc_prompt.log +19 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783604774864_ozbujc_result.log +108 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783605513587_ulvmpb_prompt.log +19 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783605513587_ulvmpb_result.log +209 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783606368867_ahysui_prompt.log +19 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783606368867_ahysui_result.log +192 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783604894861_v6n3ut_prompt.log +14 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783604894861_v6n3ut_result.log +66 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783605934532_9h8ikb_prompt.log +14 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/optimize_1783605934532_9h8ikb_result.log +68 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783605134860_9jssz9_prompt.log +14 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783605134860_9jssz9_result.log +69 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/headless/testgaps_1783606516743_zftbaa_prompt.log +14 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/backup.json +7 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/codebase-map.json +11 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/consolidation.json +16 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/harness-loop.json +83 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/performance.json +67 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/security-audit.json +191 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/test-gaps.json +68 -0
- package/plugins/ruflo-metaharness/.claude-flow/neural/stats.json +3 -3
- package/plugins/ruflo-metaharness/scripts/smoke.sh +18 -5
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.25.
|
|
3
|
+
"version": "3.25.6",
|
|
4
4
|
"files": {
|
|
5
5
|
"auto-memory-hook.mjs": "68be7e9a9eba7bf9c4e8a230db7bf61a243b965639f8504842799d6c6ca28762",
|
|
6
6
|
"hook-handler.cjs": "bc4d2d26823d49b3ab1dded2946b66369f1f67bd76b4c30dca9f3b6df3cca8f0",
|
|
7
7
|
"intelligence.cjs": "bd1f8e4b034944aee1df0391dc47ac2e8cc4b3aa542c65407a59d49620bbf76b"
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
|
-
"signature": "
|
|
10
|
+
"signature": "3D6EGXwOvFuWMNrgRU9zcNzvyxyzmDMdsCaOOs74M9sTouS2soV0yrNhyybgbqFf8bhy08KDLW0O7tohDYLbBg==",
|
|
11
11
|
"algorithm": "ed25519"
|
|
12
12
|
}
|
|
@@ -442,53 +442,98 @@ async function checkMcpServers() {
|
|
|
442
442
|
'.mcp.json',
|
|
443
443
|
];
|
|
444
444
|
const isRufloKey = (k) => k === 'ruflo' || k === 'ruflo_alpha' || k === 'claude-flow' || k === 'claude-flow_alpha';
|
|
445
|
+
// Canonical MCP-server key is `claude-flow` — matches the `mcp__claude-flow__*`
|
|
446
|
+
// prefix that ~166 plugin tool references depend on (#2206). A `ruflo`-keyed
|
|
447
|
+
// entry pointing at the same binary is the legacy duplicate created by
|
|
448
|
+
// pre-rename setup docs that #2612 tracks; doctor treats it as removable.
|
|
449
|
+
const isCurrentRufloKey = (k) => k === 'claude-flow' || k === 'claude-flow_alpha';
|
|
450
|
+
const isLegacyRufloKey = (k) => k === 'ruflo' || k === 'ruflo_alpha';
|
|
451
|
+
const isRufloServer = (server) => {
|
|
452
|
+
if (!server || typeof server !== 'object')
|
|
453
|
+
return false;
|
|
454
|
+
const entry = server;
|
|
455
|
+
const command = typeof entry.command === 'string' ? entry.command : '';
|
|
456
|
+
const args = Array.isArray(entry.args)
|
|
457
|
+
? entry.args.filter((arg) => typeof arg === 'string')
|
|
458
|
+
: [];
|
|
459
|
+
const haystack = [command, ...args].join(' ');
|
|
460
|
+
return /\b(?:ruflo|claude-flow|@claude-flow\/cli)(?:@[\w.-]+)?\b/.test(haystack);
|
|
461
|
+
};
|
|
462
|
+
let totalServersSeen = 0;
|
|
463
|
+
const rufloLocations = [];
|
|
464
|
+
const duplicateLocations = [];
|
|
465
|
+
const legacyLocations = [];
|
|
466
|
+
const currentLocations = [];
|
|
467
|
+
const inspectServers = (servers, location) => {
|
|
468
|
+
if (!servers || typeof servers !== 'object')
|
|
469
|
+
return { total: 0, hasRuflo: false };
|
|
470
|
+
const entries = Object.entries(servers);
|
|
471
|
+
const activeRufloEntries = entries.filter(([key, server]) => isRufloKey(key) && isRufloServer(server));
|
|
472
|
+
const hasLegacy = activeRufloEntries.some(([key]) => isLegacyRufloKey(key));
|
|
473
|
+
const hasCurrent = activeRufloEntries.some(([key]) => isCurrentRufloKey(key));
|
|
474
|
+
if (activeRufloEntries.length > 0) {
|
|
475
|
+
rufloLocations.push(`${location}: ${activeRufloEntries.map(([key]) => key).join(', ')}`);
|
|
476
|
+
}
|
|
477
|
+
if (hasLegacy && hasCurrent) {
|
|
478
|
+
duplicateLocations.push(`${location}: ${activeRufloEntries.map(([key]) => key).join(' + ')}`);
|
|
479
|
+
}
|
|
480
|
+
for (const [key] of activeRufloEntries) {
|
|
481
|
+
if (isLegacyRufloKey(key))
|
|
482
|
+
legacyLocations.push(`${location}: ${key}`);
|
|
483
|
+
if (isCurrentRufloKey(key))
|
|
484
|
+
currentLocations.push(`${location}: ${key}`);
|
|
485
|
+
}
|
|
486
|
+
return { total: entries.length, hasRuflo: activeRufloEntries.length > 0 };
|
|
487
|
+
};
|
|
445
488
|
for (const configPath of mcpConfigPaths) {
|
|
446
489
|
if (!existsSync(configPath))
|
|
447
490
|
continue;
|
|
448
491
|
try {
|
|
449
492
|
const content = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
450
493
|
// Top-level mcpServers (legacy / desktop form)
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
const topHasRuflo = topServerKeys.some(isRufloKey);
|
|
494
|
+
const topResult = inspectServers(content.mcpServers || content.servers || {}, `${configPath} top-level`);
|
|
495
|
+
totalServersSeen += topResult.total;
|
|
454
496
|
// Project-scoped (Claude Code shape): projects[*].mcpServers.ruflo
|
|
455
|
-
let projectHits = 0;
|
|
456
|
-
let projectScannedServers = 0;
|
|
457
497
|
if (content.projects && typeof content.projects === 'object') {
|
|
458
|
-
for (const projectVal of Object.
|
|
498
|
+
for (const [projectKey, projectVal] of Object.entries(content.projects)) {
|
|
459
499
|
const pm = projectVal?.mcpServers;
|
|
460
500
|
if (pm && typeof pm === 'object') {
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
if (keys.some(isRufloKey))
|
|
464
|
-
projectHits += 1;
|
|
501
|
+
const projectResult = inspectServers(pm, `${configPath} projects[${projectKey}]`);
|
|
502
|
+
totalServersSeen += projectResult.total;
|
|
465
503
|
}
|
|
466
504
|
}
|
|
467
505
|
}
|
|
468
|
-
const totalServers = topServerKeys.length + projectScannedServers;
|
|
469
|
-
if (topHasRuflo || projectHits > 0) {
|
|
470
|
-
const where = topHasRuflo
|
|
471
|
-
? 'top-level'
|
|
472
|
-
: `${projectHits} project-scoped`;
|
|
473
|
-
return {
|
|
474
|
-
name: 'MCP Servers',
|
|
475
|
-
status: 'pass',
|
|
476
|
-
message: `${totalServers} servers (ruflo configured: ${where})`,
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
if (totalServers > 0) {
|
|
480
|
-
return {
|
|
481
|
-
name: 'MCP Servers',
|
|
482
|
-
status: 'warn',
|
|
483
|
-
message: `${totalServers} servers (ruflo not found)`,
|
|
484
|
-
fix: 'claude mcp add ruflo -- npx -y ruflo@latest mcp start',
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
506
|
}
|
|
488
507
|
catch {
|
|
489
508
|
// continue to next path
|
|
490
509
|
}
|
|
491
510
|
}
|
|
511
|
+
if (duplicateLocations.length > 0 || (legacyLocations.length > 0 && currentLocations.length > 0)) {
|
|
512
|
+
const locations = duplicateLocations.length > 0
|
|
513
|
+
? duplicateLocations.join('; ')
|
|
514
|
+
: `legacy ${legacyLocations.join('; ')} + current ${currentLocations.join('; ')}`;
|
|
515
|
+
return {
|
|
516
|
+
name: 'MCP Servers',
|
|
517
|
+
status: 'warn',
|
|
518
|
+
message: `Duplicate Ruflo MCP registrations found (${locations}) — Claude Code will start both tool schemas`,
|
|
519
|
+
fix: 'Remove the legacy `ruflo`-keyed MCP registration (pre-rename duplicate) and keep the canonical `claude-flow` entry: `claude mcp add claude-flow -- npx -y ruflo@latest mcp start`. The canonical key stays `claude-flow` so the ~166 `mcp__claude-flow__*` plugin tool references keep resolving (#2206).',
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (rufloLocations.length > 0) {
|
|
523
|
+
return {
|
|
524
|
+
name: 'MCP Servers',
|
|
525
|
+
status: 'pass',
|
|
526
|
+
message: `${totalServersSeen} servers (ruflo configured: ${rufloLocations.join('; ')})`,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
if (totalServersSeen > 0) {
|
|
530
|
+
return {
|
|
531
|
+
name: 'MCP Servers',
|
|
532
|
+
status: 'warn',
|
|
533
|
+
message: `${totalServersSeen} servers (ruflo not found)`,
|
|
534
|
+
fix: 'claude mcp add claude-flow -- npx -y ruflo@latest mcp start',
|
|
535
|
+
};
|
|
536
|
+
}
|
|
492
537
|
return {
|
|
493
538
|
name: 'MCP Servers',
|
|
494
539
|
status: 'warn',
|
|
@@ -807,7 +807,7 @@ async function writeSettings(targetDir, options, result) {
|
|
|
807
807
|
* "same MCP server twice under two different prefixes" duplication the
|
|
808
808
|
* issue describes.
|
|
809
809
|
*
|
|
810
|
-
* Returns the path of the file that already declares `ruflo` (so we can
|
|
810
|
+
* Returns the path of the file that already declares `ruflo`/`claude-flow` (so we can
|
|
811
811
|
* surface it in the skipped-message), or null if none found.
|
|
812
812
|
*/
|
|
813
813
|
function detectExistingRufloMCP(targetDir) {
|
|
@@ -840,10 +840,9 @@ function detectExistingRufloMCP(targetDir) {
|
|
|
840
840
|
if (!parsed || typeof parsed !== 'object')
|
|
841
841
|
continue;
|
|
842
842
|
// (a) Top-level mcpServers (legacy / global form).
|
|
843
|
-
//
|
|
844
|
-
//
|
|
845
|
-
//
|
|
846
|
-
// 'claude-flow', a second `ruflo init` must still recognise the existing install.
|
|
843
|
+
// Accept BOTH names so init does not add a second server for the same
|
|
844
|
+
// binary when a project carries a pre-rename `claude-flow` registration
|
|
845
|
+
// and the current generator would write `ruflo` (#2612).
|
|
847
846
|
if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {
|
|
848
847
|
const servers = parsed.mcpServers;
|
|
849
848
|
// #2369: also recognise the legacy dist-tag keys generated by
|
|
@@ -859,10 +858,9 @@ function detectExistingRufloMCP(targetDir) {
|
|
|
859
858
|
}
|
|
860
859
|
// (b) #1840: Claude Code project-scoped registrations under
|
|
861
860
|
// parsed.projects[<projectPath>].mcpServers. Match by
|
|
862
|
-
// normalized path against targetDir or any of its ancestors so
|
|
863
|
-
//
|
|
861
|
+
// normalized path against targetDir or any of its ancestors so an
|
|
862
|
+
// existing `claude-flow` or `ruflo` registration in this repo is
|
|
864
863
|
// detected even when Claude stored the key with different casing/slash style.
|
|
865
|
-
// #2207: accept both keys here too.
|
|
866
864
|
if (parsed.projects && typeof parsed.projects === 'object') {
|
|
867
865
|
for (const [projectKey, projectVal] of Object.entries(parsed.projects)) {
|
|
868
866
|
if (!projectVal || typeof projectVal !== 'object')
|
|
@@ -925,16 +923,18 @@ async function writeMCPConfig(targetDir, options, result) {
|
|
|
925
923
|
result.skipped.push('.mcp.json');
|
|
926
924
|
return;
|
|
927
925
|
}
|
|
928
|
-
// #1779 — Skip writing if the user already has
|
|
929
|
-
//
|
|
930
|
-
//
|
|
931
|
-
//
|
|
932
|
-
//
|
|
933
|
-
//
|
|
926
|
+
// #1779/#2612 — Skip writing if the user already has this MCP server
|
|
927
|
+
// registered elsewhere (parent .mcp.json, ~/.claude.json, etc). The
|
|
928
|
+
// canonical key is `claude-flow` (per #2206 — matches mcp__claude-flow__*
|
|
929
|
+
// plugin tool refs); a stray `ruflo`-keyed entry pointing at the same
|
|
930
|
+
// binary is the legacy-duplicate form that #2612 healed. Writing our
|
|
931
|
+
// fresh `claude-flow` entry on top of either variant starts the same
|
|
932
|
+
// binary twice under two tool namespaces. Force-mode (`--force`)
|
|
933
|
+
// bypasses this guard for users who actually want both registrations.
|
|
934
934
|
if (!options.force) {
|
|
935
935
|
const existingRufloPath = detectExistingRufloMCP(targetDir);
|
|
936
936
|
if (existingRufloPath) {
|
|
937
|
-
result.skipped.push(`.mcp.json (existing
|
|
937
|
+
result.skipped.push(`.mcp.json (existing ruflo/claude-flow MCP registration found at ${existingRufloPath} — would create duplicate; pass --force to write anyway)`);
|
|
938
938
|
return;
|
|
939
939
|
}
|
|
940
940
|
}
|
|
@@ -1880,7 +1880,7 @@ npx @claude-flow/cli@latest hive-mind consensus --propose "task"
|
|
|
1880
1880
|
### MCP Server Setup
|
|
1881
1881
|
\`\`\`bash
|
|
1882
1882
|
# Add Ruflo MCP
|
|
1883
|
-
claude mcp add ruflo -- npx -y ruflo@latest
|
|
1883
|
+
claude mcp add ruflo -- npx -y ruflo@latest mcp start
|
|
1884
1884
|
|
|
1885
1885
|
# Optional servers
|
|
1886
1886
|
claude mcp add ruv-swarm -- npx -y ruv-swarm mcp start
|
|
@@ -40,10 +40,15 @@ export function generateMCPConfig(options) {
|
|
|
40
40
|
const npmEnv = {
|
|
41
41
|
npm_config_update_notifier: 'false',
|
|
42
42
|
};
|
|
43
|
-
// Ruflo MCP server (core) —
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
43
|
+
// Ruflo MCP server (core) — the registration KEY is intentionally
|
|
44
|
+
// `claude-flow` (not `ruflo`) because #2206 established that all ~166
|
|
45
|
+
// plugin tool references use `mcp__claude-flow__*`. The invoked binary
|
|
46
|
+
// is `ruflo@latest` (the post-rename wrapper) — only the registration
|
|
47
|
+
// name stays legacy so plugin tool resolution keeps working.
|
|
48
|
+
// #2612 (duplicate `claude-flow` + `ruflo` registrations after users
|
|
49
|
+
// followed pre-rename setup docs) is healed by `ruflo doctor`, which
|
|
50
|
+
// detects the duplicate and instructs the operator to remove the
|
|
51
|
+
// extra `ruflo`-keyed entry — NOT by flipping the canonical key here.
|
|
47
52
|
if (config.claudeFlow) {
|
|
48
53
|
mcpServers['claude-flow'] = createMCPServerEntry(['ruflo@latest', 'mcp', 'start'], {
|
|
49
54
|
...npmEnv,
|
|
@@ -79,7 +84,7 @@ export function generateMCPCommands(options) {
|
|
|
79
84
|
const config = options.mcp;
|
|
80
85
|
if (isWindows()) {
|
|
81
86
|
if (config.claudeFlow) {
|
|
82
|
-
// #2206: registration name must be
|
|
87
|
+
// #2206: registration name must be `claude-flow` to match mcp__claude-flow__* plugin tool references
|
|
83
88
|
commands.push('claude mcp add claude-flow -- cmd /c npx -y ruflo@latest mcp start');
|
|
84
89
|
}
|
|
85
90
|
if (config.ruvSwarm) {
|
|
@@ -91,7 +96,7 @@ export function generateMCPCommands(options) {
|
|
|
91
96
|
}
|
|
92
97
|
else {
|
|
93
98
|
if (config.claudeFlow) {
|
|
94
|
-
// #2206: registration name must be
|
|
99
|
+
// #2206: registration name must be `claude-flow` to match mcp__claude-flow__* plugin tool references
|
|
95
100
|
commands.push("claude mcp add claude-flow -- npx -y ruflo@latest mcp start");
|
|
96
101
|
}
|
|
97
102
|
if (config.ruvSwarm) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.25.
|
|
3
|
+
"version": "3.25.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -88,7 +88,10 @@
|
|
|
88
88
|
"test": "vitest run",
|
|
89
89
|
"test:plugin-store": "npx tsx src/plugins/tests/standalone-test.ts",
|
|
90
90
|
"test:pattern-store": "npx tsx src/transfer/store/tests/standalone-test.ts",
|
|
91
|
-
"postinstall": "node ./scripts/postinstall.cjs"
|
|
91
|
+
"postinstall": "node ./scripts/postinstall.cjs",
|
|
92
|
+
"prepublishOnly": "cp ../../../README.md ./README.md && rm -rf plugins && mkdir -p plugins && cp -r ../../../plugins/ruflo-metaharness plugins/ && node scripts/sign-helpers.mjs && node scripts/verify-helpers.mjs",
|
|
93
|
+
"release": "npm version prerelease --preid=alpha && npm run publish:all",
|
|
94
|
+
"publish:all": "./scripts/publish.sh"
|
|
92
95
|
},
|
|
93
96
|
"devDependencies": {
|
|
94
97
|
"typescript": "^5.3.0",
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
{
|
|
2
|
+
"running": true,
|
|
3
|
+
"startedAt": "2026-07-09T13:44:14.858Z",
|
|
4
|
+
"workers": {
|
|
5
|
+
"map": {
|
|
6
|
+
"runCount": 3,
|
|
7
|
+
"successCount": 3,
|
|
8
|
+
"failureCount": 0,
|
|
9
|
+
"averageDurationMs": 2,
|
|
10
|
+
"isRunning": false,
|
|
11
|
+
"nextRun": "2026-07-09T14:29:14.860Z",
|
|
12
|
+
"lastStartedAt": "2026-07-09T14:14:14.854Z",
|
|
13
|
+
"lastRun": "2026-07-09T14:14:14.858Z"
|
|
14
|
+
},
|
|
15
|
+
"audit": {
|
|
16
|
+
"runCount": 3,
|
|
17
|
+
"successCount": 3,
|
|
18
|
+
"failureCount": 0,
|
|
19
|
+
"averageDurationMs": 181799,
|
|
20
|
+
"isRunning": false,
|
|
21
|
+
"nextRun": "2026-07-09T14:12:48.865Z",
|
|
22
|
+
"lastStartedAt": "2026-07-09T14:12:48.864Z",
|
|
23
|
+
"lastRun": "2026-07-09T14:15:20.256Z"
|
|
24
|
+
},
|
|
25
|
+
"optimize": {
|
|
26
|
+
"runCount": 2,
|
|
27
|
+
"successCount": 2,
|
|
28
|
+
"failureCount": 0,
|
|
29
|
+
"averageDurationMs": 243440.5,
|
|
30
|
+
"isRunning": false,
|
|
31
|
+
"nextRun": "2026-07-09T14:26:21.738Z",
|
|
32
|
+
"lastStartedAt": "2026-07-09T14:05:34.528Z",
|
|
33
|
+
"lastRun": "2026-07-09T14:11:21.737Z"
|
|
34
|
+
},
|
|
35
|
+
"consolidate": {
|
|
36
|
+
"runCount": 1,
|
|
37
|
+
"successCount": 1,
|
|
38
|
+
"failureCount": 0,
|
|
39
|
+
"averageDurationMs": 0,
|
|
40
|
+
"isRunning": false,
|
|
41
|
+
"nextRun": "2026-07-09T14:20:14.861Z",
|
|
42
|
+
"lastStartedAt": "2026-07-09T13:50:14.860Z",
|
|
43
|
+
"lastRun": "2026-07-09T13:50:14.860Z"
|
|
44
|
+
},
|
|
45
|
+
"testgaps": {
|
|
46
|
+
"runCount": 1,
|
|
47
|
+
"successCount": 1,
|
|
48
|
+
"failureCount": 0,
|
|
49
|
+
"averageDurationMs": 181886,
|
|
50
|
+
"isRunning": true,
|
|
51
|
+
"nextRun": "2026-07-09T14:15:16.745Z",
|
|
52
|
+
"lastStartedAt": "2026-07-09T14:15:16.740Z",
|
|
53
|
+
"lastRun": "2026-07-09T13:55:16.744Z"
|
|
54
|
+
},
|
|
55
|
+
"backup": {
|
|
56
|
+
"runCount": 1,
|
|
57
|
+
"successCount": 1,
|
|
58
|
+
"failureCount": 0,
|
|
59
|
+
"averageDurationMs": 4,
|
|
60
|
+
"isRunning": false,
|
|
61
|
+
"nextRun": "2026-07-10T13:54:14.862Z",
|
|
62
|
+
"lastStartedAt": "2026-07-09T13:54:14.856Z",
|
|
63
|
+
"lastRun": "2026-07-09T13:54:14.860Z"
|
|
64
|
+
},
|
|
65
|
+
"harness": {
|
|
66
|
+
"runCount": 1,
|
|
67
|
+
"successCount": 1,
|
|
68
|
+
"failureCount": 0,
|
|
69
|
+
"averageDurationMs": 7,
|
|
70
|
+
"isRunning": false,
|
|
71
|
+
"nextRun": "2026-07-09T19:56:14.864Z",
|
|
72
|
+
"lastStartedAt": "2026-07-09T13:56:14.856Z",
|
|
73
|
+
"lastRun": "2026-07-09T13:56:14.863Z"
|
|
74
|
+
},
|
|
75
|
+
"predict": {
|
|
76
|
+
"runCount": 0,
|
|
77
|
+
"successCount": 0,
|
|
78
|
+
"failureCount": 0,
|
|
79
|
+
"averageDurationMs": 0,
|
|
80
|
+
"isRunning": false
|
|
81
|
+
},
|
|
82
|
+
"document": {
|
|
83
|
+
"runCount": 0,
|
|
84
|
+
"successCount": 0,
|
|
85
|
+
"failureCount": 0,
|
|
86
|
+
"averageDurationMs": 0,
|
|
87
|
+
"isRunning": false
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"config": {
|
|
91
|
+
"autoStart": false,
|
|
92
|
+
"logDir": "/Users/cohen/Projects/ruflo/plugins/ruflo-metaharness/.claude-flow/logs",
|
|
93
|
+
"stateFile": "/Users/cohen/Projects/ruflo/plugins/ruflo-metaharness/.claude-flow/daemon-state.json",
|
|
94
|
+
"maxConcurrent": 2,
|
|
95
|
+
"workerTimeoutMs": 960000,
|
|
96
|
+
"resourceThresholds": {
|
|
97
|
+
"maxCpuLoad": 9.600000000000001,
|
|
98
|
+
"minFreeMemoryPercent": 5
|
|
99
|
+
},
|
|
100
|
+
"ttlMs": 43200000,
|
|
101
|
+
"idleShutdownMs": 0,
|
|
102
|
+
"workers": [
|
|
103
|
+
{
|
|
104
|
+
"type": "map",
|
|
105
|
+
"intervalMs": 900000,
|
|
106
|
+
"offsetMs": 0,
|
|
107
|
+
"priority": "normal",
|
|
108
|
+
"description": "Codebase mapping",
|
|
109
|
+
"enabled": true
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"type": "audit",
|
|
113
|
+
"intervalMs": 600000,
|
|
114
|
+
"offsetMs": 120000,
|
|
115
|
+
"priority": "critical",
|
|
116
|
+
"description": "Security analysis",
|
|
117
|
+
"enabled": true
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"type": "optimize",
|
|
121
|
+
"intervalMs": 900000,
|
|
122
|
+
"offsetMs": 240000,
|
|
123
|
+
"priority": "high",
|
|
124
|
+
"description": "Performance optimization",
|
|
125
|
+
"enabled": true
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"type": "consolidate",
|
|
129
|
+
"intervalMs": 1800000,
|
|
130
|
+
"offsetMs": 360000,
|
|
131
|
+
"priority": "low",
|
|
132
|
+
"description": "Memory distillation (ADR-174)",
|
|
133
|
+
"enabled": true
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"type": "testgaps",
|
|
137
|
+
"intervalMs": 1200000,
|
|
138
|
+
"offsetMs": 480000,
|
|
139
|
+
"priority": "normal",
|
|
140
|
+
"description": "Test coverage analysis",
|
|
141
|
+
"enabled": true
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"type": "backup",
|
|
145
|
+
"intervalMs": 86400000,
|
|
146
|
+
"offsetMs": 600000,
|
|
147
|
+
"priority": "low",
|
|
148
|
+
"description": "Nightly memory DB backup (WAL-safe, rotated)",
|
|
149
|
+
"enabled": true
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"type": "harness",
|
|
153
|
+
"intervalMs": 21600000,
|
|
154
|
+
"offsetMs": 720000,
|
|
155
|
+
"priority": "low",
|
|
156
|
+
"description": "Self-optimizing harness loop (opt-in RUFLO_HARNESS_LOOP, $0-default)",
|
|
157
|
+
"enabled": true
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"type": "predict",
|
|
161
|
+
"intervalMs": 600000,
|
|
162
|
+
"offsetMs": 0,
|
|
163
|
+
"priority": "low",
|
|
164
|
+
"description": "Predictive preloading",
|
|
165
|
+
"enabled": false
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"type": "document",
|
|
169
|
+
"intervalMs": 3600000,
|
|
170
|
+
"offsetMs": 0,
|
|
171
|
+
"priority": "low",
|
|
172
|
+
"description": "Auto-documentation",
|
|
173
|
+
"enabled": false
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
"savedAt": "2026-07-09T14:15:20.256Z"
|
|
178
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22169
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[2026-07-09T13:44:14.859Z] [INFO] Scheduled map (interval: 900s, first run in 0s)
|
|
2
|
+
[2026-07-09T13:44:14.859Z] [INFO] Scheduled audit (interval: 600s, first run in 120s)
|
|
3
|
+
[2026-07-09T13:44:14.859Z] [INFO] Scheduled optimize (interval: 900s, first run in 240s)
|
|
4
|
+
[2026-07-09T13:44:14.859Z] [INFO] Scheduled consolidate (interval: 1800s, first run in 360s)
|
|
5
|
+
[2026-07-09T13:44:14.859Z] [INFO] Scheduled testgaps (interval: 1200s, first run in 480s)
|
|
6
|
+
[2026-07-09T13:44:14.859Z] [INFO] Scheduled backup (interval: 86400s, first run in 600s)
|
|
7
|
+
[2026-07-09T13:44:14.859Z] [INFO] Scheduled harness (interval: 21600s, first run in 720s)
|
|
8
|
+
[2026-07-09T13:44:14.859Z] [INFO] Lifecycle monitor active (ttl=43200s)
|
|
9
|
+
[2026-07-09T13:44:14.860Z] [INFO] Daemon started (PID: 22169, CPUs: 12, workers: 7, maxCpuLoad: 9.600000000000001, minFreeMemoryPercent: 5%)
|
|
10
|
+
[2026-07-09T13:44:14.860Z] [INFO] Claude Code headless mode available - AI workers enabled
|
|
11
|
+
[2026-07-09T13:44:14.860Z] [INFO] Starting worker: map (1/2 concurrent)
|
|
12
|
+
[2026-07-09T13:44:14.861Z] [INFO] Worker map completed in 1ms
|
|
13
|
+
[2026-07-09T13:46:14.862Z] [INFO] Starting worker: audit (1/2 concurrent)
|
|
14
|
+
[2026-07-09T13:46:14.863Z] [INFO] Running audit in headless mode (Claude Code AI)
|
|
15
|
+
[2026-07-09T13:48:14.860Z] [INFO] Starting worker: optimize (2/2 concurrent)
|
|
16
|
+
[2026-07-09T13:48:14.860Z] [INFO] Running optimize in headless mode (Claude Code AI)
|
|
17
|
+
[2026-07-09T13:48:33.588Z] [INFO] Worker audit completed in 138728ms
|
|
18
|
+
[2026-07-09T13:50:14.860Z] [INFO] Starting worker: consolidate (2/2 concurrent)
|
|
19
|
+
[2026-07-09T13:50:14.860Z] [INFO] Consolidate worker: distillation skipped (no-db)
|
|
20
|
+
[2026-07-09T13:50:14.860Z] [INFO] Worker consolidate completed in 0ms
|
|
21
|
+
[2026-07-09T13:50:34.530Z] [INFO] Worker optimize completed in 139672ms
|
|
22
|
+
[2026-07-09T13:52:14.860Z] [INFO] Starting worker: testgaps (1/2 concurrent)
|
|
23
|
+
[2026-07-09T13:52:14.860Z] [INFO] Running testgaps in headless mode (Claude Code AI)
|
|
24
|
+
[2026-07-09T13:54:14.858Z] [INFO] Starting worker: backup (2/2 concurrent)
|
|
25
|
+
[2026-07-09T13:54:14.860Z] [INFO] Worker backup completed in 4ms
|
|
26
|
+
[2026-07-09T13:55:16.744Z] [INFO] Worker testgaps completed in 181886ms
|
|
27
|
+
[2026-07-09T13:56:14.857Z] [INFO] Starting worker: harness (1/2 concurrent)
|
|
28
|
+
[2026-07-09T13:56:14.863Z] [INFO] Worker harness completed in 7ms
|
|
29
|
+
[2026-07-09T13:58:33.587Z] [INFO] Starting worker: audit (1/2 concurrent)
|
|
30
|
+
[2026-07-09T13:58:33.587Z] [INFO] Running audit in headless mode (Claude Code AI)
|
|
31
|
+
[2026-07-09T13:59:14.859Z] [INFO] Starting worker: map (2/2 concurrent)
|
|
32
|
+
[2026-07-09T13:59:14.859Z] [INFO] Worker map completed in 1ms
|
|
33
|
+
[2026-07-09T14:02:48.864Z] [INFO] Worker audit completed in 255277ms
|
|
34
|
+
[2026-07-09T14:05:34.531Z] [INFO] Starting worker: optimize (1/2 concurrent)
|
|
35
|
+
[2026-07-09T14:05:34.531Z] [INFO] Running optimize in headless mode (Claude Code AI)
|
|
36
|
+
[2026-07-09T14:11:21.737Z] [INFO] Worker optimize completed in 347209ms
|
|
37
|
+
[2026-07-09T14:12:48.866Z] [INFO] Starting worker: audit (1/2 concurrent)
|
|
38
|
+
[2026-07-09T14:12:48.867Z] [INFO] Running audit in headless mode (Claude Code AI)
|
|
39
|
+
[2026-07-09T14:14:14.857Z] [INFO] Starting worker: map (2/2 concurrent)
|
|
40
|
+
[2026-07-09T14:14:14.858Z] [INFO] Worker map completed in 4ms
|
|
41
|
+
[2026-07-09T14:15:16.742Z] [INFO] Starting worker: testgaps (2/2 concurrent)
|
|
42
|
+
[2026-07-09T14:15:16.743Z] [INFO] Running testgaps in headless mode (Claude Code AI)
|
|
43
|
+
[2026-07-09T14:15:20.256Z] [INFO] Worker audit completed in 151392ms
|
package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783604774864_ozbujc_prompt.log
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[2026-07-09T13:46:14.882Z] PROMPT
|
|
2
|
+
============================================================
|
|
3
|
+
Analyze this codebase for security vulnerabilities:
|
|
4
|
+
- Check for hardcoded secrets (API keys, passwords)
|
|
5
|
+
- Identify SQL injection risks
|
|
6
|
+
- Find XSS vulnerabilities
|
|
7
|
+
- Check for insecure dependencies
|
|
8
|
+
- Identify authentication/authorization issues
|
|
9
|
+
|
|
10
|
+
Provide a JSON report with:
|
|
11
|
+
{
|
|
12
|
+
"vulnerabilities": [{ "severity": "high|medium|low", "file": "...", "line": N, "description": "..." }],
|
|
13
|
+
"riskScore": 0-100,
|
|
14
|
+
"recommendations": ["..."]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
## Instructions
|
|
18
|
+
|
|
19
|
+
Analyze the codebase and provide your response following the format specified in the task.
|
package/plugins/ruflo-metaharness/.claude-flow/logs/headless/audit_1783604774864_ozbujc_result.log
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
[2026-07-09T13:48:33.587Z] RESULT
|
|
2
|
+
============================================================
|
|
3
|
+
{
|
|
4
|
+
"success": true,
|
|
5
|
+
"output": "## Security Audit Complete\n\nThe security auditor agent has completed a comprehensive scan. Combined with my manual analysis, here's the consolidated **Security Audit Report**:\n\n```json\n{\n \"vulnerabilities\": [\n {\n \"severity\": \"high\",\n \"file\": \"scripts/test-with-openrouter.mjs\",\n \"line\": 64,\n \"description\": \"Command injection vulnerability via execSync with template string interpolation. The secretName parameter is directly interpolated into a shell command without proper escaping. Although currently hardcoded as 'OPENROUTER_API_KEY', this is an unsafe code pattern that violates Node.js security best practices.\",\n \"codeSnippet\": \"const out = execSync(\\n `gcloud secrets versions access latest --secret=${secretName}`,\\n { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000 },\\n);\",\n \"recommendation\": \"Replace with spawnSync using array arguments: spawnSync('gcloud', ['secrets', 'versions', 'access', 'latest', '--secret', secretName], {...})\"\n },\n {\n \"severity\": \"medium\",\n \"file\": \"scripts/audit-trend.mjs, audit-list.mjs, similarity.mjs, oia-audit.mjs, drift-from-history.mjs\",\n \"line\": \"22-24 (various)\",\n \"description\": \"Uses @claude-flow/cli@latest which fetches the latest version on every invocation. This contradicts ADR-150's pinning requirement and creates supply chain risk if npm registry is compromised or package account hijacked.\",\n \"codeSnippet\": \"const CLI_PKG = process.env.CLI_CORE === '1'\\n ? '@claude-flow/cli-core@alpha'\\n : '@claude-flow/cli@latest';\",\n \"recommendation\": \"Pin to specific semver range: '@claude-flow/cli@~3.7.0' (aligns with ADR-150 architecture)\"\n },\n {\n \"severity\": \"low\",\n \"file\": \"scripts/test-with-openrouter.mjs\",\n \"line\": 144,\n \"description\": \"API key prefix exposure in logging. Logs first 7 characters of API key which could help attackers identify which key type and potentially which account it belongs to.\",\n \"codeSnippet\": \"console.log(` key length: ${apiKey.length}, prefix: ${apiKey.slice(0, 7)}…`);\",\n \"recommendation\": \"Remove prefix logging. Use generic message: 'OPENROUTER_API_KEY loaded successfully'\"\n },\n {\n \"severity\": \"low\",\n \"file\": \"scripts/redblue.mjs\",\n \"line\": 217,\n \"description\": \"Path resolution doesn't canonicalize symlinks. User-provided paths are resolved but not checked for symlink attacks pointing outside intended directories.\",\n \"codeSnippet\": \"const reportPath = resolve(ARGS.out);\\nif (existsSync(reportPath)) {\\n try { payload = JSON.parse(readFileSync(reportPath, 'utf-8'));\",\n \"recommendation\": \"Use fs.realpathSync() to canonicalize paths: const realPath = realpathSync(reportPath)\"\n },\n {\n \"severity\": \"low\",\n \"file\": \"scripts/router-parallel-analyze.mjs\",\n \"line\": \"89-92\",\n \"description\": \"JSONL parsing lacks error context. If JSON.parse fails on a line, no line number is reported, making debugging difficult.\",\n \"codeSnippet\": \"rows = readFileSync(ARGS.input, 'utf-8')\\n .split('\\\\n')\\n .filter((l) => l.trim())\\n .map((l) => JSON.parse(l));\",\n \"recommendation\": \"Add try-catch with line number: .map((l, idx) => { try { return JSON.parse(l); } catch(e) { throw new Error(`Line ${idx+1}: ${e.message}`); } })\"\n },\n {\n \"severity\": \"low\",\n \"file\": \"scripts/test-with-openrouter.mjs\",\n \"line\": 138,\n \"description\": \"Weak API key validation. Checks length > 20, but modern API keys are typically 40+ characters. Weak validation could pass invalid or truncated keys.\",\n \"codeSnippet\": \"assert(typeof apiKey === 'string' && apiKey.length > 20, 'OPENROUTER_API_KEY fetched (length OK)');\",\n \"recommendation\": \"Use stricter validation: apiKey.length >= 40 && /^[a-zA-Z0-9_-]+$/.test(apiKey)\"\n }\n ],\n \"riskScore\": 32,\n \"summary\": \"The ruflo-metaharness plugin demonstrates strong overall security practices: all 30+ subprocess calls use shell:false with array arguments (preventing injection), no eval/Function() usage, no hardcoded secrets, and proper error handling. However, there is one high-severity command injection vulnerability (execSync with template strings) and medium-severity supply chain risks from using @latest instead of pinned versions. Four additional low-severity issues affect defense-in-depth. With these fixes, the plugin would achieve excellent security posture.\",\n \"recommendations\": [\n {\n \"priority\": \"CRITICAL\",\n \"action\": \"Fix command injection in test-with-openrouter.mjs line 64. Replace execSync with template strings with spawnSync array-based arguments.\"\n },\n {\n \"priority\": \"HIGH\",\n \"action\": \"Pin all @claude-flow/cli@latest references to specific versions (e.g., ~3.7.0) in: audit-trend.mjs, drift-from-history.mjs, similarity.mjs, oia-audit.mjs, audit-list.mjs\"\n },\n {\n \"priority\": \"MEDIUM\",\n \"action\": \"Remove API key prefix logging from console output (line 144 of test-with-openrouter.mjs)\"\n },\n {\n \"priority\": \"MEDIUM\",\n \"action\": \"Add fs.realpathSync() path canonicalization in redblue.mjs and other file operations\"\n },\n {\n \"priority\": \"LOW\",\n \"action\": \"Improve JSONL parsing error reporting with line numbers in router-parallel-analyze.mjs\"\n },\n {\n \"priority\": \"LOW\",\n \"action\": \"Strengthen API key validation to check for minimum 40-character length and valid character patterns\"\n },\n {\n \"priority\": \"INFORMATIONAL\",\n \"action\": \"Add security.md documentation with secure coding guidelines for this plugin\"\n }\n ],\n \"positives\": [\n \"✓ All subprocess calls use shell:false with array arguments (prevents shell injection)\",\n \"✓ Zero instances of eval, Function(), or dynamic code execution\",\n \"✓ No hardcoded secrets, API keys, or credentials anywhere in codebase\",\n \"✓ Proper input validation with explicit type conversions\",\n \"✓ Safe JSON parsing with try-catch error handling\",\n \"✓ Graceful degradation for missing optional dependencies (ADR-150 compliant)\",\n \"✓ Correct path handling with resolve() and existsSync() checks\",\n \"✓ Proper error handling with explicit exit codes\",\n \"✓ Environment variables used safely with explicit defaults\",\n \"✓ No dangerous use of template literals in subprocess/file operations\"\n ]\n}\n```\n\n### Summary\n\n**Risk Score: 32/100** (Low-Medium Risk)\n\n**Key Issues:**\n1. **1 Critical** - Command injection pattern (execSync + template strings)\n2. **1 Medium** - Supply chain risk (@latest npm packages)\n3. **4 Low** - Defense-in-depth improvements needed\n\n**Strengths:**\n- 30+ subprocess calls properly secured with shell:false\n- No code execution vulnerabilities\n- No credential leaks\n- Strong error handling throughout\n\n**Next Steps:**\n1. Address the critical execSync vulnerability immediately\n2. Pin npm package versions per ADR-150\n3. Apply defense-in-depth improvements for path handling and validation\n\nAll issues are straightforward to fix and align with the project's own architectural guidelines.\n",
|
|
6
|
+
"parsedOutput": {
|
|
7
|
+
"vulnerabilities": [
|
|
8
|
+
{
|
|
9
|
+
"severity": "high",
|
|
10
|
+
"file": "scripts/test-with-openrouter.mjs",
|
|
11
|
+
"line": 64,
|
|
12
|
+
"description": "Command injection vulnerability via execSync with template string interpolation. The secretName parameter is directly interpolated into a shell command without proper escaping. Although currently hardcoded as 'OPENROUTER_API_KEY', this is an unsafe code pattern that violates Node.js security best practices.",
|
|
13
|
+
"codeSnippet": "const out = execSync(\n `gcloud secrets versions access latest --secret=${secretName}`,\n { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000 },\n);",
|
|
14
|
+
"recommendation": "Replace with spawnSync using array arguments: spawnSync('gcloud', ['secrets', 'versions', 'access', 'latest', '--secret', secretName], {...})"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"severity": "medium",
|
|
18
|
+
"file": "scripts/audit-trend.mjs, audit-list.mjs, similarity.mjs, oia-audit.mjs, drift-from-history.mjs",
|
|
19
|
+
"line": "22-24 (various)",
|
|
20
|
+
"description": "Uses @claude-flow/cli@latest which fetches the latest version on every invocation. This contradicts ADR-150's pinning requirement and creates supply chain risk if npm registry is compromised or package account hijacked.",
|
|
21
|
+
"codeSnippet": "const CLI_PKG = process.env.CLI_CORE === '1'\n ? '@claude-flow/cli-core@alpha'\n : '@claude-flow/cli@latest';",
|
|
22
|
+
"recommendation": "Pin to specific semver range: '@claude-flow/cli@~3.7.0' (aligns with ADR-150 architecture)"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"severity": "low",
|
|
26
|
+
"file": "scripts/test-with-openrouter.mjs",
|
|
27
|
+
"line": 144,
|
|
28
|
+
"description": "API key prefix exposure in logging. Logs first 7 characters of API key which could help attackers identify which key type and potentially which account it belongs to.",
|
|
29
|
+
"codeSnippet": "console.log(` key length: ${apiKey.length}, prefix: ${apiKey.slice(0, 7)}…`);",
|
|
30
|
+
"recommendation": "Remove prefix logging. Use generic message: 'OPENROUTER_API_KEY loaded successfully'"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"severity": "low",
|
|
34
|
+
"file": "scripts/redblue.mjs",
|
|
35
|
+
"line": 217,
|
|
36
|
+
"description": "Path resolution doesn't canonicalize symlinks. User-provided paths are resolved but not checked for symlink attacks pointing outside intended directories.",
|
|
37
|
+
"codeSnippet": "const reportPath = resolve(ARGS.out);\nif (existsSync(reportPath)) {\n try { payload = JSON.parse(readFileSync(reportPath, 'utf-8'));",
|
|
38
|
+
"recommendation": "Use fs.realpathSync() to canonicalize paths: const realPath = realpathSync(reportPath)"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"severity": "low",
|
|
42
|
+
"file": "scripts/router-parallel-analyze.mjs",
|
|
43
|
+
"line": "89-92",
|
|
44
|
+
"description": "JSONL parsing lacks error context. If JSON.parse fails on a line, no line number is reported, making debugging difficult.",
|
|
45
|
+
"codeSnippet": "rows = readFileSync(ARGS.input, 'utf-8')\n .split('\\n')\n .filter((l) => l.trim())\n .map((l) => JSON.parse(l));",
|
|
46
|
+
"recommendation": "Add try-catch with line number: .map((l, idx) => { try { return JSON.parse(l); } catch(e) { throw new Error(`Line ${idx+1}: ${e.message}`); } })"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"severity": "low",
|
|
50
|
+
"file": "scripts/test-with-openrouter.mjs",
|
|
51
|
+
"line": 138,
|
|
52
|
+
"description": "Weak API key validation. Checks length > 20, but modern API keys are typically 40+ characters. Weak validation could pass invalid or truncated keys.",
|
|
53
|
+
"codeSnippet": "assert(typeof apiKey === 'string' && apiKey.length > 20, 'OPENROUTER_API_KEY fetched (length OK)');",
|
|
54
|
+
"recommendation": "Use stricter validation: apiKey.length >= 40 && /^[a-zA-Z0-9_-]+$/.test(apiKey)"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"riskScore": 32,
|
|
58
|
+
"summary": "The ruflo-metaharness plugin demonstrates strong overall security practices: all 30+ subprocess calls use shell:false with array arguments (preventing injection), no eval/Function() usage, no hardcoded secrets, and proper error handling. However, there is one high-severity command injection vulnerability (execSync with template strings) and medium-severity supply chain risks from using @latest instead of pinned versions. Four additional low-severity issues affect defense-in-depth. With these fixes, the plugin would achieve excellent security posture.",
|
|
59
|
+
"recommendations": [
|
|
60
|
+
{
|
|
61
|
+
"priority": "CRITICAL",
|
|
62
|
+
"action": "Fix command injection in test-with-openrouter.mjs line 64. Replace execSync with template strings with spawnSync array-based arguments."
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"priority": "HIGH",
|
|
66
|
+
"action": "Pin all @claude-flow/cli@latest references to specific versions (e.g., ~3.7.0) in: audit-trend.mjs, drift-from-history.mjs, similarity.mjs, oia-audit.mjs, audit-list.mjs"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"priority": "MEDIUM",
|
|
70
|
+
"action": "Remove API key prefix logging from console output (line 144 of test-with-openrouter.mjs)"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"priority": "MEDIUM",
|
|
74
|
+
"action": "Add fs.realpathSync() path canonicalization in redblue.mjs and other file operations"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"priority": "LOW",
|
|
78
|
+
"action": "Improve JSONL parsing error reporting with line numbers in router-parallel-analyze.mjs"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"priority": "LOW",
|
|
82
|
+
"action": "Strengthen API key validation to check for minimum 40-character length and valid character patterns"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"priority": "INFORMATIONAL",
|
|
86
|
+
"action": "Add security.md documentation with secure coding guidelines for this plugin"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"positives": [
|
|
90
|
+
"✓ All subprocess calls use shell:false with array arguments (prevents shell injection)",
|
|
91
|
+
"✓ Zero instances of eval, Function(), or dynamic code execution",
|
|
92
|
+
"✓ No hardcoded secrets, API keys, or credentials anywhere in codebase",
|
|
93
|
+
"✓ Proper input validation with explicit type conversions",
|
|
94
|
+
"✓ Safe JSON parsing with try-catch error handling",
|
|
95
|
+
"✓ Graceful degradation for missing optional dependencies (ADR-150 compliant)",
|
|
96
|
+
"✓ Correct path handling with resolve() and existsSync() checks",
|
|
97
|
+
"✓ Proper error handling with explicit exit codes",
|
|
98
|
+
"✓ Environment variables used safely with explicit defaults",
|
|
99
|
+
"✓ No dangerous use of template literals in subprocess/file operations"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"durationMs": 138723,
|
|
103
|
+
"model": "haiku",
|
|
104
|
+
"sandboxMode": "strict",
|
|
105
|
+
"workerType": "audit",
|
|
106
|
+
"timestamp": "2026-07-09T13:48:33.587Z",
|
|
107
|
+
"executionId": "audit_1783604774864_ozbujc"
|
|
108
|
+
}
|