network-ai 5.12.4 → 5.12.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/README.md +2 -2
- package/SKILL.md +3 -0
- package/bin/console.ts +12 -0
- package/dist/bin/console.js +12 -0
- package/dist/bin/console.js.map +1 -1
- package/dist/esm/lib/agent-runtime.js +1 -1
- package/dist/esm/lib/agent-runtime.js.map +1 -1
- package/dist/esm/lib/blackboard-validator.js +6 -4
- package/dist/esm/lib/blackboard-validator.js.map +1 -1
- package/dist/esm/lib/phase-pipeline.js.map +1 -1
- package/dist/lib/agent-runtime.js +1 -1
- package/dist/lib/agent-runtime.js.map +1 -1
- package/dist/lib/blackboard-validator.d.ts.map +1 -1
- package/dist/lib/blackboard-validator.js +6 -4
- package/dist/lib/blackboard-validator.js.map +1 -1
- package/dist/lib/phase-pipeline.js.map +1 -1
- package/package.json +5 -2
- package/scripts/codeql-check.js +140 -0
- package/scripts/socket-check.js +210 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* socket-check.js — Socket.dev score monitor for network-ai
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node scripts/socket-check.js # scan published npm package (current version)
|
|
7
|
+
* node scripts/socket-check.js --version 5.12.4 # scan a specific published version
|
|
8
|
+
* node scripts/socket-check.js --local # scan the local project directory
|
|
9
|
+
* npm run socket:check # alias for default mode
|
|
10
|
+
*
|
|
11
|
+
* Exit codes:
|
|
12
|
+
* 0 — all fixable alerts absent (gptSecurity, debugAccess gone)
|
|
13
|
+
* 1 — one or more fixable alerts still present, or scan failed
|
|
14
|
+
*
|
|
15
|
+
* Requires: npx @socketsecurity/cli (installed on first run via npx -y)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const { spawnSync } = require('child_process');
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
// ── Config ─────────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Alerts we are actively trying to eliminate.
|
|
28
|
+
* Any of these present → exit 1.
|
|
29
|
+
*/
|
|
30
|
+
const FIXABLE_ALERTS = ['gptSecurity', 'debugAccess'];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Alerts that are expected for this class of tool and are NOT considered
|
|
34
|
+
* failures by this script. Document them here so the output is informative.
|
|
35
|
+
*/
|
|
36
|
+
const EXPECTED_ALERTS = {
|
|
37
|
+
networkAccess: 'Expected — MCP/HTTP server + A2A adapter (product feature)',
|
|
38
|
+
shellAccess: 'Expected — AgentRuntime shell sandbox (opt-in, policy-gated)',
|
|
39
|
+
shellExec: 'Expected — AgentRuntime shell sandbox (opt-in, policy-gated)',
|
|
40
|
+
recentlyPublished: 'Expected — auto-clears ~30 days after publish',
|
|
41
|
+
pendingScan: 'Expected — Socket is still scanning a freshly-published version; re-run in a few minutes',
|
|
42
|
+
envVars: 'Expected — API key reading in adapters (operator-supplied)',
|
|
43
|
+
filesystemAccess: 'Expected — LockedBlackboard file I/O (product feature)',
|
|
44
|
+
urlStrings: 'Expected — comes from commander dependency, not network-ai',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// ANSI colours (safe to use in any modern terminal)
|
|
48
|
+
const c = {
|
|
49
|
+
reset: '\x1b[0m',
|
|
50
|
+
bold: '\x1b[1m',
|
|
51
|
+
red: '\x1b[31m',
|
|
52
|
+
green: '\x1b[32m',
|
|
53
|
+
yellow: '\x1b[33m',
|
|
54
|
+
cyan: '\x1b[36m',
|
|
55
|
+
gray: '\x1b[90m',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// ── Argument parsing ────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
const args = process.argv.slice(2);
|
|
61
|
+
const localMode = args.includes('--local');
|
|
62
|
+
const versionFlag = args.indexOf('--version');
|
|
63
|
+
const forceVersion = versionFlag !== -1 ? args[versionFlag + 1] : null;
|
|
64
|
+
|
|
65
|
+
// ── Resolve version ─────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
68
|
+
const version = forceVersion || pkg.version;
|
|
69
|
+
const packageName = pkg.name;
|
|
70
|
+
|
|
71
|
+
// Validate user-supplied version is safe semver (guards against shell injection)
|
|
72
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
73
|
+
if (forceVersion && !SEMVER_RE.test(forceVersion)) {
|
|
74
|
+
console.error(`Invalid --version value: ${JSON.stringify(forceVersion)} — must be semver (e.g. 5.12.5)`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// npx binary name — on Windows the shim is npx.cmd
|
|
79
|
+
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
80
|
+
|
|
81
|
+
// ── Run Socket CLI ──────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
console.log(`\n${c.bold}${c.cyan}Socket.dev Score Check${c.reset}`);
|
|
84
|
+
console.log(`${c.gray}${'─'.repeat(50)}${c.reset}`);
|
|
85
|
+
|
|
86
|
+
let raw;
|
|
87
|
+
if (localMode) {
|
|
88
|
+
console.log(`${c.gray}Mode: local scan (${process.cwd()})${c.reset}\n`);
|
|
89
|
+
const r = spawnSync(
|
|
90
|
+
npx, ['-y', '@socketsecurity/cli', 'scan', '.'],
|
|
91
|
+
{ cwd: path.join(__dirname, '..'), encoding: 'utf8' }
|
|
92
|
+
);
|
|
93
|
+
raw = (r.stdout || '') + (r.stderr || '');
|
|
94
|
+
} else {
|
|
95
|
+
const purl = `npm ${packageName}@${version}`;
|
|
96
|
+
console.log(`${c.gray}Mode: published package — ${purl}${c.reset}\n`);
|
|
97
|
+
// Use spawnSync with an arg array — no shell interpolation, immune to injection
|
|
98
|
+
const r = spawnSync(
|
|
99
|
+
npx, ['-y', '@socketsecurity/cli', 'package', 'shallow', 'npm', `${packageName}@${version}`],
|
|
100
|
+
{ cwd: path.join(__dirname, '..'), encoding: 'utf8' }
|
|
101
|
+
);
|
|
102
|
+
raw = (r.stdout || '') + (r.stderr || '');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── Parse output ─────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/** Strip ANSI escape codes from CLI output so regex matching works reliably. */
|
|
108
|
+
function stripAnsi(str) {
|
|
109
|
+
// eslint-disable-next-line no-control-regex
|
|
110
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '').replace(/[✔✖ℹ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/g, '');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const clean = stripAnsi(raw);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Extract supply-chain score from CLI output.
|
|
117
|
+
* Looks for: "- Supply Chain Risk: 75"
|
|
118
|
+
*/
|
|
119
|
+
function parseScore(text) {
|
|
120
|
+
const m = text.match(/Supply Chain(?:\s+Risk)?:\s*(\d+)/i);
|
|
121
|
+
if (!m) return null;
|
|
122
|
+
const v = parseInt(m[1], 10);
|
|
123
|
+
return v > 100 ? null : v; // guard against spurious matches during pending scan
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extract alert list from the Alerts line:
|
|
128
|
+
* "- Alerts (0/4/4): [middle] gptSecurity, [middle] networkAccess, ..."
|
|
129
|
+
*/
|
|
130
|
+
function parseAlerts(text) {
|
|
131
|
+
const alerts = [];
|
|
132
|
+
const tokenRe = /\[(middle|low|high|critical)\]\s+(\w+)/g;
|
|
133
|
+
let m;
|
|
134
|
+
while ((m = tokenRe.exec(text)) !== null) {
|
|
135
|
+
alerts.push({ severity: m[1], name: m[2] });
|
|
136
|
+
}
|
|
137
|
+
// Deduplicate
|
|
138
|
+
const seen = new Set();
|
|
139
|
+
return alerts.filter(a => {
|
|
140
|
+
const key = a.severity + ':' + a.name;
|
|
141
|
+
if (seen.has(key)) return false;
|
|
142
|
+
seen.add(key);
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const score = parseScore(clean);
|
|
148
|
+
const alerts = parseAlerts(clean);
|
|
149
|
+
|
|
150
|
+
// ── Display ──────────────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
if (score !== null) {
|
|
153
|
+
const colour = score >= 85 ? c.green : score >= 70 ? c.yellow : c.red;
|
|
154
|
+
console.log(`${c.bold}Supply Chain Score:${c.reset} ${colour}${c.bold}${score}/100${c.reset}`);
|
|
155
|
+
} else {
|
|
156
|
+
console.log(`${c.gray}(score not found in output — may need auth or package not yet published)${c.reset}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (alerts.length > 0) {
|
|
160
|
+
console.log(`\n${c.bold}Alerts (${alerts.length}):${c.reset}`);
|
|
161
|
+
const sevOrder = { critical: 0, high: 1, middle: 2, low: 3 };
|
|
162
|
+
const sorted = [...alerts].sort((a, b) => (sevOrder[a.severity] ?? 9) - (sevOrder[b.severity] ?? 9));
|
|
163
|
+
|
|
164
|
+
for (const alert of sorted) {
|
|
165
|
+
const isFixable = FIXABLE_ALERTS.includes(alert.name);
|
|
166
|
+
const isExpected = Object.prototype.hasOwnProperty.call(EXPECTED_ALERTS, alert.name);
|
|
167
|
+
|
|
168
|
+
let tag;
|
|
169
|
+
if (isFixable) {
|
|
170
|
+
tag = `${c.red}[FIXABLE]${c.reset}`;
|
|
171
|
+
} else if (isExpected) {
|
|
172
|
+
tag = `${c.gray}[expected]${c.reset}`;
|
|
173
|
+
} else {
|
|
174
|
+
tag = `${c.yellow}[review] ${c.reset}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const sevColour = alert.severity === 'middle' ? c.yellow : alert.severity === 'low' ? c.gray : c.red;
|
|
178
|
+
const note = isExpected ? ` ${c.gray}${EXPECTED_ALERTS[alert.name]}${c.reset}` : '';
|
|
179
|
+
console.log(` ${tag} ${sevColour}[${alert.severity}]${c.reset} ${c.bold}${alert.name}${c.reset}${note}`);
|
|
180
|
+
}
|
|
181
|
+
} else if (clean.includes('Alerts')) {
|
|
182
|
+
console.log(`\n${c.green}${c.bold}No alerts found.${c.reset}`);
|
|
183
|
+
} else {
|
|
184
|
+
console.log(`\n${c.gray}(alert list not parsed — raw output below)${c.reset}`);
|
|
185
|
+
console.log(clean.split('\n').slice(0, 20).join('\n'));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ── Pass/fail judgment ───────────────────────────────────────────────────────
|
|
189
|
+
|
|
190
|
+
const remainingFixable = alerts.filter(a => FIXABLE_ALERTS.includes(a.name));
|
|
191
|
+
|
|
192
|
+
console.log(`\n${c.gray}${'─'.repeat(50)}${c.reset}`);
|
|
193
|
+
|
|
194
|
+
if (remainingFixable.length === 0 && alerts.length > 0) {
|
|
195
|
+
console.log(`${c.green}${c.bold}PASS${c.reset} — no fixable alerts present.`);
|
|
196
|
+
if (score !== null && score < 85) {
|
|
197
|
+
console.log(`${c.gray}Score ${score} is below 85 but remaining alerts are all expected.${c.reset}`);
|
|
198
|
+
console.log(`${c.gray}The gap is 'recentlyPublished' (auto-expires ~30 days) + download count (grows organically).${c.reset}`);
|
|
199
|
+
}
|
|
200
|
+
process.exit(0);
|
|
201
|
+
} else if (remainingFixable.length === 0) {
|
|
202
|
+
console.log(`${c.yellow}${c.bold}INCONCLUSIVE${c.reset} — no alerts parsed. Run with a published version or check auth.`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
} else {
|
|
205
|
+
console.log(`${c.red}${c.bold}FAIL${c.reset} — ${remainingFixable.length} fixable alert(s) still present:`);
|
|
206
|
+
for (const a of remainingFixable) {
|
|
207
|
+
console.log(` ${c.red}✗${c.reset} ${a.name} [${a.severity}]`);
|
|
208
|
+
}
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|