neuralos 3.9.4 → 3.9.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.
Files changed (68) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +48 -347
  3. package/cli.js +78 -0
  4. package/engine.js +124 -0
  5. package/index.js +160 -0
  6. package/package.json +37 -43
  7. package/test.js +20 -0
  8. package/tools.js +47 -0
  9. package/bin/gybackend.cjs +0 -398530
  10. package/plugins/agentspan-bridge/agentspan-bridge.extreme.spec.ts +0 -328
  11. package/plugins/agentspan-bridge/agentspan-bridge.phase2.extreme.spec.ts +0 -282
  12. package/plugins/agentspan-bridge/conductorClient.mjs +0 -182
  13. package/plugins/agentspan-bridge/index.mjs +0 -389
  14. package/plugins/agentspan-bridge/playbookToWorkflowDef.mjs +0 -209
  15. package/plugins/agentspan-bridge/plugin.json +0 -27
  16. package/plugins/fraudops/index.d.mts +0 -9
  17. package/plugins/fraudops/index.mjs +0 -185
  18. package/plugins/fraudops/plugin.json +0 -10
  19. package/plugins/iam-connector/index.d.mts +0 -10
  20. package/plugins/iam-connector/index.mjs +0 -229
  21. package/plugins/iam-connector/plugin.json +0 -10
  22. package/plugins/mitmproxy-bridge/index.d.mts +0 -20
  23. package/plugins/mitmproxy-bridge/index.mjs +0 -256
  24. package/plugins/mitmproxy-bridge/plugin.json +0 -17
  25. package/plugins/monid-bridge/index.mjs +0 -228
  26. package/plugins/monid-bridge/monid-bridge.extreme.spec.mjs +0 -75
  27. package/plugins/monid-bridge/plugin.json +0 -15
  28. package/plugins/netdata-rterm/index.mjs +0 -229
  29. package/plugins/netdata-rterm/netdata-rterm.extreme.spec.ts +0 -252
  30. package/plugins/netdata-rterm/plugin.json +0 -22
  31. package/plugins/netexec-bridge/index.d.mts +0 -28
  32. package/plugins/netexec-bridge/index.mjs +0 -325
  33. package/plugins/netexec-bridge/plugin.json +0 -20
  34. package/plugins/neuralos/index.mjs +0 -137
  35. package/plugins/neuralos/neuralos.extreme.spec.mts +0 -386
  36. package/plugins/neuralos/neuralosEngine.mjs +0 -369
  37. package/plugins/neuralos/plugin.json +0 -15
  38. package/plugins/numbat-bridge/index.mjs +0 -229
  39. package/plugins/numbat-bridge/numbat-bridge.extreme.spec.mjs +0 -148
  40. package/plugins/numbat-bridge/plugin.json +0 -21
  41. package/plugins/patch-manager/index.d.mts +0 -10
  42. package/plugins/patch-manager/index.mjs +0 -288
  43. package/plugins/patch-manager/plugin.json +0 -10
  44. package/plugins/promptfoo-redteam/index.d.mts +0 -15
  45. package/plugins/promptfoo-redteam/index.mjs +0 -277
  46. package/plugins/promptfoo-redteam/plugin.json +0 -16
  47. package/plugins/reactorpro-bridge/index.mjs +0 -499
  48. package/plugins/reactorpro-bridge/plugin.json +0 -26
  49. package/plugins/reactorpro-bridge/reactorpro-bridge.extreme.spec.mts +0 -409
  50. package/plugins/reactorpro-bridge/reactorproAgent.mjs +0 -399
  51. package/plugins/request-router/index.d.mts +0 -9
  52. package/plugins/request-router/index.mjs +0 -239
  53. package/plugins/request-router/plugin.json +0 -10
  54. package/plugins/sample-k8s-slo/index.ts +0 -58
  55. package/plugins/sample-k8s-slo/plugin.json +0 -11
  56. package/plugins/sop-assistant/index.d.mts +0 -9
  57. package/plugins/sop-assistant/index.mjs +0 -325
  58. package/plugins/sop-assistant/plugin.json +0 -10
  59. package/plugins/synapse-bridge/index.mjs +0 -484
  60. package/plugins/synapse-bridge/plugin.json +0 -29
  61. package/plugins/synapse-bridge/synapse-bridge.extreme.spec.mjs +0 -204
  62. package/plugins/synapse-bridge/synapseAgent.extreme.spec.mjs +0 -236
  63. package/plugins/synapse-bridge/synapseAgent.mjs +0 -254
  64. package/plugins/web-intel/index.mjs +0 -381
  65. package/plugins/web-intel/plugin.json +0 -28
  66. package/plugins/web-intel/sidecar.mjs +0 -134
  67. package/plugins/web-intel/web-intel.extreme.spec.ts +0 -263
  68. package/plugins/web-intel/wigoloClient.mjs +0 -145
