shieldcortex 2.4.23 → 2.4.24

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.
Files changed (97) hide show
  1. package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
  2. package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
  3. package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
  4. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
  5. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
  6. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +1 -1
  13. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +2 -2
  14. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  15. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  17. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
  21. package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +3 -3
  22. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  23. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +3 -3
  24. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
  25. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
  26. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  27. package/dashboard/.next/standalone/dashboard/.next/server/app/page/react-loadable-manifest.json +1 -1
  28. package/dashboard/.next/standalone/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  29. package/dashboard/.next/standalone/dashboard/.next/server/chunks/ssr/dashboard_25b1b286._.js +1 -1
  30. package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +1 -1
  31. package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
  32. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
  33. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
  34. package/dashboard/.next/standalone/dashboard/.next/static/chunks/{0327af3bf4830eac.js → 0ba8a0e679bf5c40.js} +1 -1
  35. package/dashboard/.next/standalone/dashboard/.next/static/chunks/17348ec48b354115.css +3 -0
  36. package/dashboard/.next/standalone/dashboard/.next/static/chunks/{21c4fc7176fbe8ee.js → caa049bd46f24dd8.js} +1 -1
  37. package/dashboard/.next/standalone/dashboard/.next/static/chunks/{511275d9224bafb2.js → cb7d5bff58e77e2c.js} +1 -1
  38. package/dist/api/visualization-server.d.ts.map +1 -1
  39. package/dist/api/visualization-server.js +54 -0
  40. package/dist/api/visualization-server.js.map +1 -1
  41. package/dist/cloud/sync.d.ts.map +1 -1
  42. package/dist/cloud/sync.js +7 -3
  43. package/dist/cloud/sync.js.map +1 -1
  44. package/dist/defence/index.d.ts +2 -0
  45. package/dist/defence/index.d.ts.map +1 -1
  46. package/dist/defence/index.js +2 -0
  47. package/dist/defence/index.js.map +1 -1
  48. package/dist/defence/skill-scanner/__tests__/skill-scanner.test.d.ts +12 -0
  49. package/dist/defence/skill-scanner/__tests__/skill-scanner.test.d.ts.map +1 -0
  50. package/dist/defence/skill-scanner/__tests__/skill-scanner.test.js +471 -0
  51. package/dist/defence/skill-scanner/__tests__/skill-scanner.test.js.map +1 -0
  52. package/dist/defence/skill-scanner/discover.d.ts +16 -0
  53. package/dist/defence/skill-scanner/discover.d.ts.map +1 -0
  54. package/dist/defence/skill-scanner/discover.js +85 -0
  55. package/dist/defence/skill-scanner/discover.js.map +1 -0
  56. package/dist/defence/skill-scanner/index.d.ts +20 -0
  57. package/dist/defence/skill-scanner/index.d.ts.map +1 -0
  58. package/dist/defence/skill-scanner/index.js +17 -0
  59. package/dist/defence/skill-scanner/index.js.map +1 -0
  60. package/dist/defence/skill-scanner/parser.d.ts +45 -0
  61. package/dist/defence/skill-scanner/parser.d.ts.map +1 -0
  62. package/dist/defence/skill-scanner/parser.js +373 -0
  63. package/dist/defence/skill-scanner/parser.js.map +1 -0
  64. package/dist/defence/skill-scanner/patterns.d.ts +37 -0
  65. package/dist/defence/skill-scanner/patterns.d.ts.map +1 -0
  66. package/dist/defence/skill-scanner/patterns.js +240 -0
  67. package/dist/defence/skill-scanner/patterns.js.map +1 -0
  68. package/dist/defence/skill-scanner/scan-skill.d.ts +75 -0
  69. package/dist/defence/skill-scanner/scan-skill.d.ts.map +1 -0
  70. package/dist/defence/skill-scanner/scan-skill.js +397 -0
  71. package/dist/defence/skill-scanner/scan-skill.js.map +1 -0
  72. package/dist/embeddings/generator.d.ts +5 -0
  73. package/dist/embeddings/generator.d.ts.map +1 -1
  74. package/dist/embeddings/generator.js +35 -5
  75. package/dist/embeddings/generator.js.map +1 -1
  76. package/dist/embeddings/index.d.ts +1 -1
  77. package/dist/embeddings/index.d.ts.map +1 -1
  78. package/dist/embeddings/index.js +1 -1
  79. package/dist/embeddings/index.js.map +1 -1
  80. package/dist/index.js +88 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/memory/contradiction.d.ts.map +1 -1
  83. package/dist/memory/contradiction.js +8 -2
  84. package/dist/memory/contradiction.js.map +1 -1
  85. package/dist/memory/store.d.ts.map +1 -1
  86. package/dist/memory/store.js +27 -0
  87. package/dist/memory/store.js.map +1 -1
  88. package/dist/server.d.ts.map +1 -1
  89. package/dist/server.js +35 -0
  90. package/dist/server.js.map +1 -1
  91. package/hooks/openclaw/cortex-memory/handler.js +75 -0
  92. package/package.json +1 -1
  93. package/scripts/session-start-hook.mjs +67 -1
  94. package/dashboard/.next/standalone/dashboard/.next/static/chunks/8e559e67e3d8782b.css +0 -3
  95. /package/dashboard/.next/standalone/dashboard/.next/static/{Ykr04kZxo_ae93TlaBU55 → G16ww7KrkUyZJT_fvjFk6}/_buildManifest.js +0 -0
  96. /package/dashboard/.next/standalone/dashboard/.next/static/{Ykr04kZxo_ae93TlaBU55 → G16ww7KrkUyZJT_fvjFk6}/_clientMiddlewareManifest.json +0 -0
  97. /package/dashboard/.next/standalone/dashboard/.next/static/{Ykr04kZxo_ae93TlaBU55 → G16ww7KrkUyZJT_fvjFk6}/_ssgManifest.js +0 -0
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Skill Scanner — Core Module
3
+ *
4
+ * Scans agent instruction files (skill definitions, tool configs, rules files)
5
+ * for threats using the full ShieldCortex defence pipeline combined with
6
+ * skill-specific pattern detection.
7
+ *
8
+ * Public API:
9
+ * - scanSkill(filePath, options?) — read from disc and scan
10
+ * - scanSkillContent(content, options?) — scan raw content directly
11
+ *
12
+ * Never throws — returns safe defaults on errors.
13
+ */
14
+ import { analyzeFirewall } from '../firewall/index.js';
15
+ import { classifySensitivity } from '../sensitivity/index.js';
16
+ import { DEFAULT_DEFENCE_CONFIG } from '../types.js';
17
+ import { detectSkillThreats, detectCodeThreats } from './patterns.js';
18
+ import { readSkillFile, parseSkillFile } from './parser.js';
19
+ // ── Threat Description Map ───────────────────────────────────────────────────
20
+ const THREAT_DESCRIPTIONS = {
21
+ // Skill-specific patterns
22
+ tool_injection: 'Instructions to execute shell commands or write files',
23
+ scope_escalation: 'Attempts to access files outside the project directory',
24
+ data_exfiltration: 'Instructions to send data to external services',
25
+ persistence: 'Attempts to modify agent configuration files',
26
+ supply_chain: 'Instructions to install packages or modify dependencies',
27
+ agent_manipulation: 'Instructions to disable safety checks or permissions',
28
+ stealth_instruction: 'Hidden instructions using formatting tricks',
29
+ // Code patterns
30
+ dangerous_require: 'Imports dangerous Node.js modules (child_process, net)',
31
+ dangerous_calls: 'Uses dangerous function calls (eval, exec, spawn)',
32
+ filesystem_access: 'Accesses sensitive filesystem paths',
33
+ network_access: 'Makes network requests to external services',
34
+ // Firewall threat indicators
35
+ instruction_injection: 'Prompt injection detected by firewall',
36
+ privilege_escalation: 'Privilege escalation patterns detected',
37
+ encoding_obfuscation: 'Obfuscated or encoded content detected',
38
+ credential_leak: 'Potential credential or secret exposure',
39
+ external_url: 'References to external URLs detected',
40
+ fragmented_payload: 'Fragmented payload assembly detected',
41
+ pipeline_error: 'Defence pipeline encountered an error',
42
+ };
43
+ // ── Severity Helpers ─────────────────────────────────────────────────────────
44
+ /** Severity ranking for comparison. */
45
+ const SEVERITY_RANK = {
46
+ low: 1,
47
+ medium: 2,
48
+ high: 3,
49
+ critical: 4,
50
+ };
51
+ /**
52
+ * Derive severity for a firewall threat indicator based on the firewall result.
53
+ */
54
+ function deriveFirewallSeverity(firewallResult, indicator) {
55
+ if (firewallResult === 'BLOCK') {
56
+ if (indicator === 'instruction_injection' || indicator === 'credential_leak') {
57
+ return 'critical';
58
+ }
59
+ return 'high';
60
+ }
61
+ if (firewallResult === 'QUARANTINE') {
62
+ if (indicator === 'instruction_injection') {
63
+ return 'high';
64
+ }
65
+ return 'medium';
66
+ }
67
+ return 'low';
68
+ }
69
+ /**
70
+ * Derive severity for a skill/code threat based on its confidence score.
71
+ */
72
+ function deriveSkillThreatSeverity(confidence) {
73
+ if (confidence >= 0.85)
74
+ return 'high';
75
+ if (confidence >= 0.6)
76
+ return 'medium';
77
+ return 'low';
78
+ }
79
+ /**
80
+ * Return the highest severity from an array of findings, or 'safe' if empty.
81
+ */
82
+ function highestSeverity(findings) {
83
+ if (findings.length === 0)
84
+ return 'safe';
85
+ let maxRank = 0;
86
+ let maxSeverity = 'low';
87
+ for (const finding of findings) {
88
+ const rank = SEVERITY_RANK[finding.severity] ?? 0;
89
+ if (rank > maxRank) {
90
+ maxRank = rank;
91
+ maxSeverity = finding.severity;
92
+ }
93
+ }
94
+ return maxSeverity;
95
+ }
96
+ /**
97
+ * Look up a human-readable description for a threat pattern name.
98
+ */
99
+ function descriptionFor(pattern) {
100
+ return THREAT_DESCRIPTIONS[pattern] ?? `Threat pattern detected: ${pattern}`;
101
+ }
102
+ /**
103
+ * Truncate text to a maximum length, appending an ellipsis if needed.
104
+ */
105
+ function truncate(text, maxLength) {
106
+ if (text.length <= maxLength)
107
+ return text;
108
+ return text.slice(0, maxLength - 1) + '\u2026';
109
+ }
110
+ // ── Build Config ─────────────────────────────────────────────────────────────
111
+ /**
112
+ * Build a DefenceConfig with the mode optionally overridden.
113
+ */
114
+ function buildConfig(mode) {
115
+ if (!mode || mode === DEFAULT_DEFENCE_CONFIG.mode) {
116
+ return DEFAULT_DEFENCE_CONFIG;
117
+ }
118
+ return { ...DEFAULT_DEFENCE_CONFIG, mode };
119
+ }
120
+ // ── Core Scan Logic ──────────────────────────────────────────────────────────
121
+ /**
122
+ * Internal scan implementation that operates on a ParsedSkill.
123
+ */
124
+ function scanParsed(parsed, options) {
125
+ const startTime = Date.now();
126
+ const includeContent = options?.includeContent ?? false;
127
+ const config = buildConfig(options?.mode);
128
+ const findings = [];
129
+ // 1. Firewall analysis — treat skill files as untrusted (trust 0.3)
130
+ const source = { type: 'file', identifier: parsed.name };
131
+ const firewall = analyzeFirewall(parsed.content, parsed.name, source, 0.3, config);
132
+ // 2. Sensitivity classification
133
+ const sensitivity = classifySensitivity(parsed.content, parsed.name);
134
+ // 3. Collect findings from firewall threat indicators
135
+ if (firewall.result === 'BLOCK' || firewall.result === 'QUARANTINE') {
136
+ for (const indicator of firewall.threatIndicators) {
137
+ const severity = deriveFirewallSeverity(firewall.result, indicator);
138
+ const finding = {
139
+ pattern: indicator,
140
+ severity,
141
+ description: descriptionFor(indicator),
142
+ };
143
+ if (includeContent && firewall.blockedPatterns.length > 0) {
144
+ finding.matchedText = truncate(firewall.blockedPatterns[0], 80);
145
+ }
146
+ findings.push(finding);
147
+ }
148
+ // If firewall blocked/quarantined but reported no specific indicators
149
+ if (firewall.threatIndicators.length === 0) {
150
+ findings.push({
151
+ pattern: 'instruction_injection',
152
+ severity: firewall.result === 'BLOCK' ? 'high' : 'medium',
153
+ description: firewall.reason,
154
+ });
155
+ }
156
+ }
157
+ // 4. Skill-specific threat detection
158
+ const skillResult = detectSkillThreats(parsed.content);
159
+ addSkillFindings(findings, skillResult, parsed.content, includeContent);
160
+ // 5. Code threat detection for code-bearing formats
161
+ if (parsed.format === 'hook-js' || parsed.format === 'continue-json') {
162
+ const codeResult = detectCodeThreats(parsed.content);
163
+ addSkillFindings(findings, codeResult, parsed.content, includeContent);
164
+ }
165
+ // 6. Scan metadata values if present
166
+ if (parsed.metadata && Object.keys(parsed.metadata).length > 0) {
167
+ try {
168
+ const metadataStr = JSON.stringify(parsed.metadata);
169
+ const metaResult = detectSkillThreats(metadataStr);
170
+ addSkillFindings(findings, metaResult, metadataStr, includeContent);
171
+ }
172
+ catch {
173
+ // JSON.stringify failed — skip metadata scan
174
+ }
175
+ }
176
+ // 7. Flag RESTRICTED sensitivity content
177
+ if (sensitivity.level === 'RESTRICTED') {
178
+ findings.push({
179
+ pattern: 'credential_leak',
180
+ severity: 'high',
181
+ description: `RESTRICTED content detected: ${sensitivity.detectedPatterns.join(', ')}`,
182
+ });
183
+ }
184
+ // 8. Deduplicate findings by pattern name (keep highest severity)
185
+ const deduped = deduplicateFindings(findings);
186
+ // 9. Derive overall risk level and safety
187
+ const riskLevel = highestSeverity(deduped);
188
+ const safe = !deduped.some((f) => f.severity === 'high' || f.severity === 'critical');
189
+ // 10. Generate summary
190
+ const summary = generateSummary(parsed.name, deduped, riskLevel, safe);
191
+ return {
192
+ safe,
193
+ skillName: parsed.name,
194
+ format: parsed.format,
195
+ findings: deduped,
196
+ riskLevel,
197
+ summary,
198
+ scanDurationMs: Date.now() - startTime,
199
+ firewall,
200
+ sensitivity,
201
+ };
202
+ }
203
+ /**
204
+ * Add findings from a SkillThreatResult into the findings array.
205
+ */
206
+ function addSkillFindings(findings, result, content, includeContent) {
207
+ if (!result.detected)
208
+ return;
209
+ const severity = deriveSkillThreatSeverity(result.confidence);
210
+ for (const threat of result.threats) {
211
+ const finding = {
212
+ pattern: threat,
213
+ severity,
214
+ description: descriptionFor(threat),
215
+ };
216
+ if (includeContent) {
217
+ // Try to find the first relevant line for context
218
+ const snippet = findSnippetForThreat(content, threat);
219
+ if (snippet) {
220
+ finding.matchedText = truncate(snippet, 80);
221
+ }
222
+ }
223
+ findings.push(finding);
224
+ }
225
+ }
226
+ /**
227
+ * Attempt to find a content snippet relevant to a named threat.
228
+ * Returns the first line containing a keyword associated with the threat,
229
+ * or null if nothing obvious is found.
230
+ */
231
+ function findSnippetForThreat(content, threat) {
232
+ const keywords = {
233
+ tool_injection: ['execute', 'run', 'bash', 'shell', 'command', 'script'],
234
+ scope_escalation: ['.ssh', '.aws', '.env', '/etc/', 'process.env'],
235
+ data_exfiltration: ['send', 'post', 'curl', 'upload', 'webhook', 'fetch'],
236
+ persistence: ['.claude', '.cursorrules', 'crontab', 'hook', 'bashrc'],
237
+ supply_chain: ['npm install', 'pip install', 'package.json', 'dependency'],
238
+ agent_manipulation: ['ignore', 'bypass', 'disable', 'override', 'skip'],
239
+ stealth_instruction: ['<!--', '---'],
240
+ dangerous_require: ['child_process', 'require', 'import'],
241
+ dangerous_calls: ['eval', 'exec', 'spawn', 'Function'],
242
+ filesystem_access: ['readFile', 'writeFile', '.env', '.key'],
243
+ network_access: ['http', 'fetch', 'WebSocket', 'listen'],
244
+ };
245
+ const kws = keywords[threat];
246
+ if (!kws)
247
+ return null;
248
+ const lines = content.split('\n');
249
+ for (const line of lines) {
250
+ const trimmed = line.trim();
251
+ if (trimmed.length === 0)
252
+ continue;
253
+ for (const kw of kws) {
254
+ if (trimmed.toLowerCase().includes(kw.toLowerCase())) {
255
+ return trimmed;
256
+ }
257
+ }
258
+ }
259
+ return null;
260
+ }
261
+ /**
262
+ * Deduplicate findings by pattern name, keeping the highest severity instance.
263
+ */
264
+ function deduplicateFindings(findings) {
265
+ const byPattern = new Map();
266
+ for (const finding of findings) {
267
+ const existing = byPattern.get(finding.pattern);
268
+ if (!existing) {
269
+ byPattern.set(finding.pattern, finding);
270
+ }
271
+ else {
272
+ const existingRank = SEVERITY_RANK[existing.severity] ?? 0;
273
+ const newRank = SEVERITY_RANK[finding.severity] ?? 0;
274
+ if (newRank > existingRank) {
275
+ byPattern.set(finding.pattern, finding);
276
+ }
277
+ }
278
+ }
279
+ return [...byPattern.values()];
280
+ }
281
+ /**
282
+ * Generate a one-line human-readable summary of the scan result.
283
+ */
284
+ function generateSummary(name, findings, riskLevel, safe) {
285
+ if (findings.length === 0) {
286
+ return `${name}: no threats detected`;
287
+ }
288
+ const severityCounts = { critical: 0, high: 0, medium: 0, low: 0 };
289
+ for (const f of findings) {
290
+ severityCounts[f.severity]++;
291
+ }
292
+ const parts = [];
293
+ if (severityCounts.critical > 0)
294
+ parts.push(`${severityCounts.critical} critical`);
295
+ if (severityCounts.high > 0)
296
+ parts.push(`${severityCounts.high} high`);
297
+ if (severityCounts.medium > 0)
298
+ parts.push(`${severityCounts.medium} medium`);
299
+ if (severityCounts.low > 0)
300
+ parts.push(`${severityCounts.low} low`);
301
+ const riskLabel = safe ? 'review recommended' : 'unsafe';
302
+ return `${name}: ${findings.length} finding(s) (${parts.join(', ')}) — ${riskLabel}`;
303
+ }
304
+ // ── Public API ───────────────────────────────────────────────────────────────
305
+ /**
306
+ * Scan a skill file from disc for threats.
307
+ *
308
+ * Reads the file, auto-detects its format, and runs the full defence pipeline
309
+ * plus skill-specific pattern detection.
310
+ *
311
+ * Never throws — returns safe defaults if the file cannot be read.
312
+ */
313
+ export function scanSkill(filePath, options) {
314
+ try {
315
+ const parsed = readSkillFile(filePath);
316
+ const result = scanParsed(parsed, options);
317
+ return result;
318
+ }
319
+ catch {
320
+ // Return a safe default on any unexpected error
321
+ const startTime = Date.now();
322
+ return {
323
+ safe: true,
324
+ skillName: filePath,
325
+ format: 'unknown',
326
+ findings: [],
327
+ riskLevel: 'safe',
328
+ summary: `${filePath}: scan failed — could not read file`,
329
+ scanDurationMs: Date.now() - startTime,
330
+ firewall: {
331
+ result: 'ALLOW',
332
+ reason: 'Scan failed — file unreadable',
333
+ threatIndicators: [],
334
+ anomalyScore: 0,
335
+ blockedPatterns: [],
336
+ },
337
+ sensitivity: {
338
+ level: 'PUBLIC',
339
+ confidence: 0,
340
+ detectedPatterns: [],
341
+ redactionRequired: false,
342
+ },
343
+ };
344
+ }
345
+ }
346
+ /**
347
+ * Scan raw skill content for threats without reading from disc.
348
+ *
349
+ * Useful when the content is already in memory (e.g. received via API,
350
+ * read from a database, or extracted from a larger document).
351
+ *
352
+ * @param content Raw file content to scan
353
+ * @param options Scan options (mode, includeContent)
354
+ * @param format Optional format hint — auto-detected as 'unknown' if omitted
355
+ * @param name Optional skill name — defaults to 'inline'
356
+ */
357
+ export function scanSkillContent(content, options, format, name) {
358
+ try {
359
+ const parsed = parseSkillFile(content, format);
360
+ // Override name if provided
361
+ if (name) {
362
+ parsed.name = name;
363
+ }
364
+ else if (parsed.name === 'unknown') {
365
+ parsed.name = 'inline';
366
+ }
367
+ return scanParsed(parsed, options);
368
+ }
369
+ catch {
370
+ // Return a safe default on any unexpected error
371
+ const effectiveName = name ?? 'inline';
372
+ const startTime = Date.now();
373
+ return {
374
+ safe: true,
375
+ skillName: effectiveName,
376
+ format: format ?? 'unknown',
377
+ findings: [],
378
+ riskLevel: 'safe',
379
+ summary: `${effectiveName}: scan failed — unexpected error`,
380
+ scanDurationMs: Date.now() - startTime,
381
+ firewall: {
382
+ result: 'ALLOW',
383
+ reason: 'Scan failed — unexpected error',
384
+ threatIndicators: [],
385
+ anomalyScore: 0,
386
+ blockedPatterns: [],
387
+ },
388
+ sensitivity: {
389
+ level: 'PUBLIC',
390
+ confidence: 0,
391
+ detectedPatterns: [],
392
+ redactionRequired: false,
393
+ },
394
+ };
395
+ }
396
+ }
397
+ //# sourceMappingURL=scan-skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-skill.js","sourceRoot":"","sources":["../../../src/defence/skill-scanner/scan-skill.ts"],"names":[],"mappings":"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"}
@@ -17,4 +17,9 @@ export declare function isModelLoaded(): boolean;
17
17
  * Preload the model (call during startup if desired)
