@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.

Files changed (159) hide show
  1. package/LICENSE +38 -0
  2. package/README.md +96 -0
  3. package/dist/index.d.ts +43 -0
  4. package/dist/index.js +365 -0
  5. package/dist/src/config.d.ts +43 -0
  6. package/dist/src/config.js +181 -0
  7. package/dist/src/events/base.d.ts +110 -0
  8. package/dist/src/events/base.js +61 -0
  9. package/dist/src/events/browser/enrich.d.ts +3 -0
  10. package/dist/src/events/browser/enrich.js +46 -0
  11. package/dist/src/events/browser/event.d.ts +10 -0
  12. package/dist/src/events/browser/event.js +2 -0
  13. package/dist/src/events/browser/index.d.ts +4 -0
  14. package/dist/src/events/browser/index.js +13 -0
  15. package/dist/src/events/browser/redactions.d.ts +2 -0
  16. package/dist/src/events/browser/redactions.js +4 -0
  17. package/dist/src/events/browser/validations.d.ts +3 -0
  18. package/dist/src/events/browser/validations.js +10 -0
  19. package/dist/src/events/cron/enrich.d.ts +3 -0
  20. package/dist/src/events/cron/enrich.js +44 -0
  21. package/dist/src/events/cron/event.d.ts +5 -0
  22. package/dist/src/events/cron/event.js +2 -0
  23. package/dist/src/events/cron/index.d.ts +4 -0
  24. package/dist/src/events/cron/index.js +13 -0
  25. package/dist/src/events/cron/redactions.d.ts +2 -0
  26. package/dist/src/events/cron/redactions.js +4 -0
  27. package/dist/src/events/cron/validations.d.ts +3 -0
  28. package/dist/src/events/cron/validations.js +4 -0
  29. package/dist/src/events/exec/enrich.d.ts +3 -0
  30. package/dist/src/events/exec/enrich.js +80 -0
  31. package/dist/src/events/exec/event.d.ts +11 -0
  32. package/dist/src/events/exec/event.js +2 -0
  33. package/dist/src/events/exec/index.d.ts +4 -0
  34. package/dist/src/events/exec/index.js +13 -0
  35. package/dist/src/events/exec/redactions.d.ts +3 -0
  36. package/dist/src/events/exec/redactions.js +12 -0
  37. package/dist/src/events/exec/validations.d.ts +3 -0
  38. package/dist/src/events/exec/validations.js +12 -0
  39. package/dist/src/events/file/enrich.d.ts +3 -0
  40. package/dist/src/events/file/enrich.js +63 -0
  41. package/dist/src/events/file/event.d.ts +11 -0
  42. package/dist/src/events/file/event.js +2 -0
  43. package/dist/src/events/file/index.d.ts +4 -0
  44. package/dist/src/events/file/index.js +13 -0
  45. package/dist/src/events/file/redactions.d.ts +2 -0
  46. package/dist/src/events/file/redactions.js +8 -0
  47. package/dist/src/events/file/validations.d.ts +3 -0
  48. package/dist/src/events/file/validations.js +10 -0
  49. package/dist/src/events/gateway/enrich.d.ts +3 -0
  50. package/dist/src/events/gateway/enrich.js +50 -0
  51. package/dist/src/events/gateway/event.d.ts +5 -0
  52. package/dist/src/events/gateway/event.js +2 -0
  53. package/dist/src/events/gateway/index.d.ts +4 -0
  54. package/dist/src/events/gateway/index.js +13 -0
  55. package/dist/src/events/gateway/redactions.d.ts +2 -0
  56. package/dist/src/events/gateway/redactions.js +4 -0
  57. package/dist/src/events/gateway/validations.d.ts +3 -0
  58. package/dist/src/events/gateway/validations.js +4 -0
  59. package/dist/src/events/generic/enrich.d.ts +3 -0
  60. package/dist/src/events/generic/enrich.js +30 -0
  61. package/dist/src/events/generic/event.d.ts +5 -0
  62. package/dist/src/events/generic/event.js +2 -0
  63. package/dist/src/events/generic/index.d.ts +5 -0
  64. package/dist/src/events/generic/index.js +14 -0
  65. package/dist/src/events/generic/redactions.d.ts +2 -0
  66. package/dist/src/events/generic/redactions.js +4 -0
  67. package/dist/src/events/generic/validations.d.ts +3 -0
  68. package/dist/src/events/generic/validations.js +4 -0
  69. package/dist/src/events/host-telemetry/enrich.d.ts +3 -0
  70. package/dist/src/events/host-telemetry/enrich.js +28 -0
  71. package/dist/src/events/host-telemetry/event.d.ts +4 -0
  72. package/dist/src/events/host-telemetry/event.js +2 -0
  73. package/dist/src/events/host-telemetry/index.d.ts +4 -0
  74. package/dist/src/events/host-telemetry/index.js +13 -0
  75. package/dist/src/events/host-telemetry/redactions.d.ts +2 -0
  76. package/dist/src/events/host-telemetry/redactions.js +4 -0
  77. package/dist/src/events/host-telemetry/validations.d.ts +3 -0
  78. package/dist/src/events/host-telemetry/validations.js +4 -0
  79. package/dist/src/events/index.d.ts +40 -0
  80. package/dist/src/events/index.js +39 -0
  81. package/dist/src/events/message/enrich.d.ts +3 -0
  82. package/dist/src/events/message/enrich.js +36 -0
  83. package/dist/src/events/message/event.d.ts +5 -0
  84. package/dist/src/events/message/event.js +2 -0
  85. package/dist/src/events/message/index.d.ts +4 -0
  86. package/dist/src/events/message/index.js +13 -0
  87. package/dist/src/events/message/redactions.d.ts +2 -0
  88. package/dist/src/events/message/redactions.js +4 -0
  89. package/dist/src/events/message/validations.d.ts +3 -0
  90. package/dist/src/events/message/validations.js +7 -0
  91. package/dist/src/events/sessions-spawn/enrich.d.ts +3 -0
  92. package/dist/src/events/sessions-spawn/enrich.js +40 -0
  93. package/dist/src/events/sessions-spawn/event.d.ts +9 -0
  94. package/dist/src/events/sessions-spawn/event.js +2 -0
  95. package/dist/src/events/sessions-spawn/index.d.ts +4 -0
  96. package/dist/src/events/sessions-spawn/index.js +13 -0
  97. package/dist/src/events/sessions-spawn/redactions.d.ts +2 -0
  98. package/dist/src/events/sessions-spawn/redactions.js +4 -0
  99. package/dist/src/events/sessions-spawn/validations.d.ts +3 -0
  100. package/dist/src/events/sessions-spawn/validations.js +4 -0
  101. package/dist/src/events/tool-result/enrich.d.ts +13 -0
  102. package/dist/src/events/tool-result/enrich.js +46 -0
  103. package/dist/src/events/tool-result/event.d.ts +7 -0
  104. package/dist/src/events/tool-result/event.js +2 -0
  105. package/dist/src/events/tool-result/index.d.ts +4 -0
  106. package/dist/src/events/tool-result/index.js +9 -0
  107. package/dist/src/events/tool-result/redactions.d.ts +2 -0
  108. package/dist/src/events/tool-result/redactions.js +7 -0
  109. package/dist/src/events/tool-result/validations.d.ts +3 -0
  110. package/dist/src/events/tool-result/validations.js +9 -0
  111. package/dist/src/events/web/enrich.d.ts +8 -0
  112. package/dist/src/events/web/enrich.js +78 -0
  113. package/dist/src/events/web/event.d.ts +10 -0
  114. package/dist/src/events/web/event.js +2 -0
  115. package/dist/src/events/web/index.d.ts +4 -0
  116. package/dist/src/events/web/index.js +13 -0
  117. package/dist/src/events/web/redactions.d.ts +2 -0
  118. package/dist/src/events/web/redactions.js +6 -0
  119. package/dist/src/events/web/validations.d.ts +3 -0
  120. package/dist/src/events/web/validations.js +10 -0
  121. package/dist/src/fetcher.d.ts +12 -0
  122. package/dist/src/fetcher.js +182 -0
  123. package/dist/src/host-collector.d.ts +1 -0
  124. package/dist/src/host-collector.js +200 -0
  125. package/dist/src/index.d.ts +1 -0
  126. package/dist/src/index.js +210 -0
  127. package/dist/src/log.d.ts +39 -0
  128. package/dist/src/log.js +102 -0
  129. package/dist/src/redactor/base.d.ts +29 -0
  130. package/dist/src/redactor/base.js +9 -0
  131. package/dist/src/redactor/index.d.ts +27 -0
  132. package/dist/src/redactor/index.js +109 -0
  133. package/dist/src/redactor/strategies/command.d.ts +2 -0
  134. package/dist/src/redactor/strategies/command.js +19 -0
  135. package/dist/src/redactor/strategies/hostname.d.ts +2 -0
  136. package/dist/src/redactor/strategies/hostname.js +15 -0
  137. package/dist/src/redactor/strategies/index.d.ts +13 -0
  138. package/dist/src/redactor/strategies/index.js +25 -0
  139. package/dist/src/redactor/strategies/path.d.ts +2 -0
  140. package/dist/src/redactor/strategies/path.js +23 -0
  141. package/dist/src/redactor/strategies/secret-key.d.ts +2 -0
  142. package/dist/src/redactor/strategies/secret-key.js +22 -0
  143. package/dist/src/redactor/strategies/username.d.ts +2 -0
  144. package/dist/src/redactor/strategies/username.js +12 -0
  145. package/dist/src/redactor/vault.d.ts +25 -0
  146. package/dist/src/redactor/vault.js +209 -0
  147. package/dist/src/sender.d.ts +29 -0
  148. package/dist/src/sender.js +186 -0
  149. package/dist/src/setup.d.ts +10 -0
  150. package/dist/src/setup.js +222 -0
  151. package/dist/src/transformer.d.ts +26 -0
  152. package/dist/src/transformer.js +302 -0
  153. package/dist/src/validator.d.ts +17 -0
  154. package/dist/src/validator.js +110 -0
  155. package/dist/src/version.d.ts +1 -0
  156. package/dist/src/version.js +19 -0
  157. package/openclaw.plugin.json +52 -0
  158. package/package.json +64 -0
  159. package/skills/shield/SKILL.md +38 -0
