@upx-us/shield 0.2.12-beta
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.
Potentially problematic release.
This version of @upx-us/shield might be problematic. Click here for more details.
- package/LICENSE +38 -0
- package/README.md +96 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +365 -0
- package/dist/src/config.d.ts +43 -0
- package/dist/src/config.js +181 -0
- package/dist/src/events/base.d.ts +110 -0
- package/dist/src/events/base.js +61 -0
- package/dist/src/events/browser/enrich.d.ts +3 -0
- package/dist/src/events/browser/enrich.js +46 -0
- package/dist/src/events/browser/event.d.ts +10 -0
- package/dist/src/events/browser/event.js +2 -0
- package/dist/src/events/browser/index.d.ts +4 -0
- package/dist/src/events/browser/index.js +13 -0
- package/dist/src/events/browser/redactions.d.ts +2 -0
- package/dist/src/events/browser/redactions.js +4 -0
- package/dist/src/events/browser/validations.d.ts +3 -0
- package/dist/src/events/browser/validations.js +10 -0
- package/dist/src/events/cron/enrich.d.ts +3 -0
- package/dist/src/events/cron/enrich.js +44 -0
- package/dist/src/events/cron/event.d.ts +5 -0
- package/dist/src/events/cron/event.js +2 -0
- package/dist/src/events/cron/index.d.ts +4 -0
- package/dist/src/events/cron/index.js +13 -0
- package/dist/src/events/cron/redactions.d.ts +2 -0
- package/dist/src/events/cron/redactions.js +4 -0
- package/dist/src/events/cron/validations.d.ts +3 -0
- package/dist/src/events/cron/validations.js +4 -0
- package/dist/src/events/exec/enrich.d.ts +3 -0
- package/dist/src/events/exec/enrich.js +80 -0
- package/dist/src/events/exec/event.d.ts +11 -0
- package/dist/src/events/exec/event.js +2 -0
- package/dist/src/events/exec/index.d.ts +4 -0
- package/dist/src/events/exec/index.js +13 -0
- package/dist/src/events/exec/redactions.d.ts +3 -0
- package/dist/src/events/exec/redactions.js +12 -0
- package/dist/src/events/exec/validations.d.ts +3 -0
- package/dist/src/events/exec/validations.js +12 -0
- package/dist/src/events/file/enrich.d.ts +3 -0
- package/dist/src/events/file/enrich.js +63 -0
- package/dist/src/events/file/event.d.ts +11 -0
- package/dist/src/events/file/event.js +2 -0
- package/dist/src/events/file/index.d.ts +4 -0
- package/dist/src/events/file/index.js +13 -0
- package/dist/src/events/file/redactions.d.ts +2 -0
- package/dist/src/events/file/redactions.js +8 -0
- package/dist/src/events/file/validations.d.ts +3 -0
- package/dist/src/events/file/validations.js +10 -0
- package/dist/src/events/gateway/enrich.d.ts +3 -0
- package/dist/src/events/gateway/enrich.js +50 -0
- package/dist/src/events/gateway/event.d.ts +5 -0
- package/dist/src/events/gateway/event.js +2 -0
- package/dist/src/events/gateway/index.d.ts +4 -0
- package/dist/src/events/gateway/index.js +13 -0
- package/dist/src/events/gateway/redactions.d.ts +2 -0
- package/dist/src/events/gateway/redactions.js +4 -0
- package/dist/src/events/gateway/validations.d.ts +3 -0
- package/dist/src/events/gateway/validations.js +4 -0
- package/dist/src/events/generic/enrich.d.ts +3 -0
- package/dist/src/events/generic/enrich.js +30 -0
- package/dist/src/events/generic/event.d.ts +5 -0
- package/dist/src/events/generic/event.js +2 -0
- package/dist/src/events/generic/index.d.ts +5 -0
- package/dist/src/events/generic/index.js +14 -0
- package/dist/src/events/generic/redactions.d.ts +2 -0
- package/dist/src/events/generic/redactions.js +4 -0
- package/dist/src/events/generic/validations.d.ts +3 -0
- package/dist/src/events/generic/validations.js +4 -0
- package/dist/src/events/host-telemetry/enrich.d.ts +3 -0
- package/dist/src/events/host-telemetry/enrich.js +28 -0
- package/dist/src/events/host-telemetry/event.d.ts +4 -0
- package/dist/src/events/host-telemetry/event.js +2 -0
- package/dist/src/events/host-telemetry/index.d.ts +4 -0
- package/dist/src/events/host-telemetry/index.js +13 -0
- package/dist/src/events/host-telemetry/redactions.d.ts +2 -0
- package/dist/src/events/host-telemetry/redactions.js +4 -0
- package/dist/src/events/host-telemetry/validations.d.ts +3 -0
- package/dist/src/events/host-telemetry/validations.js +4 -0
- package/dist/src/events/index.d.ts +40 -0
- package/dist/src/events/index.js +39 -0
- package/dist/src/events/message/enrich.d.ts +3 -0
- package/dist/src/events/message/enrich.js +36 -0
- package/dist/src/events/message/event.d.ts +5 -0
- package/dist/src/events/message/event.js +2 -0
- package/dist/src/events/message/index.d.ts +4 -0
- package/dist/src/events/message/index.js +13 -0
- package/dist/src/events/message/redactions.d.ts +2 -0
- package/dist/src/events/message/redactions.js +4 -0
- package/dist/src/events/message/validations.d.ts +3 -0
- package/dist/src/events/message/validations.js +7 -0
- package/dist/src/events/sessions-spawn/enrich.d.ts +3 -0
- package/dist/src/events/sessions-spawn/enrich.js +40 -0
- package/dist/src/events/sessions-spawn/event.d.ts +9 -0
- package/dist/src/events/sessions-spawn/event.js +2 -0
- package/dist/src/events/sessions-spawn/index.d.ts +4 -0
- package/dist/src/events/sessions-spawn/index.js +13 -0
- package/dist/src/events/sessions-spawn/redactions.d.ts +2 -0
- package/dist/src/events/sessions-spawn/redactions.js +4 -0
- package/dist/src/events/sessions-spawn/validations.d.ts +3 -0
- package/dist/src/events/sessions-spawn/validations.js +4 -0
- package/dist/src/events/tool-result/enrich.d.ts +13 -0
- package/dist/src/events/tool-result/enrich.js +46 -0
- package/dist/src/events/tool-result/event.d.ts +7 -0
- package/dist/src/events/tool-result/event.js +2 -0
- package/dist/src/events/tool-result/index.d.ts +4 -0
- package/dist/src/events/tool-result/index.js +9 -0
- package/dist/src/events/tool-result/redactions.d.ts +2 -0
- package/dist/src/events/tool-result/redactions.js +7 -0
- package/dist/src/events/tool-result/validations.d.ts +3 -0
- package/dist/src/events/tool-result/validations.js +9 -0
- package/dist/src/events/web/enrich.d.ts +8 -0
- package/dist/src/events/web/enrich.js +78 -0
- package/dist/src/events/web/event.d.ts +10 -0
- package/dist/src/events/web/event.js +2 -0
- package/dist/src/events/web/index.d.ts +4 -0
- package/dist/src/events/web/index.js +13 -0
- package/dist/src/events/web/redactions.d.ts +2 -0
- package/dist/src/events/web/redactions.js +6 -0
- package/dist/src/events/web/validations.d.ts +3 -0
- package/dist/src/events/web/validations.js +10 -0
- package/dist/src/fetcher.d.ts +12 -0
- package/dist/src/fetcher.js +182 -0
- package/dist/src/host-collector.d.ts +1 -0
- package/dist/src/host-collector.js +200 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +210 -0
- package/dist/src/log.d.ts +39 -0
- package/dist/src/log.js +102 -0
- package/dist/src/redactor/base.d.ts +29 -0
- package/dist/src/redactor/base.js +9 -0
- package/dist/src/redactor/index.d.ts +27 -0
- package/dist/src/redactor/index.js +109 -0
- package/dist/src/redactor/strategies/command.d.ts +2 -0
- package/dist/src/redactor/strategies/command.js +19 -0
- package/dist/src/redactor/strategies/hostname.d.ts +2 -0
- package/dist/src/redactor/strategies/hostname.js +15 -0
- package/dist/src/redactor/strategies/index.d.ts +13 -0
- package/dist/src/redactor/strategies/index.js +25 -0
- package/dist/src/redactor/strategies/path.d.ts +2 -0
- package/dist/src/redactor/strategies/path.js +23 -0
- package/dist/src/redactor/strategies/secret-key.d.ts +2 -0
- package/dist/src/redactor/strategies/secret-key.js +22 -0
- package/dist/src/redactor/strategies/username.d.ts +2 -0
- package/dist/src/redactor/strategies/username.js +12 -0
- package/dist/src/redactor/vault.d.ts +25 -0
- package/dist/src/redactor/vault.js +209 -0
- package/dist/src/sender.d.ts +29 -0
- package/dist/src/sender.js +186 -0
- package/dist/src/setup.d.ts +10 -0
- package/dist/src/setup.js +222 -0
- package/dist/src/transformer.d.ts +26 -0
- package/dist/src/transformer.js +302 -0
- package/dist/src/validator.d.ts +17 -0
- package/dist/src/validator.js +110 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +19 -0
- package/openclaw.plugin.json +52 -0
- package/package.json +64 -0
- package/skills/shield/SKILL.md +38 -0
|
@@ -0,0 +1,181 @@
|
|
|
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.SHIELD_CONFIG_PATH = void 0;
|
|
37
|
+
exports.injectConfigEnv = injectConfigEnv;
|
|
38
|
+
exports.loadCredentials = loadCredentials;
|
|
39
|
+
exports.loadCredentialsFromPluginConfig = loadCredentialsFromPluginConfig;
|
|
40
|
+
exports.loadConfig = loadConfig;
|
|
41
|
+
const os_1 = require("os");
|
|
42
|
+
const path_1 = require("path");
|
|
43
|
+
const fs_1 = require("fs");
|
|
44
|
+
const log = __importStar(require("./log"));
|
|
45
|
+
/** Default OpenClaw agents directory */
|
|
46
|
+
const OPENCLAW_AGENTS_DIR = (0, path_1.join)((0, os_1.homedir)(), '.openclaw/agents');
|
|
47
|
+
function safeParseInt(value, fallback) {
|
|
48
|
+
if (!value)
|
|
49
|
+
return fallback;
|
|
50
|
+
const parsed = parseInt(value, 10);
|
|
51
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
52
|
+
}
|
|
53
|
+
function optString(val) {
|
|
54
|
+
return val != null ? String(val) : '';
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Auto-discover all agent session directories.
|
|
58
|
+
* Scans ~/.openclaw/agents/{agent}/sessions for directories that exist.
|
|
59
|
+
*/
|
|
60
|
+
function discoverSessionDirs() {
|
|
61
|
+
const dirs = [];
|
|
62
|
+
if (!(0, fs_1.existsSync)(OPENCLAW_AGENTS_DIR))
|
|
63
|
+
return dirs;
|
|
64
|
+
try {
|
|
65
|
+
for (const agent of (0, fs_1.readdirSync)(OPENCLAW_AGENTS_DIR)) {
|
|
66
|
+
const sessDir = (0, path_1.join)(OPENCLAW_AGENTS_DIR, agent, 'sessions');
|
|
67
|
+
if ((0, fs_1.existsSync)(sessDir)) {
|
|
68
|
+
dirs.push(sessDir);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
log.warn('config', `Failed to scan agents directory: ${err instanceof Error ? err.message : String(err)}`);
|
|
74
|
+
}
|
|
75
|
+
return dirs;
|
|
76
|
+
}
|
|
77
|
+
/** Canonical config location — shared by setup, bridge, and plugin entry. */
|
|
78
|
+
exports.SHIELD_CONFIG_PATH = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'config.env');
|
|
79
|
+
/**
|
|
80
|
+
* Load config file if it exists (~/.openclaw/shield/config.env).
|
|
81
|
+
* Parses KEY=VALUE lines into a flat record. Lines starting with # are ignored.
|
|
82
|
+
*
|
|
83
|
+
* The path can be overridden by SHIELD_CONFIG_PATH env var — used in tests
|
|
84
|
+
* to isolate from real credentials on the developer machine.
|
|
85
|
+
*/
|
|
86
|
+
function loadConfigFile() {
|
|
87
|
+
const configPath = process.env.SHIELD_CONFIG_PATH || exports.SHIELD_CONFIG_PATH;
|
|
88
|
+
if (!(0, fs_1.existsSync)(configPath))
|
|
89
|
+
return {};
|
|
90
|
+
try {
|
|
91
|
+
const lines = (0, fs_1.readFileSync)(configPath, 'utf-8').split('\n');
|
|
92
|
+
const result = {};
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
const trimmed = line.trim();
|
|
95
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
96
|
+
continue;
|
|
97
|
+
const eq = trimmed.indexOf('=');
|
|
98
|
+
if (eq < 1)
|
|
99
|
+
continue;
|
|
100
|
+
const key = trimmed.slice(0, eq).trim();
|
|
101
|
+
const val = trimmed.slice(eq + 1).trim();
|
|
102
|
+
result[key] = val;
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
catch { /* corrupt config — use defaults */ }
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Inject config file values into process.env so the standalone bridge
|
|
111
|
+
* (sender, fetcher, etc.) can read credentials via env vars.
|
|
112
|
+
* Values already set in the environment take precedence (allows override).
|
|
113
|
+
*/
|
|
114
|
+
function injectConfigEnv() {
|
|
115
|
+
const file = loadConfigFile();
|
|
116
|
+
for (const [key, val] of Object.entries(file)) {
|
|
117
|
+
if (!process.env[key])
|
|
118
|
+
process.env[key] = val;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Load credentials from config.env file and environment variables.
|
|
123
|
+
* Supports new env var names with backward compat for the old names.
|
|
124
|
+
*/
|
|
125
|
+
function loadCredentials() {
|
|
126
|
+
const file = loadConfigFile();
|
|
127
|
+
function resolve(newName, oldName) {
|
|
128
|
+
const val = process.env[newName] || process.env[oldName] || file[newName] || file[oldName] || '';
|
|
129
|
+
if (!val && (process.env[oldName] || file[oldName])) {
|
|
130
|
+
log.warn('config', `${oldName} is deprecated, use ${newName} instead`);
|
|
131
|
+
}
|
|
132
|
+
return val;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
apiUrl: process.env.SHIELD_API_URL || file.SHIELD_API_URL || '',
|
|
136
|
+
hmacSecret: resolve('SHIELD_HMAC_SECRET', 'SHIELD_SECRET'),
|
|
137
|
+
instanceId: resolve('SHIELD_INSTANCE_ID', 'SHIELD_FINGERPRINT'),
|
|
138
|
+
shieldEnv: process.env.SHIELD_ENV || file.SHIELD_ENV || '',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Build credentials from a plugin config object (openclaw.json).
|
|
143
|
+
* Falls back to config.env / env vars for any missing values.
|
|
144
|
+
*/
|
|
145
|
+
function loadCredentialsFromPluginConfig(pluginConfig) {
|
|
146
|
+
const fileCreds = loadCredentials();
|
|
147
|
+
return {
|
|
148
|
+
apiUrl: optString(pluginConfig.apiUrl) || fileCreds.apiUrl,
|
|
149
|
+
hmacSecret: optString(pluginConfig.registrationKey) || fileCreds.hmacSecret,
|
|
150
|
+
instanceId: optString(pluginConfig.instanceId) || fileCreds.instanceId,
|
|
151
|
+
// shieldEnv is internal — not exposed in plugin config (customers only see PROD)
|
|
152
|
+
shieldEnv: fileCreds.shieldEnv,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function loadConfig(overrides) {
|
|
156
|
+
if (!overrides?.credentials) {
|
|
157
|
+
injectConfigEnv();
|
|
158
|
+
}
|
|
159
|
+
let sessionDirs;
|
|
160
|
+
if (process.env.SESSION_DIR) {
|
|
161
|
+
sessionDirs = process.env.SESSION_DIR.split(',').map(s => s.trim()).filter(Boolean);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
sessionDirs = discoverSessionDirs();
|
|
165
|
+
}
|
|
166
|
+
if (sessionDirs.length === 0) {
|
|
167
|
+
log.warn('config', 'No OpenClaw session directories found. Run: npx shield-setup');
|
|
168
|
+
}
|
|
169
|
+
const credentials = overrides?.credentials ?? loadCredentials();
|
|
170
|
+
return {
|
|
171
|
+
dryRun: overrides?.dryRun ?? (process.env.DRY_RUN === 'true'),
|
|
172
|
+
pollIntervalMs: overrides?.pollIntervalMs ?? safeParseInt(process.env.POLL_INTERVAL_MS, 30000),
|
|
173
|
+
sessionDirs,
|
|
174
|
+
cursorFile: process.env.CURSOR_FILE || (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data', 'cursor.json'),
|
|
175
|
+
hostname: process.env.INSTANCE_NAME || 'openclaw-instance',
|
|
176
|
+
maxEvents: safeParseInt(process.env.MAX_EVENTS, 0),
|
|
177
|
+
collectHostMetrics: overrides?.collectHostMetrics ?? (process.env.COLLECT_HOST_METRICS === 'true'),
|
|
178
|
+
redactionEnabled: overrides?.redactionEnabled ?? (process.env.REDACTION_ENABLED !== 'false'),
|
|
179
|
+
credentials,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* base.ts — Core types, composable blocks, and generic engine for the Shield event model.
|
|
3
|
+
*
|
|
4
|
+
* All event types extend BaseEvent. The engine composes base rules with type-specific rules.
|
|
5
|
+
* Individual event types never redeclare base-level concerns.
|
|
6
|
+
*/
|
|
7
|
+
export interface BaseEvent {
|
|
8
|
+
timestamp: string;
|
|
9
|
+
event_type: 'TOOL_CALL' | 'TOOL_RESULT';
|
|
10
|
+
tool_name: string;
|
|
11
|
+
/** Discriminator — lets the parser know the shape of each event */
|
|
12
|
+
tool_category: string;
|
|
13
|
+
session_id: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
product_name: string;
|
|
16
|
+
vendor_name: string;
|
|
17
|
+
principal: {
|
|
18
|
+
hostname: string;
|
|
19
|
+
ip: string;
|
|
20
|
+
platform: string;
|
|
21
|
+
user: string;
|
|
22
|
+
};
|
|
23
|
+
tool_metadata?: Record<string, string | null>;
|
|
24
|
+
}
|
|
25
|
+
/** Attach to any event type when network context is relevant (SSH, HTTP, etc.) */
|
|
26
|
+
export interface NetworkBlock {
|
|
27
|
+
network: {
|
|
28
|
+
session_id: string;
|
|
29
|
+
protocol: string;
|
|
30
|
+
direction: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Attach to any event type when a security-relevant finding is detected.
|
|
35
|
+
* NOTE: `target` is NOT a composable block — each event type owns its `target` shape
|
|
36
|
+
* to avoid property collisions across types.
|
|
37
|
+
*/
|
|
38
|
+
export interface SecurityResultBlock {
|
|
39
|
+
security_result: {
|
|
40
|
+
severity: string;
|
|
41
|
+
summary: string;
|
|
42
|
+
category: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Dot-notation path to a field in an event, plus the redaction strategy to apply */
|
|
46
|
+
export interface FieldRedaction {
|
|
47
|
+
path: string;
|
|
48
|
+
/** Strategy key — must match a registered RedactionStrategy in src/redactor/strategies/ */
|
|
49
|
+
strategy: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ValidationResult {
|
|
52
|
+
valid: boolean;
|
|
53
|
+
field?: string;
|
|
54
|
+
error?: string;
|
|
55
|
+
}
|
|
56
|
+
/** Raw tool call from the JSONL session file */
|
|
57
|
+
export interface RawToolCall {
|
|
58
|
+
name: string;
|
|
59
|
+
id?: string;
|
|
60
|
+
arguments: Record<string, any>;
|
|
61
|
+
}
|
|
62
|
+
/** Context passed to every enrich() function */
|
|
63
|
+
export interface EnrichmentContext {
|
|
64
|
+
sessionId: string;
|
|
65
|
+
agentId: string;
|
|
66
|
+
timestamp: string;
|
|
67
|
+
model?: string;
|
|
68
|
+
source: SourceInfo;
|
|
69
|
+
}
|
|
70
|
+
export interface SourceInfo {
|
|
71
|
+
hostname: string;
|
|
72
|
+
ip_addresses: string[];
|
|
73
|
+
os: {
|
|
74
|
+
type: string;
|
|
75
|
+
platform: string;
|
|
76
|
+
release: string;
|
|
77
|
+
arch: string;
|
|
78
|
+
};
|
|
79
|
+
openclaw: {
|
|
80
|
+
version: string;
|
|
81
|
+
agent_id: string;
|
|
82
|
+
agent_label: string;
|
|
83
|
+
};
|
|
84
|
+
plugin: {
|
|
85
|
+
version: string;
|
|
86
|
+
transport: string;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Schema descriptor — one per event type */
|
|
90
|
+
export interface EventSchema<T extends BaseEvent = BaseEvent> {
|
|
91
|
+
category: string;
|
|
92
|
+
/** Pure, fast matcher. First match wins. GenericSchema must be last. */
|
|
93
|
+
match: (tool: RawToolCall) => boolean;
|
|
94
|
+
enrich: (tool: RawToolCall, context: EnrichmentContext) => T;
|
|
95
|
+
redactions: FieldRedaction[];
|
|
96
|
+
validate: (event: T) => ValidationResult;
|
|
97
|
+
}
|
|
98
|
+
export declare const baseValidations: {
|
|
99
|
+
validate(event: BaseEvent): ValidationResult;
|
|
100
|
+
};
|
|
101
|
+
/** Applied to every event, regardless of type */
|
|
102
|
+
export declare const baseRedactions: FieldRedaction[];
|
|
103
|
+
/**
|
|
104
|
+
* Run base validations first, then type-specific validations.
|
|
105
|
+
* Returns the first failure encountered.
|
|
106
|
+
*/
|
|
107
|
+
export declare function validateEvent(event: BaseEvent, schema: EventSchema): ValidationResult;
|
|
108
|
+
/** Convert all values in a metadata object to strings (Chronicle CBN can't extract booleans/integers) */
|
|
109
|
+
export declare function stringifyMetadata(meta: Record<string, any>): Record<string, string | null>;
|
|
110
|
+
export declare function truncate(s: string, max?: number): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* base.ts — Core types, composable blocks, and generic engine for the Shield event model.
|
|
4
|
+
*
|
|
5
|
+
* All event types extend BaseEvent. The engine composes base rules with type-specific rules.
|
|
6
|
+
* Individual event types never redeclare base-level concerns.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.baseRedactions = exports.baseValidations = void 0;
|
|
10
|
+
exports.validateEvent = validateEvent;
|
|
11
|
+
exports.stringifyMetadata = stringifyMetadata;
|
|
12
|
+
exports.truncate = truncate;
|
|
13
|
+
// ─── Base Validations ─────────────────────────────────────────────────────────
|
|
14
|
+
exports.baseValidations = {
|
|
15
|
+
validate(event) {
|
|
16
|
+
if (!event.timestamp)
|
|
17
|
+
return { valid: false, field: 'timestamp', error: 'missing' };
|
|
18
|
+
if (!event.session_id)
|
|
19
|
+
return { valid: false, field: 'session_id', error: 'missing' };
|
|
20
|
+
if (!event.tool_name)
|
|
21
|
+
return { valid: false, field: 'tool_name', error: 'missing' };
|
|
22
|
+
if (!event.tool_category)
|
|
23
|
+
return { valid: false, field: 'tool_category', error: 'missing' };
|
|
24
|
+
if (!event.principal?.hostname)
|
|
25
|
+
return { valid: false, field: 'principal.hostname', error: 'missing' };
|
|
26
|
+
return { valid: true };
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
// ─── Base Redactions ──────────────────────────────────────────────────────────
|
|
30
|
+
/** Applied to every event, regardless of type */
|
|
31
|
+
exports.baseRedactions = [
|
|
32
|
+
{ path: 'principal.hostname', strategy: 'hostname' },
|
|
33
|
+
{ path: 'principal.user', strategy: 'username' },
|
|
34
|
+
];
|
|
35
|
+
// ─── Generic Engine ───────────────────────────────────────────────────────────
|
|
36
|
+
/**
|
|
37
|
+
* Run base validations first, then type-specific validations.
|
|
38
|
+
* Returns the first failure encountered.
|
|
39
|
+
*/
|
|
40
|
+
function validateEvent(event, schema) {
|
|
41
|
+
const baseResult = exports.baseValidations.validate(event);
|
|
42
|
+
if (!baseResult.valid)
|
|
43
|
+
return baseResult;
|
|
44
|
+
return schema.validate(event);
|
|
45
|
+
}
|
|
46
|
+
/** Convert all values in a metadata object to strings (Chronicle CBN can't extract booleans/integers) */
|
|
47
|
+
function stringifyMetadata(meta) {
|
|
48
|
+
const result = {};
|
|
49
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
50
|
+
if (v === null || v === undefined)
|
|
51
|
+
result[k] = null;
|
|
52
|
+
else if (Array.isArray(v))
|
|
53
|
+
result[k] = JSON.stringify(v);
|
|
54
|
+
else
|
|
55
|
+
result[k] = String(v);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
function truncate(s, max = 500) {
|
|
60
|
+
return s && s.length > max ? s.slice(0, max) : s;
|
|
61
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enrich = enrich;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
function enrich(tool, ctx) {
|
|
6
|
+
const args = tool.arguments;
|
|
7
|
+
const url = args.targetUrl || args.url || '';
|
|
8
|
+
const meta = {
|
|
9
|
+
tool_name: 'browser',
|
|
10
|
+
'openclaw.session_id': ctx.sessionId,
|
|
11
|
+
'openclaw.agent_id': ctx.agentId,
|
|
12
|
+
browser_action: args.action || null,
|
|
13
|
+
url_domain: null,
|
|
14
|
+
url_is_internal: false,
|
|
15
|
+
};
|
|
16
|
+
const event = {
|
|
17
|
+
timestamp: ctx.timestamp,
|
|
18
|
+
event_type: 'TOOL_CALL',
|
|
19
|
+
tool_name: 'browser',
|
|
20
|
+
tool_category: 'browser',
|
|
21
|
+
session_id: ctx.sessionId,
|
|
22
|
+
model: ctx.model,
|
|
23
|
+
product_name: 'OpenClaw',
|
|
24
|
+
vendor_name: 'UPX',
|
|
25
|
+
principal: {
|
|
26
|
+
hostname: ctx.source.hostname,
|
|
27
|
+
ip: ctx.source.ip_addresses?.[0] || '',
|
|
28
|
+
platform: ctx.source.os.platform,
|
|
29
|
+
user: ctx.agentId,
|
|
30
|
+
},
|
|
31
|
+
url,
|
|
32
|
+
target: { url },
|
|
33
|
+
tool_metadata: (0, base_1.stringifyMetadata)(meta),
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
const u = new URL(url);
|
|
37
|
+
event.target.hostname = u.hostname;
|
|
38
|
+
event.target.url_domain = u.hostname;
|
|
39
|
+
meta.url_domain = u.hostname;
|
|
40
|
+
meta.url_is_internal = u.hostname === 'localhost' || u.hostname === '127.0.0.1';
|
|
41
|
+
event.network = { session_id: ctx.sessionId, protocol: 'HTTPS', direction: 'OUTBOUND' };
|
|
42
|
+
event.tool_metadata = (0, base_1.stringifyMetadata)(meta);
|
|
43
|
+
}
|
|
44
|
+
catch { /* no valid URL */ }
|
|
45
|
+
return event;
|
|
46
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BrowserSchema = void 0;
|
|
4
|
+
const enrich_1 = require("./enrich");
|
|
5
|
+
const redactions_1 = require("./redactions");
|
|
6
|
+
const validations_1 = require("./validations");
|
|
7
|
+
exports.BrowserSchema = {
|
|
8
|
+
category: 'browser',
|
|
9
|
+
match: (tool) => tool.name === 'browser',
|
|
10
|
+
enrich: enrich_1.enrich,
|
|
11
|
+
redactions: redactions_1.redactions,
|
|
12
|
+
validate: validations_1.validate,
|
|
13
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validate = validate;
|
|
4
|
+
function validate(event) {
|
|
5
|
+
if (!event.url)
|
|
6
|
+
return { valid: false, field: 'url', error: 'missing url' };
|
|
7
|
+
if (!event.target?.url)
|
|
8
|
+
return { valid: false, field: 'target.url', error: 'missing target.url' };
|
|
9
|
+
return { valid: true };
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enrich = enrich;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
function enrich(tool, ctx) {
|
|
6
|
+
const args = tool.arguments;
|
|
7
|
+
const meta = {
|
|
8
|
+
tool_name: 'cron',
|
|
9
|
+
'openclaw.session_id': ctx.sessionId,
|
|
10
|
+
'openclaw.agent_id': ctx.agentId,
|
|
11
|
+
sub_action: args.action || null,
|
|
12
|
+
cron_job_id: args.jobId || args.id || null,
|
|
13
|
+
};
|
|
14
|
+
if (args.job) {
|
|
15
|
+
const payload = args.job.payload || {};
|
|
16
|
+
meta.cron_payload_kind = payload.kind || null;
|
|
17
|
+
meta.cron_session_target = args.job.sessionTarget || null;
|
|
18
|
+
if (payload.message) {
|
|
19
|
+
meta.cron_has_exec_instruction = /\b(exec|run|execute|shell|command|delete|rm|kill)\b/i.test(payload.message);
|
|
20
|
+
meta.cron_has_cred_instruction = /\b(password|credential|secret|key|token|ssh)\b/i.test(payload.message);
|
|
21
|
+
}
|
|
22
|
+
if (payload.text) {
|
|
23
|
+
meta.cron_has_exec_instruction = /\b(exec|run|execute|shell|command|delete|rm|kill)\b/i.test(payload.text);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
timestamp: ctx.timestamp,
|
|
28
|
+
event_type: 'TOOL_CALL',
|
|
29
|
+
tool_name: 'cron',
|
|
30
|
+
tool_category: 'cron',
|
|
31
|
+
session_id: ctx.sessionId,
|
|
32
|
+
model: ctx.model,
|
|
33
|
+
product_name: 'OpenClaw',
|
|
34
|
+
vendor_name: 'UPX',
|
|
35
|
+
principal: {
|
|
36
|
+
hostname: ctx.source.hostname,
|
|
37
|
+
ip: ctx.source.ip_addresses?.[0] || '',
|
|
38
|
+
platform: ctx.source.os.platform,
|
|
39
|
+
user: ctx.agentId,
|
|
40
|
+
},
|
|
41
|
+
arguments_summary: (0, base_1.truncate)(JSON.stringify(args || {})),
|
|
42
|
+
tool_metadata: (0, base_1.stringifyMetadata)(meta),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CronSchema = void 0;
|
|
4
|
+
const enrich_1 = require("./enrich");
|
|
5
|
+
const redactions_1 = require("./redactions");
|
|
6
|
+
const validations_1 = require("./validations");
|
|
7
|
+
exports.CronSchema = {
|
|
8
|
+
category: 'cron',
|
|
9
|
+
match: (tool) => tool.name === 'cron',
|
|
10
|
+
enrich: enrich_1.enrich,
|
|
11
|
+
redactions: redactions_1.redactions,
|
|
12
|
+
validate: validations_1.validate,
|
|
13
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enrich = enrich;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
function enrich(tool, ctx) {
|
|
6
|
+
const args = tool.arguments;
|
|
7
|
+
const cmd = args.command || '';
|
|
8
|
+
const rootCmd = (cmd.split(/\s+/)[0] || '').split('/').pop() || '';
|
|
9
|
+
const meta = {
|
|
10
|
+
tool_name: 'exec',
|
|
11
|
+
'openclaw.session_id': ctx.sessionId,
|
|
12
|
+
'openclaw.agent_id': ctx.agentId,
|
|
13
|
+
cmd_root_command: rootCmd,
|
|
14
|
+
cmd_has_sudo: /\bsudo\b/.test(cmd),
|
|
15
|
+
cmd_has_pipe: /\|/.test(cmd),
|
|
16
|
+
};
|
|
17
|
+
// Process scope escape detection (kill/pkill/killall)
|
|
18
|
+
if (/^(kill|pkill|killall)$/.test(rootCmd)) {
|
|
19
|
+
const pidMatch = cmd.match(/\bkill\s+(?:-\d+\s+)?(\d+)/);
|
|
20
|
+
if (pidMatch) {
|
|
21
|
+
meta.target_pid = pidMatch[1];
|
|
22
|
+
meta.target_pid_in_scope = 'false';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const event = {
|
|
26
|
+
timestamp: ctx.timestamp,
|
|
27
|
+
event_type: 'TOOL_CALL',
|
|
28
|
+
tool_name: 'exec',
|
|
29
|
+
tool_category: 'exec',
|
|
30
|
+
session_id: ctx.sessionId,
|
|
31
|
+
model: ctx.model,
|
|
32
|
+
product_name: 'OpenClaw',
|
|
33
|
+
vendor_name: 'UPX',
|
|
34
|
+
principal: {
|
|
35
|
+
hostname: ctx.source.hostname,
|
|
36
|
+
ip: ctx.source.ip_addresses?.[0] || '',
|
|
37
|
+
platform: ctx.source.os.platform,
|
|
38
|
+
user: ctx.agentId,
|
|
39
|
+
},
|
|
40
|
+
command: (0, base_1.truncate)(cmd),
|
|
41
|
+
workdir: args.workdir || null,
|
|
42
|
+
target: { command_line: (0, base_1.truncate)(cmd) },
|
|
43
|
+
tool_metadata: (0, base_1.stringifyMetadata)(meta),
|
|
44
|
+
};
|
|
45
|
+
// SSH / SCP / rsync detection
|
|
46
|
+
if (/^(ssh|scp|rsync)\b/.test(rootCmd)) {
|
|
47
|
+
const stripped = cmd.replace(/\s-[ipoFlJWbDeLRw]\s+\S+/g, ' ').replace(/\s-[^\s]+/g, ' ');
|
|
48
|
+
const parts = stripped.trim().split(/\s+/).slice(1);
|
|
49
|
+
let targetHost = null;
|
|
50
|
+
for (const p of parts) {
|
|
51
|
+
if (p.includes('@')) {
|
|
52
|
+
targetHost = p.includes(':') ? p.split(':')[0] : p;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!targetHost) {
|
|
57
|
+
for (const p of parts) {
|
|
58
|
+
if (p.startsWith("'") || p.startsWith('"') || p.startsWith('|') || p.startsWith('2>'))
|
|
59
|
+
break;
|
|
60
|
+
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(p)) {
|
|
61
|
+
targetHost = p;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (targetHost) {
|
|
67
|
+
const atIdx = targetHost.indexOf('@');
|
|
68
|
+
const host = atIdx >= 0 ? targetHost.substring(atIdx + 1) : targetHost;
|
|
69
|
+
event.target.hostname = host;
|
|
70
|
+
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host))
|
|
71
|
+
event.target.ip = host;
|
|
72
|
+
event.tool_metadata['cmd_target_host'] = targetHost;
|
|
73
|
+
}
|
|
74
|
+
if (event.target.hostname) {
|
|
75
|
+
event.network = { session_id: ctx.sessionId, protocol: 'SSH', direction: 'OUTBOUND' };
|
|
76
|
+
}
|
|
77
|
+
event.security_result = { severity: 'MEDIUM', summary: 'SSH to external host', category: 'lateral_movement' };
|
|
78
|
+
}
|
|
79
|
+
return event;
|
|
80
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseEvent, NetworkBlock, SecurityResultBlock } from '../base';
|
|
2
|
+
export interface ExecEvent extends BaseEvent, Partial<NetworkBlock>, Partial<SecurityResultBlock> {
|
|
3
|
+
tool_category: 'exec';
|
|
4
|
+
command: string;
|
|
5
|
+
workdir: string | null;
|
|
6
|
+
target: {
|
|
7
|
+
command_line: string;
|
|
8
|
+
hostname?: string;
|
|
9
|
+
ip?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|