18
18
  */
19
19
  export declare function preloadModel(): Promise<void>;
20
+ /**
21
+ * Dispose the embedding model and release ONNX session resources.
22
+ * Call during shutdown to prevent SIGABRT from orphaned ONNX thread pools.
23
+ */
24
+ export declare function disposeModel(): Promise<void>;
20
25
  //# sourceMappingURL=generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/embeddings/generator.ts"],"names":[],"mappings":"AAoCA;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAmBzE;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAElD"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/embeddings/generator.ts"],"names":[],"mappings":"AAoCA;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA4B3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAmBzE;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAElD;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAWlD"}
@@ -41,11 +41,24 @@ export async function generateEmbedding(text) {
41
41
  pooling: 'mean',
42
42
  normalize: true,
43
43
  });
44
- // Handle both Tensor objects (new @huggingface/transformers) and plain objects
45
- const data = typeof output.tolist === 'function'
46
- ? new Float32Array(output.tolist().flat(Infinity))
47
- : new Float32Array(output.data);
48
- return data;
44
+ try {
45
+ // Prefer .data (direct typed array) over .tolist() to avoid intermediate allocations
46
+ if (output.data && output.data.length > 0) {
47
+ return new Float32Array(output.data);
48
+ }
49
+ // Fallback for older/different Tensor implementations
50
+ if (typeof output.tolist === 'function') {
51
+ const list = output.tolist().flat(Infinity);
52
+ return new Float32Array(list);
53
+ }
54
+ throw new Error('Tensor has no .data or .tolist() — cannot extract embedding');
55
+ }
56
+ finally {
57
+ // CRITICAL: Release ONNX native memory backing this tensor
58
+ if (output && typeof output.dispose === 'function') {
59
+ output.dispose();
60
+ }
61
+ }
49
62
  }