@@ -1,148 +0,0 @@
1
- import { register, resolveConfig, normalizeRecord, parseNdjson, buildDeployCommand } from './index.mjs'
2
-
3
- const cases = []
4
- function test(n, r) { cases.push({ name: n, run: r }) }
5
- function assert(c, m) { if (!c) throw new Error(m ?? 'assertion failed') }
6
- function eq(a, b, m) { if (a !== b) throw new Error(`${m ?? 'eq'}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}`) }
7
-
8
- function mkCtx(settings = {}, extra = {}) {
9
- const tools = new Map(); const triggers = []; const panels = []; const logs = []; const emitted = []
10
- const ctx = {
11
- settings: { numbat: settings },
12
- registerTool: (t) => tools.set(t.name, t),
13
- registerTrigger: (t) => triggers.push(t),
14
- registerPanel: (p) => panels.push(p),
15
- log: (l) => logs.push(l),
16
- emitEvent: (e) => emitted.push(e),
17
- ...extra,
18
- }
19
- return { tools, triggers, panels, logs, emitted, ctx }
20
- }
21
-
22
- const CFG = { minSeverity: 'low' }
23
-
24
- // ─── config ─────────────────────────────────────────────────────────────────
25
-
26
- test('resolveConfig defaults', () => {
27
- const c = resolveConfig({ settings: {} }, {})
28
- eq(c.binaryPath, 'numbat', 'default binary')
29
- eq(c.minSeverity, 'low', 'default minSeverity')
30
- eq(c.enabled, true, 'enabled default')
31
- assert(c.recordsPath.includes('.numbat'), 'records path under ~/.numbat')
32
- })
33
-
34
- // ─── normalizeRecord ────────────────────────────────────────────────────────
35
-
36
- test('normalizeRecord maps a finding record', () => {
37
- const rec = { record_type: 'finding', severity: 'high', rule_id: 'cloud-metadata', agent: 'codex', host: 'web-01', summary: 'metadata access' }
38
- const n = normalizeRecord(rec, CFG)
39
- eq(n.source, 'numbat', 'source')
40
- eq(n.recordType, 'finding', 'type')
41
- eq(n.severity, 'high', 'severity')
42
- eq(n.ruleId, 'cloud-metadata', 'ruleId')
43
- eq(n.agent, 'codex', 'agent')
44
- eq(n.host, 'web-01', 'host')
45
- })
46
-
47
- test('normalizeRecord drops low-signal raw events (noise)', () => {
48
- const rec = { record_type: 'event', severity: 'info', summary: 'routine' }
49
- eq(normalizeRecord(rec, CFG), null, 'info event dropped')
50
- })
51
-
52
- test('normalizeRecord keeps high-severity events', () => {
53
- const rec = { record_type: 'event', severity: 'critical', summary: 'metadata 169.254.169.254' }
54
- const n = normalizeRecord(rec, CFG)
55
- assert(n, 'critical event kept')
56
- eq(n.severity, 'critical', 'severity')
57
- })
58
-
59
- test('normalizeRecord enforces minSeverity', () => {
60
- const rec = { record_type: 'finding', severity: 'low', rule_id: 'x' }
61
- eq(normalizeRecord(rec, { minSeverity: 'high' }), null, 'low finding below high threshold dropped')
62
- assert(normalizeRecord(rec, { minSeverity: 'low' }), 'low finding at low threshold kept')
63
- })
64
-
65
- // ─── parseNdjson ────────────────────────────────────────────────────────────
66
-
67
- test('parseNdjson parses multiple records, skips malformed', () => {
68
- const nd = [
69
- JSON.stringify({ record_type: 'finding', severity: 'high', rule_id: 'r1', agent: 'codex' }),
70
- 'not json',
71
- JSON.stringify({ record_type: 'event', severity: 'info' }),
72
- JSON.stringify({ record_type: 'indicator', severity: 'medium', rule_id: 'r2' }),
73
- ].join('\n')
74
- const out = parseNdjson(nd, CFG)
75
- eq(out.length, 2, 'two findings (finding + indicator; info event + malformed dropped)')
76
- eq(out[0].ruleId, 'r1', 'first rule')
77
- eq(out[1].recordType, 'indicator', 'second type')
78
- })
79
-
80
- // ─── buildDeployCommand ─────────────────────────────────────────────────────
81
-
82
- test('buildDeployCommand builds correct argv per action', () => {
83
- eq(JSON.stringify(buildDeployCommand('inventory')), JSON.stringify(['agents']), 'inventory')
84
- eq(JSON.stringify(buildDeployCommand('scan', { agent: 'codex' })), JSON.stringify(['scan', '--agent', 'codex']), 'scan')
85
- eq(JSON.stringify(buildDeployCommand('install-monitor', { agent: 'codex' })), JSON.stringify(['hook', 'install', '--agent', 'codex', '--emit', 'all']), 'install-monitor')
86
- const en = buildDeployCommand('install-enforce', { agent: 'codex', rulesDir: './policy' })
87
- assert(en.includes('--enforce'), 'enforce flag')
88
- assert(en.includes('--rules-dir'), 'rules-dir flag')
89
- eq(JSON.stringify(buildDeployCommand('status', { agent: 'codex' })), JSON.stringify(['hook', 'status', '--agent', 'codex']), 'status')
90
- })
91
-
92
- test('buildDeployCommand throws on unknown action', () => {
93
- let threw = false
94
- try { buildDeployCommand('bogus') } catch { threw = true }
95
- assert(threw, 'expected throw')
96
- })
97
-
98
- // ─── register wiring ────────────────────────────────────────────────────────
99
-
100
- test('register wires 4 tools, 1 trigger, 1 panel', () => {
101
- const { tools, triggers, panels, ctx } = mkCtx()
102
- register(ctx)
103
- eq(tools.size, 4, 'tool count')
104
- for (const n of ['numbat_health', 'numbat_deploy', 'numbat_ingest', 'numbat_findings_summary']) assert(tools.has(n), `missing ${n}`)
105
- eq(triggers.length, 1, 'trigger count')
106
- eq(triggers[0].name, 'numbat_finding', 'trigger name')
107
- eq(panels.length, 1, 'panel count')
108
- })
109
-
110
- // ─── ingest → trigger ───────────────────────────────────────────────────────
111
-
112
- test('numbat_ingest normalizes + emits a trigger event per finding', async () => {
113
- const { tools, emitted, ctx } = mkCtx()
114
- register(ctx)
115
- const nd = [
116
- JSON.stringify({ record_type: 'finding', severity: 'high', rule_id: 'cloud-metadata', agent: 'codex', host: 'web-01' }),
117
- JSON.stringify({ record_type: 'finding', severity: 'medium', rule_id: 'r2', agent: 'cursor' }),
118
- ].join('\n')
119
- const r = await tools.get('numbat_ingest').handler({ ndjson: nd })
120
- eq(r.ingested, 2, 'ingested count')
121
- eq(emitted.length, 2, 'emitted events')
122
- eq(emitted[0].source, 'numbat', 'event source')
123
- eq(emitted[0].ruleId, 'cloud-metadata', 'event ruleId')
124
- })
125
-
126
- // ─── trigger match ──────────────────────────────────────────────────────────
127
-
128
- test('numbat_finding trigger matches numbat-source medium+ severity', () => {
129
- const { triggers, ctx } = mkCtx()
130
- register(ctx)
131
- const t = triggers[0]
132
- assert(t.match({ source: 'numbat', severity: 'high' }), 'matches high')
133
- assert(t.match({ source: 'numbat', severity: 'medium' }), 'matches medium')
134
- assert(!t.match({ source: 'numbat', severity: 'low' }), 'rejects low')
135
- assert(!t.match({ source: 'other', severity: 'high' }), 'rejects other source')
136
- })
137
-
138
- // ─── runner ─────────────────────────────────────────────────────────────────
139
- async function main() {
140
- let pass = 0, fail = 0
141
- for (const c of cases) {
142
- try { await c.run(); pass++; console.log(`PASS ${c.name}`) }
143
- catch (e) { fail++; console.log(`FAIL ${c.name}: ${e?.message ?? e}`) }
144
- }
145
- console.log(`\n${pass}/${pass + fail} passed, ${fail} failed`)
146
- if (fail > 0) process.exit(1)
147
- }
148
- main()
@@ -1,21 +0,0 @@
1
- {
2
- "name": "numbat-bridge",
3
- "version": "1.0.0",
4
- "description": "Numbat bridge for RTerm — deploy Numbat (endpoint AI-agent detection/EDR) to hosts, ingest its findings (NDJSON events/findings/enforcement/indicators), and fire RTerm triggers to auto-remediate (isolate host, kill agent, open incident, run playbook). Numbat detects; RTerm responds. Config in Settings (numbat block).",
5
- "entry": "index.mjs",
6
- "tools": [
7
- "numbat_health",
8
- "numbat_deploy",
9
- "numbat_ingest",
10
- "numbat_findings_summary"
11
- ],
12
- "triggers": [
13
- "numbat_finding"
14
- ],
15
- "panels": [
16
- "numbat-findings"
17
- ],
18
- "permissions": [
19
- "exec"
20
- ]
21
- }
@@ -1,10 +0,0 @@
1
- // patch-manager plugin type declarations
2
- export function register(ctx: any): void
3
- export function buildPatchStatusCommand(os: string): string
4
- export function buildPatchApplyCommand(os: string, opts?: { severity?: string; dryRun?: boolean }): string
5
- export function buildPrePatchCheckCommand(os: string): string
6
- export function buildPostPatchCheckCommand(os: string): string
7
- export function parsePatchStatus(output: string | null | undefined, os: string | null | undefined): { patches: Array<{ id: string; title: string; severity: string; os: string }>; summary: { total: number; critical: number; security: number; recommended: number } }
8
- export function buildPatchPlan(host: string, os: string, patchStatus: any, opts?: { severity?: string }): any
9
- export function buildComplianceReport(hostStatuses: Record<string, any> | null | undefined): any
10
- export default any
@@ -1,288 +0,0 @@
1
- /**
2
- * patch-manager plugin — autonomous patch management for RTerm.
3
- *
4
- * Discovers patch status across hosts (yum/apt/Windows Update), plans patch
5
- * deployments, executes via playbooks with MOP approval gates, and alerts on
6
- * completion/failure. Combines RTerm's fleet orchestration, MOP change
7
- * management, triggers, and audit trail into an autonomous patch pipeline.
8
- */
9
-
10
- // --- Pure: build the command to query patch status on a host ---
11
- export function buildPatchStatusCommand(os) {
12
- const o = String(os).toLowerCase()
13
- if (o === 'windows' || o === 'win32') {
14
- return 'powershell -Command "Get-WindowsUpdate -Verbose 2>&1 | Select-Object -First 50"'
15
- }
16
- if (o === 'linux' || o === 'darwin') {
17
- // Try yum first, then apt
18
- return '(yum check-update --quiet 2>/dev/null || apt list --upgradable 2>/dev/null) | head -50'
19
- }
20
- return 'echo "unsupported os: ' + o + '"'
21
- }
22
-
23
- // --- Pure: build the command to apply patches on a host ---
24
- export function buildPatchApplyCommand(os, opts = {}) {
25
- const o = String(os).toLowerCase()
26
- const severity = opts.severity ?? 'all' // all, critical, security, recommended
27
- const dryRun = opts.dryRun ?? false
28
-
29
- if (o === 'windows' || o === 'win32') {
30
- const kbFilter = severity === 'critical' ? '-KBArticleID "KB*"' : ''
31
- const dryRunFlag = dryRun ? '-WhatIf' : ''
32
- return `powershell -Command "Install-WindowsUpdate ${kbFilter} ${dryRunFlag} -AcceptAll -AutoReboot:$false 2>&1"`
33
- }
34
- if (o === 'linux') {
35
- if (severity === 'security') {
36
- return dryRun
37
- ? '(yum update --security --assumeno 2>/dev/null || apt-get -s upgrade --with-new-pkgs 2>/dev/null | grep -i security | head -20)'
38
- : '(yum update --security -y 2>/dev/null || apt-get upgrade -y --with-new-pkgs 2>/dev/null) | tail -20'
39
- }
40
- return dryRun
41
- ? '(yum update --assumeno 2>/dev/null || apt-get -s upgrade 2>/dev/null) | tail -20'
42
- : '(yum update -y 2>/dev/null || apt-get upgrade -y 2>/dev/null) | tail -20'
43
- }
44
- return 'echo "unsupported os: ' + o + '"'
45
- }
46
-
47
- // --- Pure: build the pre-patch verification command ---
48
- export function buildPrePatchCheckCommand(os) {
49
- const o = String(os).toLowerCase()
50
- if (o === 'windows' || o === 'win32') {
51
- return 'powershell -Command "Get-PSDrive -Name C | Select-Object Used,Free; Get-Service -Name wuauserv | Select-Object Status"'
52
- }
53
- if (o === 'linux') {
54
- return 'df -h / | tail -1; systemctl is-system-running 2>/dev/null || true; uptime'
55
- }
56
- return 'echo "unsupported os: ' + o + '"'
57
- }
58
-
59
- // --- Pure: build the post-patch verification command ---
60
- export function buildPostPatchCheckCommand(os) {
61
- const o = String(os).toLowerCase()
62
- if (o === 'windows' || o === 'win32') {
63
- return 'powershell -Command "Get-WindowsUpdate -Verbose 2>&1 | Select-Object -First 10; Get-Service -Name wuauserv | Select-Object Status"'
64
- }
65
- if (o === 'linux') {
66
- return 'echo "post-patch check: system running"; uptime; df -h / | tail -1'
67
- }
68
- return 'echo "unsupported os: ' + o + '"'
69
- }
70
-
71
- // --- Pure: parse patch status output into structured results ---
72
- export function parsePatchStatus(output, os) {
73
- const o = String(os ?? '').toLowerCase()
74
- const lines = String(output ?? '').split(/\r?\n/).filter((l) => l.trim().length > 0)
75
- const patches = []
76
- let summary = { total: 0, critical: 0, security: 0, recommended: 0 }
77
-
78
- for (const line of lines) {
79
- const l = line.trim()
80
- // Windows: KB article lines
81
- const kbMatch = l.match(/(KB\d{7})/i)
82
- if (kbMatch) {
83
- patches.push({
84
- id: kbMatch[1],
85
- title: l.replace(/KB\d{7}/i, '').trim().slice(0, 80),
86
- severity: l.match(/critical/i) ? 'critical' : l.match(/security/i) ? 'security' : 'recommended',
87
- os: 'windows',
88
- })
89
- summary.total++
90
- if (l.match(/critical/i)) summary.critical++
91
- if (l.match(/security/i)) summary.security++
92
- continue
93
- }
94
- // Linux yum: package.arch version repo
95
- const yumMatch = l.match(/^(\S+)\.(\S+)\s+(\S+)\s+(\S+)/)
96
- if (yumMatch && !l.startsWith('Loaded') && !l.startsWith('Last') && !l.startsWith('No')) {
97
- patches.push({
98
- id: yumMatch[1],
99
- title: `${yumMatch[1]}.${yumMatch[2]} ${yumMatch[3]} (${yumMatch[4]})`,
100
- severity: l.match(/security/i) ? 'security' : l.match(/critical/i) ? 'critical' : 'recommended',
101
- os: 'linux',
102
- })
103
- summary.total++
104
- if (l.match(/security/i)) summary.security++
105
- if (l.match(/critical/i)) summary.critical++
106
- continue
107
- }
108
- // Linux apt: package/version
109
- const aptMatch = l.match(/^(\S+)\/(\S+)\s+(\S+)\s+(\S+)/)
110
- if (aptMatch && !l.startsWith('Listing')) {
111
- patches.push({
112
- id: aptMatch[1],
113
- title: `${aptMatch[1]}/${aptMatch[2]} ${aptMatch[3]} (${aptMatch[4]})`,
114
- severity: l.match(/security/i) ? 'security' : l.match(/critical/i) ? 'critical' : 'recommended',
115
- os: 'linux',
116
- })
117
- summary.total++
118
- if (l.match(/security/i)) summary.security++
119
- if (l.match(/critical/i)) summary.critical++
120
- }
121
- }
122
-
123
- summary.recommended = summary.total - summary.critical - summary.security
124
- return { patches, summary }
125
- }
126
-
127
- // --- Pure: build a patch plan from patch status ---
128
- export function buildPatchPlan(host, os, patchStatus, opts = {}) {
129
- const severity = opts.severity ?? 'all'
130
- const toApply = severity === 'all'
131
- ? patchStatus.patches
132
- : patchStatus.patches.filter((p) => p.severity === severity)
133
-
134
- return {
135
- host,
136
- os,
137
- patchesToApply: toApply.length,
138
- patchIds: toApply.map((p) => p.id),
139
- severity,
140
- estimatedDowntimeMin: toApply.length * 2, // rough estimate
141
- requiresReboot: os === 'windows' || toApply.some((p) => p.severity === 'critical'),
142
- plan: [
143
- { step: 1, name: 'pre-check', command: buildPrePatchCheckCommand(os), description: 'Verify system health before patching' },
144
- { step: 2, name: 'backup', command: 'echo "snapshot/checkpoint before patch"', description: 'Create restore point' },
145
- { step: 3, name: 'apply', command: buildPatchApplyCommand(os, { severity }), description: `Apply ${toApply.length} patches (${severity})` },
146
- { step: 4, name: 'post-check', command: buildPostPatchCheckCommand(os), description: 'Verify patches applied successfully' },
147
- { step: 5, name: 'rollback', command: 'echo "rollback if post-check fails"', description: 'Rollback on failure', onError: 'continue' },
148
- ],
149
- }
150
- }
151
-
152
- // --- Pure: build a compliance report for a fleet ---
153
- export function buildComplianceReport(hostStatuses) {
154
- const hosts = Object.entries(hostStatuses ?? {}).map(([host, status]) => ({
155
- host,
156
- os: status.os ?? 'unknown',
157
- totalPatches: status.summary?.total ?? 0,
158
- criticalPatches: status.summary?.critical ?? 0,
159
- securityPatches: status.summary?.security ?? 0,
160
- compliant: (status.summary?.critical ?? 0) === 0 && (status.summary?.security ?? 0) === 0,
161
- }))
162
-
163
- const totalHosts = hosts.length
164
- const compliantHosts = hosts.filter((h) => h.compliant).length
165
- const nonCompliantHosts = totalHosts - compliantHosts
166
- const totalCritical = hosts.reduce((s, h) => s + h.criticalPatches, 0)
167
- const totalSecurity = hosts.reduce((s, h) => s + h.securityPatches, 0)
168
-
169
- return {
170
- hosts,
171
- summary: {
172
- totalHosts,
173
- compliantHosts,
174
- nonCompliantHosts,
175
- complianceRate: totalHosts > 0 ? Math.round((compliantHosts / totalHosts) * 100) : 100,
176
- totalCriticalPatches: totalCritical,
177
- totalSecurityPatches: totalSecurity,
178
- },
179
- }
180
- }
181
-
182
- // --- Plugin entry ---
183
- export function register(ctx) {
184
- const { registerTool, registerTrigger, registerPanel, exec, readLedger, log } = ctx
185
-
186
- // Tool: patch_status — query patch status of a host
187
- registerTool({
188
- name: 'patch_status',
189
- description: 'Query the current patch status of a host (available updates). Returns patch list with severity classification.',
190
- params: {
191
- host: { type: 'string', description: 'Host to query' },
192
- os: { type: 'string', description: 'OS type: linux, windows, darwin' },
193
- },
194
- handler: async (params) => {
195
- const { host, os } = params ?? {}
196
- if (!host || !os) return { error: 'host and os are required' }
197
- const cmd = buildPatchStatusCommand(os)
198
- log(`[patch-manager] querying patch status on ${host} (${os})`)
199
- const output = await exec(cmd, { host })
200
- const result = parsePatchStatus(output, os)
201
- return { host, os, ...result }
202
- },
203
- })
204
-
205
- // Tool: patch_plan — build a patch deployment plan for a host
206
- registerTool({
207
- name: 'patch_plan',
208
- description: 'Build a patch deployment plan for a host (pre-check → backup → apply → post-check → rollback). Returns the plan for MOP approval.',
209
- params: {
210
- host: { type: 'string', description: 'Host to plan for' },
211
- os: { type: 'string', description: 'OS type' },
212
- severity: { type: 'string', description: 'Patch severity filter: all, critical, security, recommended' },
213
- },
214
- handler: async (params) => {
215
- const { host, os, severity = 'all' } = params ?? {}
216
- if (!host || !os) return { error: 'host and os are required' }
217
- // First get current status
218
- const statusCmd = buildPatchStatusCommand(os)
219
- const statusOutput = await exec(statusCmd, { host })
220
- const patchStatus = parsePatchStatus(statusOutput, os)
221
- const plan = buildPatchPlan(host, os, patchStatus, { severity })
222
- log(`[patch-manager] built patch plan for ${host}: ${plan.patchesToApply} patches`)
223
- return plan
224
- },
225
- })
226
-
227
- // Tool: patch_apply — execute a patch plan on a host (runs as MOP change)
228
- registerTool({
229
- name: 'patch_apply',
230
- description: 'Execute a patch deployment on a host. This is a destructive operation — requires MOP approval. Returns the execution result.',
231
- params: {
232
- host: { type: 'string', description: 'Host to patch' },
233
- os: { type: 'string', description: 'OS type' },
234
- severity: { type: 'string', description: 'Patch severity filter' },
235
- dryRun: { type: 'boolean', description: 'If true, simulate only (no changes)' },
236
- },
237
- handler: async (params) => {
238
- const { host, os, severity = 'all', dryRun = false } = params ?? {}
239
- if (!host || !os) return { error: 'host and os are required' }
240
- const applyCmd = buildPatchApplyCommand(os, { severity, dryRun })
241
- log(`[patch-manager] ${dryRun ? 'simulating' : 'applying'} patches on ${host} (${severity})`)
242
- const output = await exec(applyCmd, { host })
243
- const postCheck = await exec(buildPostPatchCheckCommand(os), { host })
244
- return { host, os, severity, dryRun, output: output.slice(0, 2000), postCheck: postCheck.slice(0, 500) }
245
- },
246
- })
247
-
248
- // Trigger: patch_failure — fires when a patch execution fails
249
- registerTrigger({
250
- name: 'patch_failure',
251
- description: 'Fires when a patch execution output contains error/failure indicators. Use for auto-remediation or investigation.',
252
- match: (event) => {
253
- if (event?.source !== 'playbook') return false
254
- const detail = String(event?.detail ?? '').toLowerCase()
255
- return detail.includes('error') || detail.includes('failed') || detail.includes('rollback')
256
- },
257
- action: 'propose-change',
258
- })
259
-
260
- // Trigger: patch_completion — fires when a patch execution completes successfully
261
- registerTrigger({
262
- name: 'patch_completion',
263
- description: 'Fires when a patch execution completes without errors. Use for compliance reporting.',
264
- match: (event) => {
265
- if (event?.source !== 'playbook') return false
266
- const detail = String(event?.detail ?? '').toLowerCase()
267
- return detail.includes('success') || detail.includes('completed') || detail.includes('no packages marked for update')
268
- },
269
- action: 'run-playbook',
270
- })
271
-
272
- // Panel: patch-compliance — patch compliance dashboard
273
- registerPanel({
274
- name: 'patch-compliance',
275
- title: 'Patch Compliance',
276
- render: (data) => {
277
- const report = buildComplianceReport(data ?? {})
278
- const rows = report.hosts.map((h) =>
279
- `<tr><td>${h.host}</td><td>${h.os}</td><td>${h.totalPatches}</td><td>${h.criticalPatches}</td><td>${h.securityPatches}</td><td>${h.compliant ? '✅' : '❌'}</td></tr>`
280
- ).join('')
281
- return `<div class="patch-compliance"><h3>Patch Compliance</h3><p>Compliance rate: ${report.summary.complianceRate}% (${report.summary.compliantHosts}/${report.summary.totalHosts} hosts)</p><p>Critical: ${report.summary.totalCriticalPatches} | Security: ${report.summary.totalSecurityPatches}</p><table><thead><tr><th>Host</th><th>OS</th><th>Total</th><th>Critical</th><th>Security</th><th>Compliant</th></tr></thead><tbody>${rows}</tbody></table></div>`
282
- },
283
- })
284
-
285
- log('[patch-manager] registered: 3 tools, 2 triggers, 1 panel')
286
- }
287
-
288
- export default { register, buildPatchStatusCommand, buildPatchApplyCommand, buildPrePatchCheckCommand, buildPostPatchCheckCommand, parsePatchStatus, buildPatchPlan, buildComplianceReport }
@@ -1,10 +0,0 @@
1
- {
2
- "name": "patch-manager",
3
- "version": "1.0.0",
4
- "description": "Autonomous patch management for RTerm — discover patch status across hosts, plan patch deployments, execute via playbooks with MOP approval gates, and alert on completion/failure. Supports Linux (yum/apt) and Windows (Get-WindowsUpdate).",
5
- "entry": "index.mjs",
6
- "tools": ["patch_status", "patch_plan", "patch_apply"],
7
- "triggers": ["patch_failure", "patch_completion"],
8
- "panels": ["patch-compliance"],
9
- "permissions": ["exec", "readLedger:metrics", "readLedger:incidents"]
10
- }
@@ -1,15 +0,0 @@
1
- // promptfoo-redteam plugin type declarations
2
- export function register(ctx: any): void
3
- export function buildPromptfooConfig(opts: {
4
- providers: Array<{ name: string; model: string; baseUrl?: string; apiKeyRef?: string }>
5
- tests: Array<{ vars: { prompt: string }; assert?: any[] }>
6
- description?: string
7
- }): { description: string; prompts: string[]; providers: any[]; tests: any[] }
8
- export function builtinRedteamTests(): Array<{ description: string; vars: { prompt: string }; assert: any[] }>
9
- export function parsePromptfooResults(raw: unknown): {
10
- summary: { total: number; passed: number; failed: number; errors: number; byProvider: Record<string, { total: number; passed: number; failed: number }> }
11
- findings: Array<{ severity: string; category: string; provider: string; test: string; score: number; message: string }>
12
- error?: string
13
- }
14
- export function redteamVerdict(summary: { total: number; passed?: number; failed?: number; errors?: number } | null | undefined): string
15
- export default any