@upx-us/shield 0.3.29 → 0.4.36
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/CHANGELOG.md +462 -0
- package/README.md +75 -14
- package/dist/index.js +180 -16
- package/dist/src/case-monitor.d.ts +24 -0
- package/dist/src/case-monitor.js +193 -0
- package/dist/src/cli-cases.d.ts +1 -0
- package/dist/src/cli-cases.js +184 -0
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +2 -0
- package/dist/src/event-store.d.ts +31 -0
- package/dist/src/event-store.js +163 -0
- package/dist/src/index.js +43 -5
- package/dist/src/inventory.d.ts +26 -0
- package/dist/src/inventory.js +191 -0
- package/dist/src/rpc/client.d.ts +12 -0
- package/dist/src/rpc/client.js +105 -0
- package/dist/src/rpc/handlers.d.ts +57 -0
- package/dist/src/rpc/handlers.js +141 -0
- package/dist/src/rpc/index.d.ts +10 -0
- package/dist/src/rpc/index.js +13 -0
- package/dist/src/safe-io.d.ts +2 -0
- package/dist/src/safe-io.js +78 -0
- package/dist/src/transformer.d.ts +1 -0
- package/dist/src/transformer.js +59 -20
- package/dist/src/updater.js +8 -9
- package/openclaw.plugin.json +80 -75
- package/package.json +17 -8
- package/skills/shield/README.md +39 -0
- package/skills/shield/SKILL.md +66 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface AgentInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string | null;
|
|
4
|
+
workspace: string;
|
|
5
|
+
has_identity: boolean;
|
|
6
|
+
is_bootstrapped: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface HostInventory {
|
|
9
|
+
collected_at: string;
|
|
10
|
+
agents: AgentInfo[];
|
|
11
|
+
agent_count: number;
|
|
12
|
+
workspace_count: number;
|
|
13
|
+
}
|
|
14
|
+
export interface InventoryPaths {
|
|
15
|
+
agentsDir: string;
|
|
16
|
+
openclawHome: string;
|
|
17
|
+
vaultPath: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function scanAgents(paths?: Partial<InventoryPaths>): AgentInfo[];
|
|
20
|
+
export declare function readVault(vaultPath?: string): Record<string, unknown>;
|
|
21
|
+
export declare function writeVault(vault: Record<string, unknown>, vaultPath?: string): void;
|
|
22
|
+
export declare function collectInventory(paths?: Partial<InventoryPaths>): HostInventory;
|
|
23
|
+
export declare function loadCachedInventory(paths?: Partial<InventoryPaths>): HostInventory | null;
|
|
24
|
+
export declare function setCachedInventory(inventory: HostInventory): void;
|
|
25
|
+
export declare function resetCachedInventory(): void;
|
|
26
|
+
export declare function detectCrossWorkspace(text: string, currentAgentId: string, inventory?: HostInventory | null): string | null;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.scanAgents = scanAgents;
|
|
37
|
+
exports.readVault = readVault;
|
|
38
|
+
exports.writeVault = writeVault;
|
|
39
|
+
exports.collectInventory = collectInventory;
|
|
40
|
+
exports.loadCachedInventory = loadCachedInventory;
|
|
41
|
+
exports.setCachedInventory = setCachedInventory;
|
|
42
|
+
exports.resetCachedInventory = resetCachedInventory;
|
|
43
|
+
exports.detectCrossWorkspace = detectCrossWorkspace;
|
|
44
|
+
const os_1 = require("os");
|
|
45
|
+
const path_1 = require("path");
|
|
46
|
+
const fs_1 = require("fs");
|
|
47
|
+
const safe_io_1 = require("./safe-io");
|
|
48
|
+
const log = __importStar(require("./log"));
|
|
49
|
+
const OPENCLAW_HOME = (0, path_1.join)((0, os_1.homedir)(), '.openclaw');
|
|
50
|
+
const AGENTS_DIR = (0, path_1.join)(OPENCLAW_HOME, 'agents');
|
|
51
|
+
const VAULT_PATH = (0, path_1.join)(OPENCLAW_HOME, 'shield', 'vault.json');
|
|
52
|
+
function defaultPaths() {
|
|
53
|
+
return { agentsDir: AGENTS_DIR, openclawHome: OPENCLAW_HOME, vaultPath: VAULT_PATH };
|
|
54
|
+
}
|
|
55
|
+
function resolveWorkspace(agentId) {
|
|
56
|
+
if (agentId === 'main')
|
|
57
|
+
return 'workspace';
|
|
58
|
+
return `workspace-${agentId}`;
|
|
59
|
+
}
|
|
60
|
+
function parseIdentityName(identityPath) {
|
|
61
|
+
try {
|
|
62
|
+
if (!(0, fs_1.existsSync)(identityPath))
|
|
63
|
+
return null;
|
|
64
|
+
const content = (0, fs_1.readFileSync)(identityPath, 'utf8');
|
|
65
|
+
const match = content.match(/^\s*-\s*\*\*Name:\*\*\s*(.+)$/m);
|
|
66
|
+
if (match) {
|
|
67
|
+
const name = match[1].trim();
|
|
68
|
+
return name || null;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function scanAgents(paths) {
|
|
77
|
+
const p = { ...defaultPaths(), ...paths };
|
|
78
|
+
const agents = [];
|
|
79
|
+
if (!(0, fs_1.existsSync)(p.agentsDir)) {
|
|
80
|
+
log.warn('inventory', `Agents directory not found: ${p.agentsDir}`);
|
|
81
|
+
return agents;
|
|
82
|
+
}
|
|
83
|
+
let entries;
|
|
84
|
+
try {
|
|
85
|
+
entries = (0, fs_1.readdirSync)(p.agentsDir);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
log.warn('inventory', `Failed to read agents directory: ${err instanceof Error ? err.message : String(err)}`);
|
|
89
|
+
return agents;
|
|
90
|
+
}
|
|
91
|
+
for (const agentId of entries) {
|
|
92
|
+
try {
|
|
93
|
+
const workspaceName = resolveWorkspace(agentId);
|
|
94
|
+
const workspacePath = (0, path_1.join)(p.openclawHome, workspaceName);
|
|
95
|
+
const identityPath = (0, path_1.join)(workspacePath, 'IDENTITY.md');
|
|
96
|
+
const bootstrapPath = (0, path_1.join)(workspacePath, 'BOOTSTRAP.md');
|
|
97
|
+
const name = parseIdentityName(identityPath);
|
|
98
|
+
const hasIdentity = (0, fs_1.existsSync)(identityPath);
|
|
99
|
+
const isBootstrapped = !(0, fs_1.existsSync)(bootstrapPath);
|
|
100
|
+
agents.push({
|
|
101
|
+
id: agentId,
|
|
102
|
+
name,
|
|
103
|
+
workspace: workspaceName,
|
|
104
|
+
has_identity: hasIdentity,
|
|
105
|
+
is_bootstrapped: isBootstrapped,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
log.warn('inventory', `Failed to process agent "${agentId}": ${err instanceof Error ? err.message : String(err)}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return agents;
|
|
113
|
+
}
|
|
114
|
+
function readVault(vaultPath) {
|
|
115
|
+
const path = vaultPath ?? VAULT_PATH;
|
|
116
|
+
try {
|
|
117
|
+
if (!(0, fs_1.existsSync)(path))
|
|
118
|
+
return {};
|
|
119
|
+
const content = (0, fs_1.readFileSync)(path, 'utf8');
|
|
120
|
+
const parsed = JSON.parse(content);
|
|
121
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
122
|
+
log.warn('inventory', 'Vault file has unexpected shape — resetting');
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
return parsed;
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
log.warn('inventory', `Failed to read vault (will reset): ${err instanceof Error ? err.message : String(err)}`);
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function writeVault(vault, vaultPath) {
|
|
133
|
+
const p = vaultPath ?? VAULT_PATH;
|
|
134
|
+
try {
|
|
135
|
+
(0, safe_io_1.writeJsonSafe)(p, vault);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
log.warn('inventory', `Failed to write vault: ${err instanceof Error ? err.message : String(err)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function collectInventory(paths) {
|
|
142
|
+
const agents = scanAgents(paths);
|
|
143
|
+
const workspaces = new Set(agents.map(a => a.workspace));
|
|
144
|
+
const inventory = {
|
|
145
|
+
collected_at: new Date().toISOString(),
|
|
146
|
+
agents,
|
|
147
|
+
agent_count: agents.length,
|
|
148
|
+
workspace_count: workspaces.size,
|
|
149
|
+
};
|
|
150
|
+
const p = { ...defaultPaths(), ...paths };
|
|
151
|
+
const vault = readVault(p.vaultPath);
|
|
152
|
+
vault.host_inventory = inventory;
|
|
153
|
+
writeVault(vault, p.vaultPath);
|
|
154
|
+
log.info('inventory', `Collected inventory: ${inventory.agent_count} agents, ${inventory.workspace_count} workspaces`);
|
|
155
|
+
return inventory;
|
|
156
|
+
}
|
|
157
|
+
function escapeRegex(s) {
|
|
158
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
159
|
+
}
|
|
160
|
+
let _cachedInventory = null;
|
|
161
|
+
function loadCachedInventory(paths) {
|
|
162
|
+
if (_cachedInventory)
|
|
163
|
+
return _cachedInventory;
|
|
164
|
+
const p = { ...defaultPaths(), ...paths };
|
|
165
|
+
const vault = readVault(p.vaultPath);
|
|
166
|
+
if (vault.host_inventory && typeof vault.host_inventory === 'object') {
|
|
167
|
+
_cachedInventory = vault.host_inventory;
|
|
168
|
+
return _cachedInventory;
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
function setCachedInventory(inventory) {
|
|
173
|
+
_cachedInventory = inventory;
|
|
174
|
+
}
|
|
175
|
+
function resetCachedInventory() {
|
|
176
|
+
_cachedInventory = null;
|
|
177
|
+
}
|
|
178
|
+
function detectCrossWorkspace(text, currentAgentId, inventory) {
|
|
179
|
+
const inv = inventory ?? _cachedInventory;
|
|
180
|
+
if (!inv || inv.agents.length === 0)
|
|
181
|
+
return null;
|
|
182
|
+
for (const agent of inv.agents) {
|
|
183
|
+
if (agent.id === currentAgentId)
|
|
184
|
+
continue;
|
|
185
|
+
const pattern = new RegExp(`(?:^|[/\\\\])${escapeRegex(agent.workspace)}(?:[/\\\\]|$)`);
|
|
186
|
+
if (pattern.test(text)) {
|
|
187
|
+
return agent.workspace;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface PlatformApiConfig {
|
|
2
|
+
apiUrl: string | null;
|
|
3
|
+
instanceId: string;
|
|
4
|
+
hmacSecret: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PlatformApiResponse<T = unknown> {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
data?: T;
|
|
9
|
+
error?: string;
|
|
10
|
+
upgradeUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function callPlatformApi<T = unknown>(config: PlatformApiConfig, path: string, params?: Record<string, unknown>, method?: 'GET' | 'POST'): Promise<PlatformApiResponse<T>>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.callPlatformApi = callPlatformApi;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
const TIMEOUT_MS = 10_000;
|
|
39
|
+
function signRequest(_method, _path, _body, secret, instanceId) {
|
|
40
|
+
const nonce = `${Date.now()}-${crypto.randomBytes(8).toString('hex')}`;
|
|
41
|
+
const signature = crypto
|
|
42
|
+
.createHmac('sha256', secret)
|
|
43
|
+
.update(`${instanceId}:${nonce}`)
|
|
44
|
+
.digest('hex');
|
|
45
|
+
return {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'User-Agent': 'OpenClaw-Shield-Plugin/rpc',
|
|
48
|
+
'X-Shield-Instance': instanceId,
|
|
49
|
+
'X-Shield-Nonce': nonce,
|
|
50
|
+
'X-Shield-Signature': signature,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function callPlatformApi(config, path, params, method) {
|
|
54
|
+
if (!config.apiUrl) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
error: 'Platform API not configured. This feature requires the Shield platform API which is not yet available for your instance. Check your Shield dashboard for updates.',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const url = new URL(path, config.apiUrl);
|
|
61
|
+
const httpMethod = method || (params ? 'POST' : 'GET');
|
|
62
|
+
if (httpMethod === 'GET' && params) {
|
|
63
|
+
for (const [k, v] of Object.entries(params)) {
|
|
64
|
+
if (v != null)
|
|
65
|
+
url.searchParams.set(k, String(v));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const body = JSON.stringify(params ?? {});
|
|
69
|
+
const headers = signRequest(httpMethod, path, body, config.hmacSecret, config.instanceId);
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
72
|
+
try {
|
|
73
|
+
const res = await fetch(url.toString(), {
|
|
74
|
+
method: httpMethod,
|
|
75
|
+
headers,
|
|
76
|
+
body: httpMethod === 'POST' ? body : undefined,
|
|
77
|
+
signal: controller.signal,
|
|
78
|
+
});
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
if (res.status === 403) {
|
|
81
|
+
const json = (await res.json().catch(() => ({})));
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
error: json.message ?? 'Your Shield subscription has expired or is inactive.',
|
|
85
|
+
upgradeUrl: json.upgrade_url || undefined,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
error: `Platform returned HTTP ${res.status}`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const data = (await res.json());
|
|
95
|
+
return { ok: true, data };
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
100
|
+
if (message.includes('abort')) {
|
|
101
|
+
return { ok: false, error: 'Platform API request timed out (10s).' };
|
|
102
|
+
}
|
|
103
|
+
return { ok: false, error: `Platform API error: ${message}` };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type PlatformApiConfig } from './client';
|
|
2
|
+
export interface EventSummary {
|
|
3
|
+
period: string;
|
|
4
|
+
totalEvents: number;
|
|
5
|
+
byCategory: Record<string, number>;
|
|
6
|
+
topCommands?: Array<{
|
|
7
|
+
command: string;
|
|
8
|
+
count: number;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export interface RecentEvent {
|
|
12
|
+
id: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
category: string;
|
|
15
|
+
toolName: string;
|
|
16
|
+
summary: string;
|
|
17
|
+
severity?: 'low' | 'medium' | 'high' | 'critical';
|
|
18
|
+
}
|
|
19
|
+
export interface SubscriptionStatus {
|
|
20
|
+
active: boolean;
|
|
21
|
+
tier: string;
|
|
22
|
+
expiresAt: string | null;
|
|
23
|
+
features: string[];
|
|
24
|
+
}
|
|
25
|
+
type RespondFn = (ok: boolean, data: unknown) => void;
|
|
26
|
+
export declare function createEventsRecentHandler(_config: PlatformApiConfig): ({ respond, params }: {
|
|
27
|
+
respond: RespondFn;
|
|
28
|
+
params?: Record<string, unknown>;
|
|
29
|
+
}) => Promise<void>;
|
|
30
|
+
export declare function createEventsSummaryHandler(_config: PlatformApiConfig): ({ respond, params }: {
|
|
31
|
+
respond: RespondFn;
|
|
32
|
+
params?: Record<string, unknown>;
|
|
33
|
+
}) => Promise<void>;
|
|
34
|
+
export declare function createSubscriptionStatusHandler(config: PlatformApiConfig): ({ respond }: {
|
|
35
|
+
respond: RespondFn;
|
|
36
|
+
}) => Promise<void>;
|
|
37
|
+
export declare const VALID_RESOLUTIONS: readonly ["true_positive", "false_positive", "benign", "duplicate"];
|
|
38
|
+
export declare const VALID_ROOT_CAUSES: readonly ["user_initiated", "misconfiguration", "expected_behavior", "actual_threat", "testing", "unknown"];
|
|
39
|
+
export type CaseResolution = typeof VALID_RESOLUTIONS[number];
|
|
40
|
+
export type CaseRootCause = typeof VALID_ROOT_CAUSES[number];
|
|
41
|
+
export declare function createCasesListHandler(config: PlatformApiConfig): ({ respond, params }: {
|
|
42
|
+
respond: RespondFn;
|
|
43
|
+
params?: Record<string, unknown>;
|
|
44
|
+
}) => Promise<void>;
|
|
45
|
+
export declare function createCaseDetailHandler(config: PlatformApiConfig): ({ respond, params }: {
|
|
46
|
+
respond: RespondFn;
|
|
47
|
+
params?: Record<string, unknown>;
|
|
48
|
+
}) => Promise<void>;
|
|
49
|
+
export declare function createCaseResolveHandler(config: PlatformApiConfig): ({ respond, params }: {
|
|
50
|
+
respond: RespondFn;
|
|
51
|
+
params?: Record<string, unknown>;
|
|
52
|
+
}) => Promise<void>;
|
|
53
|
+
export declare function createCasesAckHandler(): ({ respond, params }: {
|
|
54
|
+
respond: RespondFn;
|
|
55
|
+
params?: Record<string, unknown>;
|
|
56
|
+
}) => Promise<void>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VALID_ROOT_CAUSES = exports.VALID_RESOLUTIONS = void 0;
|
|
4
|
+
exports.createEventsRecentHandler = createEventsRecentHandler;
|
|
5
|
+
exports.createEventsSummaryHandler = createEventsSummaryHandler;
|
|
6
|
+
exports.createSubscriptionStatusHandler = createSubscriptionStatusHandler;
|
|
7
|
+
exports.createCasesListHandler = createCasesListHandler;
|
|
8
|
+
exports.createCaseDetailHandler = createCaseDetailHandler;
|
|
9
|
+
exports.createCaseResolveHandler = createCaseResolveHandler;
|
|
10
|
+
exports.createCasesAckHandler = createCasesAckHandler;
|
|
11
|
+
const event_store_1 = require("../event-store");
|
|
12
|
+
const client_1 = require("./client");
|
|
13
|
+
function formatResponse(result) {
|
|
14
|
+
if (result.ok) {
|
|
15
|
+
return { ok: true, data: result.data };
|
|
16
|
+
}
|
|
17
|
+
const errorData = { error: result.error };
|
|
18
|
+
if (result.upgradeUrl) {
|
|
19
|
+
errorData.upgradeUrl = result.upgradeUrl;
|
|
20
|
+
}
|
|
21
|
+
return { ok: false, data: errorData };
|
|
22
|
+
}
|
|
23
|
+
function createEventsRecentHandler(_config) {
|
|
24
|
+
return async ({ respond, params }) => {
|
|
25
|
+
const limit = typeof params?.limit === 'number' ? params.limit : 20;
|
|
26
|
+
const type = typeof params?.type === 'string' ? params.type : undefined;
|
|
27
|
+
const sinceMs = typeof params?.sinceMs === 'number' ? params.sinceMs : undefined;
|
|
28
|
+
const events = (0, event_store_1.queryEvents)({ limit, type, sinceMs });
|
|
29
|
+
respond(true, { events, count: events.length, source: 'local' });
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function createEventsSummaryHandler(_config) {
|
|
33
|
+
return async ({ respond, params }) => {
|
|
34
|
+
const sinceMs = typeof params?.sinceMs === 'number' ? params.sinceMs : undefined;
|
|
35
|
+
const summary = (0, event_store_1.summarizeEvents)(sinceMs);
|
|
36
|
+
respond(true, { ...summary, source: 'local' });
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function createSubscriptionStatusHandler(config) {
|
|
40
|
+
return async ({ respond }) => {
|
|
41
|
+
const result = await (0, client_1.callPlatformApi)(config, '/v1/subscription/status');
|
|
42
|
+
const { ok, data } = formatResponse(result);
|
|
43
|
+
respond(ok, data);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
exports.VALID_RESOLUTIONS = ['true_positive', 'false_positive', 'benign', 'duplicate'];
|
|
47
|
+
exports.VALID_ROOT_CAUSES = ['user_initiated', 'misconfiguration', 'expected_behavior', 'actual_threat', 'testing', 'unknown'];
|
|
48
|
+
function createCasesListHandler(config) {
|
|
49
|
+
return async ({ respond, params }) => {
|
|
50
|
+
const { getPendingCases, formatCaseNotification } = require('../case-monitor');
|
|
51
|
+
const pending = getPendingCases();
|
|
52
|
+
if (!config.apiUrl) {
|
|
53
|
+
respond(true, {
|
|
54
|
+
cases: pending,
|
|
55
|
+
total: pending.length,
|
|
56
|
+
has_more: false,
|
|
57
|
+
pending_count: pending.length,
|
|
58
|
+
pending_notifications: pending.map((c) => ({
|
|
59
|
+
...c,
|
|
60
|
+
formatted_message: formatCaseNotification(c),
|
|
61
|
+
})),
|
|
62
|
+
source: 'local_cache',
|
|
63
|
+
});
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const queryParams = {
|
|
67
|
+
status: typeof params?.status === 'string' ? params.status : 'open',
|
|
68
|
+
limit: typeof params?.limit === 'number' ? params.limit : 20,
|
|
69
|
+
};
|
|
70
|
+
if (typeof params?.since === 'string') {
|
|
71
|
+
queryParams.since = params.since;
|
|
72
|
+
}
|
|
73
|
+
const result = await (0, client_1.callPlatformApi)(config, '/v1/agent/cases', queryParams);
|
|
74
|
+
const { ok, data } = formatResponse(result);
|
|
75
|
+
if (ok && data && typeof data === 'object') {
|
|
76
|
+
data.pending_count = pending.length;
|
|
77
|
+
data.pending_notifications = pending.map((c) => ({
|
|
78
|
+
...c,
|
|
79
|
+
formatted_message: formatCaseNotification(c),
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
respond(ok, data);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function createCaseDetailHandler(config) {
|
|
86
|
+
return async ({ respond, params }) => {
|
|
87
|
+
const caseId = typeof params?.id === 'string' ? params.id : null;
|
|
88
|
+
if (!caseId) {
|
|
89
|
+
respond(false, { error: 'Missing required parameter: id' });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const result = await (0, client_1.callPlatformApi)(config, `/v1/agent/cases/${caseId}`);
|
|
93
|
+
const { ok, data } = formatResponse(result);
|
|
94
|
+
respond(ok, data);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createCaseResolveHandler(config) {
|
|
98
|
+
return async ({ respond, params }) => {
|
|
99
|
+
const caseId = typeof params?.id === 'string' ? params.id : null;
|
|
100
|
+
if (!caseId) {
|
|
101
|
+
respond(false, { error: 'Missing required parameter: id' });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const resolution = params?.resolution;
|
|
105
|
+
const rootCause = params?.root_cause;
|
|
106
|
+
const comment = typeof params?.comment === 'string' ? params.comment : '';
|
|
107
|
+
if (!resolution || !exports.VALID_RESOLUTIONS.includes(resolution)) {
|
|
108
|
+
respond(false, {
|
|
109
|
+
error: `Invalid resolution. Must be one of: ${exports.VALID_RESOLUTIONS.join(', ')}`,
|
|
110
|
+
valid_resolutions: exports.VALID_RESOLUTIONS,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!rootCause || !exports.VALID_ROOT_CAUSES.includes(rootCause)) {
|
|
115
|
+
respond(false, {
|
|
116
|
+
error: `Invalid root_cause. Must be one of: ${exports.VALID_ROOT_CAUSES.join(', ')}`,
|
|
117
|
+
valid_root_causes: exports.VALID_ROOT_CAUSES,
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const result = await (0, client_1.callPlatformApi)(config, `/v1/agent/cases/${caseId}/resolve`, {
|
|
122
|
+
resolution,
|
|
123
|
+
root_cause: rootCause,
|
|
124
|
+
comment,
|
|
125
|
+
});
|
|
126
|
+
const { ok, data } = formatResponse(result);
|
|
127
|
+
respond(ok, data);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function createCasesAckHandler() {
|
|
131
|
+
return async ({ respond, params }) => {
|
|
132
|
+
const { acknowledgeCases } = require('../case-monitor');
|
|
133
|
+
const ids = Array.isArray(params?.ids) ? params.ids : [];
|
|
134
|
+
if (ids.length === 0) {
|
|
135
|
+
respond(false, { error: 'Missing required parameter: ids (array of case IDs)' });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
acknowledgeCases(ids);
|
|
139
|
+
respond(true, { acknowledged: ids.length });
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PlatformApiConfig } from './client';
|
|
2
|
+
interface PluginAPI {
|
|
3
|
+
registerGatewayMethod(method: string, handler: (ctx: {
|
|
4
|
+
respond: (ok: boolean, data: unknown) => void;
|
|
5
|
+
params?: Record<string, unknown>;
|
|
6
|
+
}) => void): void;
|
|
7
|
+
}
|
|
8
|
+
export declare function registerAllRpcs(api: PluginAPI, config: PlatformApiConfig): void;
|
|
9
|
+
export type { PlatformApiConfig } from './client';
|
|
10
|
+
export type { EventSummary, RecentEvent, SubscriptionStatus } from './handlers';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAllRpcs = registerAllRpcs;
|
|
4
|
+
const handlers_1 = require("./handlers");
|
|
5
|
+
function registerAllRpcs(api, config) {
|
|
6
|
+
api.registerGatewayMethod('shield.events_recent', (0, handlers_1.createEventsRecentHandler)(config));
|
|
7
|
+
api.registerGatewayMethod('shield.events_summary', (0, handlers_1.createEventsSummaryHandler)(config));
|
|
8
|
+
api.registerGatewayMethod('shield.subscription_status', (0, handlers_1.createSubscriptionStatusHandler)(config));
|
|
9
|
+
api.registerGatewayMethod('shield.cases_list', (0, handlers_1.createCasesListHandler)(config));
|
|
10
|
+
api.registerGatewayMethod('shield.case_detail', (0, handlers_1.createCaseDetailHandler)(config));
|
|
11
|
+
api.registerGatewayMethod('shield.case_resolve', (0, handlers_1.createCaseResolveHandler)(config));
|
|
12
|
+
api.registerGatewayMethod('shield.cases_ack', (0, handlers_1.createCasesAckHandler)());
|
|
13
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.writeJsonSafe = writeJsonSafe;
|
|
37
|
+
exports.readJsonSafe = readJsonSafe;
|
|
38
|
+
const fs_1 = require("fs");
|
|
39
|
+
const path_1 = require("path");
|
|
40
|
+
const log = __importStar(require("./log"));
|
|
41
|
+
function writeJsonSafe(filePath, data) {
|
|
42
|
+
const dir = (0, path_1.dirname)(filePath);
|
|
43
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
44
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
45
|
+
const tmp = filePath + '.tmp';
|
|
46
|
+
try {
|
|
47
|
+
(0, fs_1.writeFileSync)(tmp, JSON.stringify(data, null, 2) + '\n');
|
|
48
|
+
(0, fs_1.renameSync)(tmp, filePath);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
try {
|
|
52
|
+
(0, fs_1.unlinkSync)(tmp);
|
|
53
|
+
}
|
|
54
|
+
catch { }
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function readJsonSafe(filePath, fallback, label) {
|
|
59
|
+
if (!(0, fs_1.existsSync)(filePath))
|
|
60
|
+
return fallback;
|
|
61
|
+
try {
|
|
62
|
+
const raw = (0, fs_1.readFileSync)(filePath, 'utf8').trim();
|
|
63
|
+
if (!raw)
|
|
64
|
+
return fallback;
|
|
65
|
+
return JSON.parse(raw);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
const tag = label || filePath;
|
|
69
|
+
log.warn('safe-io', `Corrupt JSON in ${tag} — using defaults: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
try {
|
|
71
|
+
const backup = filePath + '.corrupt.' + Date.now();
|
|
72
|
+
(0, fs_1.renameSync)(filePath, backup);
|
|
73
|
+
log.warn('safe-io', `Corrupt file preserved as ${backup}`);
|
|
74
|
+
}
|
|
75
|
+
catch { }
|
|
76
|
+
return fallback;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -9,6 +9,7 @@ export interface IngestPayload {
|
|
|
9
9
|
entries: EnvelopeEvent[];
|
|
10
10
|
}
|
|
11
11
|
export declare function resolveOpenClawVersion(): string;
|
|
12
|
+
export declare function _resetCachedOpenClawVersion(): void;
|
|
12
13
|
export declare function resolveAgentLabel(agentId: string): string;
|
|
13
14
|
export declare function getCachedPublicIp(): string | null;
|
|
14
15
|
export declare function resolveOutboundIp(): Promise<string | null>;
|