50
63
  /**
51
64
  * Calculate cosine similarity between two embeddings
@@ -80,4 +93,21 @@ export function isModelLoaded() {
80
93
  export async function preloadModel() {
81
94
  await getEmbeddingPipeline();
82
95
  }
96
+ /**
97
+ * Dispose the embedding model and release ONNX session resources.
98
+ * Call during shutdown to prevent SIGABRT from orphaned ONNX thread pools.
99
+ */
100
+ export async function disposeModel() {
101
+ if (embeddingPipeline) {
102
+ try {
103
+ await embeddingPipeline.dispose();
104
+ }
105
+ catch {
106
+ // Best-effort disposal — don't block shutdown
107
+ }
108
+ embeddingPipeline = null;
109
+ }
110
+ loadPromise = null;
111
+ isLoading = false;
112
+ }
83
113
  //# sourceMappingURL=generator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/embeddings/generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/embeddings/generator.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export { generateEmbedding, cosineSimilarity, isModelLoaded, preloadModel } from './generator.js';
1
+ export { generateEmbedding, cosineSimilarity, isModelLoaded, preloadModel, disposeModel } from './generator.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,2 +1,2 @@
1
- export { generateEmbedding, cosineSimilarity, isModelLoaded, preloadModel } from './generator.js';
1
+ export { generateEmbedding, cosineSimilarity, isModelLoaded, preloadModel, disposeModel } from './generator.js';
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -41,6 +41,8 @@ import { setupClaudeMd } from './setup/claude-md.js';
41
41
  import { handleHookCommand } from './setup/hooks.js';