@@ -0,0 +1,302 @@
1
+ "use strict";
2
+ /**
3
+ * transformer.ts — Thin orchestration layer
4
+ *
5
+ * Iterates raw JSONL entries, routes each tool call to the matching schema,
6
+ * and wraps the resulting ShieldEvent in an EnvelopeEvent for transmission.
7
+ *
8
+ * All enrichment logic lives in src/events/{type}/enrich.ts.
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.resolveOpenClawVersion = resolveOpenClawVersion;
45
+ exports.resolveAgentLabel = resolveAgentLabel;
46
+ exports.transformEntries = transformEntries;
47
+ exports.generateHostTelemetry = generateHostTelemetry;
48
+ const os = __importStar(require("os"));
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const https = __importStar(require("https"));
52
+ const events_1 = require("./events");
53
+ const log = __importStar(require("./log"));
54
+ const version_1 = require("./version");
55
+ // ─── OpenClaw info resolution ─────────────────────────────────────────────────
56
+ /** Resolve the installed OpenClaw version from known package.json paths. */
57
+ function resolveOpenClawVersion() {
58
+ if (process.env.OPENCLAW_VERSION)
59
+ return process.env.OPENCLAW_VERSION;
60
+ const candidates = [
61
+ '/opt/homebrew/lib/node_modules/openclaw/package.json',
62
+ '/usr/local/lib/node_modules/openclaw/package.json',
63
+ path.join(os.homedir(), '.nvm/versions/node/*/lib/node_modules/openclaw/package.json'),
64
+ ];
65
+ for (const p of candidates) {
66
+ try {
67
+ if (fs.existsSync(p)) {
68
+ const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
69
+ if (pkg.version)
70
+ return pkg.version;
71
+ }
72
+ }
73
+ catch { /* skip */ }
74
+ }
75
+ // Fallback: read lastTouchedVersion from openclaw.json
76
+ try {
77
+ const cfg = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.openclaw/openclaw.json'), 'utf8'));
78
+ return cfg?.meta?.lastTouchedVersion || 'unknown';
79
+ }
80
+ catch {
81
+ return 'unknown';
82
+ }
83
+ }
84
+ /** Resolve agent_label from IDENTITY.md in the configured workspace. */
85
+ function resolveAgentLabel(agentId) {
86
+ if (process.env.OPENCLAW_AGENT_LABEL)
87
+ return process.env.OPENCLAW_AGENT_LABEL;
88
+ try {
89
+ const cfgPath = path.join(os.homedir(), '.openclaw/openclaw.json');
90
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
91
+ const workspace = cfg?.agents?.defaults?.workspace || path.join(os.homedir(), '.openclaw/workspace');
92
+ const identityPath = path.join(workspace, 'IDENTITY.md');
93
+ if (fs.existsSync(identityPath)) {
94
+ const content = fs.readFileSync(identityPath, 'utf8');
95
+ const match = content.match(/^\s*-\s*\*\*Name:\*\*\s*(.+)$/m);
96
+ if (match)
97
+ return match[1].trim();
98
+ }
99
+ }
100
+ catch { /* skip */ }
101
+ return agentId;
102
+ }
103
+ /** Fetch the public (egress) IP from ipify.org — resolves once at startup. */
104
+ function fetchPublicIp() {
105
+ return new Promise((resolve) => {
106
+ const req = https.get('https://api.ipify.org?format=json', { timeout: 5000 }, (res) => {
107
+ let data = '';
108
+ res.on('data', (chunk) => { data += chunk; });
109
+ res.on('end', () => {
110
+ try {
111
+ resolve(JSON.parse(data).ip || null);
112
+ }
113
+ catch {
114
+ resolve(null);
115
+ }
116
+ });
117
+ });
118
+ req.on('error', () => resolve(null));
119
+ req.on('timeout', () => { req.destroy(); resolve(null); });
120
+ });
121
+ }
122
+ // ─── Source Info (cached, async-enriched) ────────────────────────────────────
123
+ let _source = null;
124
+ /** Kick off public IP resolution at module load (non-blocking). */
125
+ function initPublicIp() {
126
+ fetchPublicIp().then((ip) => {
127
+ if (ip && _source) {
128
+ // Inject public IP as first entry so SIEM sees it
129
+ const alreadyHas = _source.ip_addresses.includes(ip);
130
+ if (!alreadyHas)
131
+ _source.ip_addresses = [ip, ..._source.ip_addresses];
132
+ log.info('transformer', `Public IP resolved: ${ip}`);
133
+ }
134
+ return ip;
135
+ });
136
+ }
137
+ initPublicIp();
138
+ function getSourceInfo() {
139
+ if (_source)
140
+ return _source;
141
+ const agentId = process.env.OPENCLAW_AGENT_ID || 'main';
142
+ _source = {
143
+ hostname: os.hostname(),
144
+ ip_addresses: Object.values(os.networkInterfaces())
145
+ .flat()
146
+ .filter((i) => i && i.family === 'IPv4' && !i.internal)
147
+ .map((i) => i.address),
148
+ os: {
149
+ type: os.type(),
150
+ platform: os.platform() === 'darwin' ? 'MAC' : os.platform() === 'linux' ? 'LINUX' : 'WINDOWS',
151
+ release: os.release(),
152
+ arch: os.arch(),
153
+ },
154
+ openclaw: {
155
+ version: resolveOpenClawVersion(),
156
+ agent_id: agentId,
157
+ agent_label: resolveAgentLabel(agentId),
158
+ },
159
+ plugin: { version: version_1.VERSION, transport: 'openclaw_plugin' },
160
+ };
161
+ return _source;
162
+ }
163
+ // ─── Administrative Detection ─────────────────────────────────────────────────
164
+ /** Detect if an event is administrative Shield activity (prevents meta-detection) */
165
+ function isAdministrativeEvent(toolName, args, sessionId) {
166
+ if (sessionId && /subagent/i.test(sessionId))
167
+ return true;
168
+ if (toolName === 'exec') {
169
+ const cmd = args.command || '';
170
+ if (/tools\/(deploy-rules|sync-rules|audit-meta|validate-rules|fix-meta)/.test(cmd))
171
+ return true;
172
+ if (/openclaw\s+cron\s+(list|log|runs|status)/.test(cmd))
173
+ return true;
174
+ if (/ps\s+aux.*grep.*(ts-node|bridge|shield)/.test(cmd))
175
+ return true;
176
+ if (/gcloud\s+auth\s+print-access-token/.test(cmd))
177
+ return true;
178
+ }
179
+ if (['read', 'write', 'edit'].includes(toolName)) {
180
+ const fp = args.file_path || args.path || args.filePath || '';
181
+ if (/openclaw-shield\/(rules|tools|playbooks|docs)\//.test(fp))
182
+ return true;
183
+ if (/team-dashboard\/data\/detection-rules\.json/.test(fp))
184
+ return true;
185
+ if (/memory\/squad-audit/.test(fp))
186
+ return true;
187
+ }
188
+ if (toolName === 'cron' && ['list', 'status', 'runs'].includes(args.action))
189
+ return true;
190
+ if (['sessions_list', 'sessions_history', 'session_status'].includes(toolName))
191
+ return true;
192
+ return false;
193
+ }
194
+ // ─── Main Transform ───────────────────────────────────────────────────────────
195
+ function transformEntries(entries) {
196
+ const baseSource = getSourceInfo();
197
+ const envelopes = [];
198
+ for (const entry of entries) {
199
+ const msg = entry.message;
200
+ const agentId = entry._agentId || baseSource.openclaw.agent_id;
201
+ const source = {
202
+ ...baseSource,
203
+ openclaw: { ...baseSource.openclaw, agent_id: agentId },
204
+ };
205
+ // ── TOOL_CALL ──
206
+ if (msg.role === 'assistant' && Array.isArray(msg.content)) {
207
+ const model = msg.model ? `${msg.provider || ''}/${msg.model}`.replace(/^\//, '') : '';
208
+ for (const item of msg.content) {
209
+ if (item.type !== 'toolCall')
210
+ continue;
211
+ const toolName = item.name || 'unknown';
212
+ const args = item.arguments || {};
213
+ // Route to matching schema (first match wins; GenericSchema is last fallback)
214
+ const schema = events_1.schemas.find(s => s.match({ name: toolName, id: item.id, arguments: args }));
215
+ if (!schema)
216
+ continue; // should never happen since GenericSchema matches everything
217
+ const event = schema.enrich({ name: toolName, id: item.id, arguments: args }, { sessionId: entry._sessionId, agentId, timestamp: entry.timestamp, model, source });
218
+ // Inject administrative flag post-enrichment (cross-cutting concern)
219
+ if (isAdministrativeEvent(toolName, args, entry._sessionId)) {
220
+ if (!event.tool_metadata)
221
+ event.tool_metadata = {};
222
+ event.tool_metadata['openclaw.is_administrative'] = 'true';
223
+ }
224
+ log.debug('transformer', `TOOL_CALL tool=${toolName} session=${entry._sessionId} agent=${agentId} schema=${schema.constructor?.name || 'unknown'} admin=${event.tool_metadata?.['openclaw.is_administrative'] === 'true'}`, log.isDebug ? event : undefined);
225
+ envelopes.push({ source, event });
226
+ }
227
+ }
228
+ // ── TOOL_RESULT ──
229
+ else if (msg.role === 'toolResult') {
230
+ const event = (0, events_1.buildToolResult)({ toolName: msg.toolName, content: msg.content, details: msg.details }, { sessionId: entry._sessionId, agentId, timestamp: entry.timestamp, source });
231
+ if (isAdministrativeEvent(event.tool_name, {}, entry._sessionId)) {
232
+ if (!event.tool_metadata)
233
+ event.tool_metadata = {};
234
+ event.tool_metadata['openclaw.is_administrative'] = 'true';
235
+ }
236
+ log.debug('transformer', `TOOL_RESULT tool=${event.tool_name} session=${entry._sessionId} agent=${agentId}`, log.isDebug ? event : undefined);
237
+ envelopes.push({ source, event });
238
+ }
239
+ }
240
+ log.info('transformer', `${envelopes.length} envelopes from ${entries.length} entries`);
241
+ return envelopes;
242
+ }
243
+ // ─── Host Telemetry ───────────────────────────────────────────────────────────
244
+ /** Generate a HOST_TELEMETRY envelope with version + config info for security rules */
245
+ function generateHostTelemetry() {
246
+ const source = getSourceInfo();
247
+ const version = source.openclaw.version;
248
+ const versionSortable = version.split('.').map((p, i) => i > 0 ? p.padStart(2, '0') : p).join('.');
249
+ let gatewayBind = '127.0.0.1';
250
+ let webhookUrl = null;
251
+ let webhookConfigured = 'false';
252
+ let webhookHasSecret = 'false';
253
+ let browserAuthRequired = 'false';
254
+ let gatewayUrl = null;
255
+ try {
256
+ const fs = require('fs');
257
+ const home = require('os').homedir();
258
+ const configPath = `${home}/.openclaw/openclaw.json`;
259
+ if (fs.existsSync(configPath)) {
260
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
261
+ gatewayBind = config.gateway?.host || config.host || '127.0.0.1';
262
+ gatewayUrl = config.gateway?.url || null;
263
+ const wh = config.webhooks || config.webhook;
264
+ if (wh) {
265
+ webhookConfigured = 'true';
266
+ webhookUrl = typeof wh === 'string' ? wh : wh.url || wh.endpoint || null;
267
+ webhookHasSecret = (wh.secret || wh.hmac_secret || wh.signing_secret) ? 'true' : 'false';
268
+ }
269
+ if (config.browser?.requireAuth || config.browser?.password) {
270
+ browserAuthRequired = 'true';
271
+ }
272
+ }
273
+ }
274
+ catch { /* default values */ }
275
+ const event = {
276
+ timestamp: new Date().toISOString(),
277
+ event_type: 'TOOL_CALL',
278
+ tool_name: 'host_telemetry',
279
+ tool_category: 'host_telemetry',
280
+ session_id: 'telemetry',
281
+ product_name: 'OpenClaw',
282
+ vendor_name: 'UPX',
283
+ principal: {
284
+ hostname: source.hostname,
285
+ ip: source.ip_addresses?.[0] || '',
286
+ platform: source.os.platform,
287
+ user: source.openclaw.agent_id,
288
+ },
289
+ tool_metadata: {
290
+ tool_name: 'host_telemetry',
291
+ 'openclaw.version': version,
292
+ 'openclaw.version_sortable': versionSortable,
293
+ 'openclaw.gateway_bind': gatewayBind,
294
+ 'openclaw.gateway_url': gatewayUrl,
295
+ 'openclaw.webhook_url': webhookUrl,
296
+ 'openclaw.webhook_configured': webhookConfigured,
297
+ 'openclaw.webhook_has_secret': webhookHasSecret,
298
+ 'openclaw.browser_auth_required': browserAuthRequired,
299
+ },
300
+ };
301
+ return { source, event };
302
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * validator.ts — Event validation with quarantine
3
+ *
4
+ * Validates events using the schema registry. Events that fail validation are
5
+ * written to a local quarantine file and never enter the redaction/send path.
6
+ * This is the tamper-prevention gate — malformed or unexpected events don't leave the machine.
7
+ */
8
+ import type { ShieldEvent } from './events';
9
+ export declare const QUARANTINE_FILE: string;
10
+ /**
11
+ * Validate a batch of events.
12
+ * Returns the valid events and the count of quarantined ones.
13
+ */
14
+ export declare function validate(events: ShieldEvent[]): {
15
+ valid: ShieldEvent[];
16
+ quarantined: number;
17
+ };
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * validator.ts — Event validation with quarantine
4
+ *
5
+ * Validates events using the schema registry. Events that fail validation are
6
+ * written to a local quarantine file and never enter the redaction/send path.
7
+ * This is the tamper-prevention gate — malformed or unexpected events don't leave the machine.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.QUARANTINE_FILE = void 0;
44
+ exports.validate = validate;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const os = __importStar(require("os"));
48
+ const events_1 = require("./events");
49
+ const base_1 = require("./events/base");
50
+ const log = __importStar(require("./log"));
51
+ const SHIELD_DATA_DIR = path.join(os.homedir(), '.openclaw', 'shield', 'data');
52
+ exports.QUARANTINE_FILE = path.join(SHIELD_DATA_DIR, 'quarantine.jsonl');
53
+ function ensureDataDir() {
54
+ fs.mkdirSync(SHIELD_DATA_DIR, { recursive: true });
55
+ }
56
+ function appendQuarantine(event, error, field) {
57
+ try {
58
+ ensureDataDir();
59
+ const record = JSON.stringify({
60
+ quarantined_at: new Date().toISOString(),
61
+ validation_error: error,
62
+ validation_field: field || null,
63
+ event,
64
+ });
65
+ fs.appendFileSync(exports.QUARANTINE_FILE, record + '\n');
66
+ }
67
+ catch (e) {
68
+ // Quarantine write failure is non-fatal
69
+ console.error('[validator] Failed to write quarantine record:', e);
70
+ }
71
+ }
72
+ /**
73
+ * Validate a batch of events.
74
+ * Returns the valid events and the count of quarantined ones.
75
+ */
76
+ function validate(events) {
77
+ const valid = [];
78
+ let quarantined = 0;
79
+ for (const event of events) {
80
+ // Find the matching schema by tool_category
81
+ const schema = events_1.schemas.find(s => s.category === event.tool_category);
82
+ if (!schema) {
83
+ // No schema found — run base validations only and pass through
84
+ const baseResult = base_1.baseValidations.validate(event);
85
+ if (baseResult.valid) {
86
+ valid.push(event);
87
+ }
88
+ else {
89
+ quarantined++;
90
+ appendQuarantine(event, baseResult.error || 'validation_failed', baseResult.field);
91
+ }
92
+ continue;
93
+ }
94
+ const result = (0, base_1.validateEvent)(event, schema);
95
+ if (result.valid) {
96
+ log.debug('validator', `PASS tool=${event.tool_name} category=${event.tool_category}`);
97
+ valid.push(event);
98
+ }
99
+ else {
100
+ quarantined++;
101
+ appendQuarantine(event, result.error || 'validation_failed', result.field);
102
+ log.warn('validator', `QUARANTINE tool=${event.tool_name} field=${result.field} error=${result.error}`);
103
+ log.debug('validator', 'quarantined event', event);
104
+ }
105
+ }
106
+ if (quarantined > 0 || log.isDebug) {
107
+ log.info('validator', `${valid.length} valid, ${quarantined} quarantined`);
108
+ }
109
+ return { valid, quarantined };
110
+ }
@@ -0,0 +1 @@
1
+ export declare const VERSION: string;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VERSION = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ // Works from both src/ (dev) and dist/src/ (compiled)
7
+ function loadVersion() {
8
+ for (const rel of ['../package.json', '../../package.json']) {
9
+ const p = (0, path_1.join)(__dirname, rel);
10
+ if ((0, fs_1.existsSync)(p)) {
11
+ try {
12
+ return JSON.parse((0, fs_1.readFileSync)(p, 'utf-8')).version ?? '0.0.0';
13
+ }
14
+ catch { /* next */ }
15
+ }
16
+ }
17
+ return '0.0.0';
18
+ }
19
+ exports.VERSION = loadVersion();
@@ -0,0 +1,52 @@
1
+ {
2
+ "id": "shield",
3
+ "name": "OpenClaw Shield",
4
+ "description": "Real-time security monitoring — streams enriched, redacted security events to the Shield detection platform.",
5
+ "version": "0.2.12-beta",
6
+ "skills": [
7
+ "./skills"
8
+ ],
9
+ "configSchema": {
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "properties": {
13
+ "enabled": {
14
+ "type": "boolean",
15
+ "default": true
16
+ },
17
+ "dryRun": {
18
+ "type": "boolean",
19
+ "default": false
20
+ },
21
+ "redactionEnabled": {
22
+ "type": "boolean",
23
+ "default": true
24
+ },
25
+ "pollIntervalMs": {
26
+ "type": "number",
27
+ "default": 30000
28
+ },
29
+ "collectHostMetrics": {
30
+ "type": "boolean",
31
+ "default": false
32
+ }
33
+ }
34
+ },
35
+ "uiHints": {
36
+ "enabled": {
37
+ "label": "Enable security monitoring"
38
+ },
39
+ "dryRun": {
40
+ "label": "Dry run (log events locally, do not transmit)"
41
+ },
42
+ "redactionEnabled": {
43
+ "label": "Redact sensitive values before transmitting"
44
+ },
45
+ "pollIntervalMs": {
46
+ "label": "Polling interval (milliseconds)"
47
+ },
48
+ "collectHostMetrics": {
49
+ "label": "Collect host telemetry metrics"
50
+ }
51
+ }
52
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@upx-us/shield",
3
+ "version": "0.2.12-beta",
4
+ "description": "Security monitoring plugin for OpenClaw agents — streams enriched security events to the Shield detection platform",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "shield-bridge": "dist/src/index.js",
9
+ "shield-setup": "dist/src/setup.js"
10
+ },
11
+ "files": [
12
+ "dist/index.js",
13
+ "dist/index.d.ts",
14
+ "dist/src/",
15
+ "openclaw.plugin.json",
16
+ "skills/",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "prebuild": "npm run clean",
22
+ "build": "tsc",
23
+ "start": "node dist/src/index.js",
24
+ "setup": "node dist/src/setup.js",
25
+ "lint": "tsc --noEmit",
26
+ "test": "node --require tsx/cjs --test --test-reporter spec tests/**/*.test.ts tests/*.test.ts",
27
+ "test:watch": "node --require tsx/cjs --test --watch tests/**/*.test.ts tests/*.test.ts",
28
+ "test:parser": "node tests/run-parser.js",
29
+ "test:parser:short": "node tests/run-parser.js --short",
30
+ "test:parser:verbose": "node tests/run-parser.js --verbose",
31
+ "test:parser:help": "node tests/run-parser.js help",
32
+ "dev": "tsx scripts/dev-harness.ts",
33
+ "dev:dry": "tsx scripts/dev-harness.ts --dry-run",
34
+ "generate:schemas": "tsx scripts/generate-schemas.ts",
35
+ "prepublishOnly": "npm run prepublish:check && npm run build && npm run generate:schemas",
36
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
37
+ "prepublish:check": "node scripts/prepublish-check.js"
38
+ },
39
+ "keywords": [
40
+ "openclaw",
41
+ "openclaw-plugin",
42
+ "security",
43
+ "monitoring",
44
+ "detection",
45
+ "siem",
46
+ "compliance"
47
+ ],
48
+ "author": "UPX Security Services",
49
+ "license": "SEE LICENSE IN LICENSE",
50
+ "engines": {
51
+ "node": ">=20.0.0"
52
+ },
53
+ "openclaw": {
54
+ "extensions": [
55
+ "./dist/index.js"
56
+ ]
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "^25.2.3",
60
+ "ts-json-schema-generator": "^2.5.0",
61
+ "tsx": "^4.21.0",
62
+ "typescript": "^5.9.3"
63
+ }
64
+ }
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: shield
3
+ description: OpenClaw Shield security monitoring plugin — check status, manage configuration, and review security event activity.
4
+ metadata: {"openclaw": {"requires": {"env": []}, "emoji": "🛡️", "os": ["darwin", "linux", "windows"]}}
5
+ ---
6
+
7
+ OpenClaw Shield monitors this agent's activity and streams security events to the Shield detection platform for threat detection and compliance.
8
+
9
+ ## Check status
10
+
11
+ ```bash
12
+ openclaw shield status
13
+ ```
14
+
15
+ Shows: running state, last poll time, events processed, quarantine count, failure count, version.
16
+
17
+ ## Commands
18
+
19
+ | Command | Description |
20
+ |---------|-------------|
21
+ | `openclaw shield status` | Show monitoring status |
22
+ | `openclaw shield flush` | Force an immediate sync |
23
+
24
+ ## First-time setup
25
+
26
+ If Shield is not yet activated, run:
27
+
28
+ ```bash
29
+ npx -p @upx-us/shield@beta shield-setup
30
+ ```
31
+
32
+ The wizard will ask for an **installation key** (provided by your Shield administrator). It registers the instance and saves credentials locally. Restart the Gateway when done.
33
+
34
+ ## Troubleshooting
35
+
36
+ - **Not running**: check `openclaw shield status` for failure count and last poll time
37
+ - **High failure count**: Shield backs off automatically; run `openclaw shield flush` to retry immediately
38
+ - **Setup required**: if credentials are missing, re-run `npx -p @upx-us/shield@beta shield-setup` with a valid installation key