42
42
  import { handleOpenClawCommand } from './setup/openclaw.js';
43
43
  import { createRequire } from 'module';
44
+ import { disposeModel } from './embeddings/index.js';
45
+ import { stopDefaultWorker } from './worker/brain-worker.js';
44
46
  const require = createRequire(import.meta.url);
45
47
  const pkg = require('../package.json');
46
48
  // Get the directory of this file for relative paths
@@ -77,11 +79,15 @@ async function startMcpServer(dbPath) {
77
79
  const transport = new StdioServerTransport();
78
80
  // Register signal handlers BEFORE connect so cleanup runs even if connect() throws
79
81
  process.on('SIGINT', async () => {
82
+ stopDefaultWorker();
80
83
  await server.close();
84
+ await disposeModel();
81
85
  process.exit(0);
82
86
  });
83
87
  process.on('SIGTERM', async () => {
88
+ stopDefaultWorker();
84
89
  await server.close();
90
+ await disposeModel();
85
91
  process.exit(0);
86
92
  });
87
93
  // Connect via stdio transport
@@ -294,6 +300,88 @@ async function main() {
294
300
  }
295
301
  return;
296
302
  }
303
+ // Handle "scan-skill" subcommand — scan a single skill/instruction file
304
+ if (process.argv[2] === 'scan-skill') {
305
+ const filePath = process.argv[3];
306
+ if (!filePath) {
307
+ console.error('Usage: npx shieldcortex scan-skill <path>');
308
+ console.error(' Scans an agent instruction file for threats.');
309
+ console.error(' Supports: SKILL.md, HOOK.md, handler.js, .cursorrules,');
310
+ console.error(' .windsurfrules, .clinerules, CLAUDE.md, copilot-instructions.md,');
311
+ console.error(' .aider.conf.yml, .continue/config.json');
312
+ process.exit(1);
313
+ }
314
+ const { scanSkill } = await import('./defence/skill-scanner/index.js');
315
+ const result = scanSkill(filePath);
316
+ // Coloured output
317
+ const severityColor = {
318
+ critical: '\x1b[31m', // red
319
+ high: '\x1b[91m', // bright red
320
+ medium: '\x1b[33m', // yellow
321
+ low: '\x1b[36m', // cyan
322
+ };
323
+ const reset = '\x1b[0m';
324
+ const bold = '\x1b[1m';
325
+ console.log(`\n${bold}Skill Scanner Report${reset}`);
326
+ console.log(`${'─'.repeat(50)}`);
327
+ console.log(` Skill: ${result.skillName}`);
328
+ console.log(` Format: ${result.format}`);
329
+ console.log(` Risk: ${result.safe ? '\x1b[32mSAFE\x1b[0m' : `${severityColor[result.riskLevel] ?? ''}${result.riskLevel.toUpperCase()}${reset}`}`);
330
+ console.log(` Time: ${result.scanDurationMs}ms`);
331
+ if (result.findings.length > 0) {
332
+ console.log(`\n${bold}Findings (${result.findings.length}):${reset}`);
333
+ for (const f of result.findings) {
334
+ const color = severityColor[f.severity] ?? '';
335
+ console.log(` ${color}[${f.severity.toUpperCase()}]${reset} ${f.pattern}`);
336
+ console.log(` ${f.description}`);
337
+ if (f.matchedText) {
338
+ console.log(` Match: "${f.matchedText}"`);
339
+ }
340
+ }
341
+ }
342
+ else {
343
+ console.log(`\n \x1b[32mNo threats detected.\x1b[0m`);
344
+ }
345
+ console.log(`\n${result.summary}\n`);
346
+ process.exit(result.safe ? 0 : 1);
347
+ }
348
+ // Handle "scan-skills" subcommand — scan all installed skills/hooks
349
+ if (process.argv[2] === 'scan-skills') {
350
+ const { scanSkill, discoverSkillFiles } = await import('./defence/skill-scanner/index.js');
351
+ const customDir = process.argv.indexOf('--dir') !== -1
352
+ ? process.argv[process.argv.indexOf('--dir') + 1]
353
+ : undefined;
354
+ const filesToScan = discoverSkillFiles(customDir);
355
+ if (filesToScan.length === 0) {
356
+ console.log('\nNo agent instruction files found to scan.');
357
+ console.log('Checked: Claude Code skills, OpenClaw hooks, .cursorrules, CLAUDE.md, and more.');
358
+ console.log('Use --dir <path> to scan a custom directory.\n');
359
+ process.exit(0);
360
+ }
361
+ const bold = '\x1b[1m';
362
+ const reset = '\x1b[0m';
363
+ console.log(`\n${bold}Scanning ${filesToScan.length} file(s)...${reset}\n`);
364
+ let threatCount = 0;
365
+ const results = [];
366
+ for (const fp of filesToScan) {
367
+ const result = scanSkill(fp);
368
+ results.push({ path: fp, safe: result.safe, riskLevel: result.riskLevel, summary: result.summary });
369
+ if (!result.safe)
370
+ threatCount++;
371
+ const icon = result.safe ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
372
+ const risk = result.safe ? '\x1b[32msafe\x1b[0m' : `\x1b[31m${result.riskLevel}\x1b[0m`;
373
+ console.log(` ${icon} ${risk.padEnd(20)} ${fp}`);
374
+ if (!result.safe) {
375
+ for (const f of result.findings) {
376
+ if (f.severity === 'high' || f.severity === 'critical') {
377
+ console.log(` \x1b[31m[${f.severity}]\x1b[0m ${f.description}`);
378
+ }
379
+ }
380
+ }
381
+ }
382
+ console.log(`\n${bold}Summary:${reset} ${filesToScan.length} scanned, ${threatCount} with threats\n`);
383
+ process.exit(threatCount > 0 ? 1 : 0);
384
+ }
297
385
  const { dbPath, mode } = parseArgs();
298
386
  let dashboardProcess = null;
299
387
  if (mode === 'api') {