@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,200 @@
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.collectHostMetrics = collectHostMetrics;
37
+ const child_process_1 = require("child_process");
38
+ const os = __importStar(require("os"));
39
+ function run(cmd) {
40
+ try {
41
+ return (0, child_process_1.execSync)(cmd, { timeout: 10000, encoding: 'utf-8' }).trim();
42
+ }
43
+ catch {
44
+ return '';
45
+ }
46
+ }
47
+ async function collectCpu() {
48
+ try {
49
+ const top = run('top -l 1 -n 0');
50
+ const m = top.match(/CPU usage:\s+([\d.]+)%\s+user,\s+([\d.]+)%\s+sys,\s+([\d.]+)%\s+idle/);
51
+ const idle = m ? parseFloat(m[3]) : null;
52
+ const usage = idle !== null ? Math.round((100 - idle) * 100) / 100 : null;
53
+ const loads = os.loadavg();
54
+ return {
55
+ usage,
56
+ load1: Math.round(loads[0] * 100) / 100,
57
+ load5: Math.round(loads[1] * 100) / 100,
58
+ load15: Math.round(loads[2] * 100) / 100,
59
+ };
60
+ }
61
+ catch {
62
+ return { usage: null, load1: null, load5: null, load15: null };
63
+ }
64
+ }
65
+ async function collectMemory() {
66
+ try {
67
+ const totalBytes = os.totalmem();
68
+ const freeBytes = os.freemem();
69
+ const totalMb = Math.round(totalBytes / 1024 / 1024);
70
+ const usedMb = Math.round((totalBytes - freeBytes) / 1024 / 1024);
71
+ const usagePercent = Math.round((usedMb / totalMb) * 10000) / 100;
72
+ return { totalMb, usedMb, usagePercent };
73
+ }
74
+ catch {
75
+ return { totalMb: null, usedMb: null, usagePercent: null };
76
+ }
77
+ }
78
+ async function collectDisk() {
79
+ try {
80
+ const df = run('df -g /');
81
+ const lines = df.split('\n');
82
+ if (lines.length < 2)
83
+ return { totalGb: null, usedGb: null, usagePercent: null };
84
+ const parts = lines[1].split(/\s+/);
85
+ const totalGb = parseInt(parts[1]);
86
+ const usedGb = parseInt(parts[2]);
87
+ const usagePercent = Math.round((usedGb / totalGb) * 10000) / 100;
88
+ return { totalGb, usedGb, usagePercent };
89
+ }
90
+ catch {
91
+ return { totalGb: null, usedGb: null, usagePercent: null };
92
+ }
93
+ }
94
+ async function collectUptime() {
95
+ try {
96
+ return Math.round(os.uptime());
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ async function collectNetwork() {
103
+ try {
104
+ const connCount = parseInt(run("netstat -an | grep ESTABLISHED | wc -l").trim()) || 0;
105
+ const lsof = run("lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null");
106
+ const ports = new Set();
107
+ for (const line of lsof.split('\n').slice(1)) {
108
+ const m = line.match(/:(\d+)\s/);
109
+ if (m)
110
+ ports.add(m[1]);
111
+ }
112
+ return {
113
+ connections: connCount,
114
+ listeningPorts: ports.size > 0 ? Array.from(ports).sort((a, b) => parseInt(a) - parseInt(b)).join(',') : null,
115
+ };
116
+ }
117
+ catch {
118
+ return { connections: null, listeningPorts: null };
119
+ }
120
+ }
121
+ async function collectTopProcesses() {
122
+ try {
123
+ const ps = run("ps aux -r | head -6");
124
+ const lines = ps.split('\n').slice(1); // skip header
125
+ const procs = lines.map(l => {
126
+ const parts = l.split(/\s+/);
127
+ return `${parts[10] || parts[parts.length - 1]}(${parts[2]}%)`;
128
+ });
129
+ return procs.join(', ') || null;
130
+ }
131
+ catch {
132
+ return null;
133
+ }
134
+ }
135
+ async function collectOpenClawStatus() {
136
+ try {
137
+ const out = run("pgrep -f 'openclaw' | wc -l");
138
+ const count = parseInt(out.trim()) || 0;
139
+ return {
140
+ status: count > 0 ? 'running' : 'stopped',
141
+ sessions: null, // would need API access
142
+ };
143
+ }
144
+ catch {
145
+ return { status: null, sessions: null };
146
+ }
147
+ }
148
+ async function collectHostMetrics() {
149
+ const cpu = await collectCpu();
150
+ const mem = await collectMemory();
151
+ const disk = await collectDisk();
152
+ const uptimeSec = await collectUptime();
153
+ const net = await collectNetwork();
154
+ const procs = await collectTopProcesses();
155
+ const oc = await collectOpenClawStatus();
156
+ const anomalies = [];
157
+ if (cpu.usage !== null && cpu.usage > 90)
158
+ anomalies.push(`CPU=${cpu.usage}%`);
159
+ if (mem.usagePercent !== null && mem.usagePercent > 90)
160
+ anomalies.push(`Memory=${mem.usagePercent}%`);
161
+ if (disk.usagePercent !== null && disk.usagePercent > 90)
162
+ anomalies.push(`Disk=${disk.usagePercent}%`);
163
+ const event = {
164
+ timestamp: new Date().toISOString(),
165
+ event_type: 'TOOL_CALL',
166
+ tool_name: 'host_telemetry',
167
+ session_id: 'host-monitor',
168
+ model: '',
169
+ hostname: os.hostname(),
170
+ product_name: 'OpenClaw',
171
+ vendor_name: 'UPX',
172
+ event_category: 'HOST_METRIC',
173
+ metric_type: 'system_snapshot',
174
+ cpu_usage_percent: cpu.usage,
175
+ cpu_load_1m: cpu.load1,
176
+ cpu_load_5m: cpu.load5,
177
+ cpu_load_15m: cpu.load15,
178
+ memory_total_mb: mem.totalMb,
179
+ memory_used_mb: mem.usedMb,
180
+ memory_usage_percent: mem.usagePercent,
181
+ disk_total_gb: disk.totalGb,
182
+ disk_used_gb: disk.usedGb,
183
+ disk_usage_percent: disk.usagePercent,
184
+ uptime_seconds: uptimeSec,
185
+ network_connections_count: net.connections,
186
+ network_listening_ports: net.listeningPorts,
187
+ top_processes: procs,
188
+ openclaw_gateway_status: oc.status,
189
+ openclaw_active_sessions: oc.sessions,
190
+ os_name: os.platform(),
191
+ os_version: os.release(),
192
+ arch: os.arch(),
193
+ tool_metadata: {
194
+ collection_method: 'shell_exec',
195
+ is_anomaly: anomalies.length > 0,
196
+ anomaly_details: anomalies.length > 0 ? anomalies.join('; ') : null,
197
+ },
198
+ };
199
+ return [event];
200
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,210 @@
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
+ const config_1 = require("./config");
37
+ const log = __importStar(require("./log"));
38
+ const fetcher_1 = require("./fetcher");
39
+ const transformer_1 = require("./transformer");
40
+ const sender_1 = require("./sender");
41
+ const redactor_1 = require("./redactor");
42
+ const validator_1 = require("./validator");
43
+ const fs_1 = require("fs");
44
+ const version_1 = require("./version");
45
+ let running = true;
46
+ let lastTelemetryAt = 0;
47
+ let consecutiveFailures = 0;
48
+ const TELEMETRY_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
49
+ const MAX_BACKOFF_MS = 5 * 60 * 1000; // 5 minutes
50
+ function getBackoffInterval(baseMs) {
51
+ if (consecutiveFailures === 0)
52
+ return baseMs;
53
+ const backoff = baseMs * Math.pow(2, Math.min(consecutiveFailures, 10));
54
+ return Math.min(backoff, MAX_BACKOFF_MS);
55
+ }
56
+ async function poll() {
57
+ const config = (0, config_1.loadConfig)();
58
+ if (config.redactionEnabled) {
59
+ (0, redactor_1.init)();
60
+ }
61
+ log.info('bridge', `Starting — dryRun=${config.dryRun} poll=${config.pollIntervalMs}ms maxEvents=${config.maxEvents || 'unlimited'} redaction=${config.redactionEnabled} logLevel=${process.env.LOG_LEVEL || 'info'}`);
62
+ while (running) {
63
+ try {
64
+ let entries = await (0, fetcher_1.fetchNewEntries)(config);
65
+ const now = Date.now();
66
+ if (now - lastTelemetryAt >= TELEMETRY_INTERVAL_MS) {
67
+ // Host telemetry is no longer forwarded to Chronicle/SIEM.
68
+ // It is summarised server-side via the instance report sent to the
69
+ // platform backend (Cloud Run → uss.upx.com). This avoids noisy
70
+ // GENERIC_EVENT floods that trigger false-positive YARA-L rules.
71
+ (0, transformer_1.generateHostTelemetry)(); // still collect locally (for score / future use)
72
+ const instancePayload = {
73
+ machine: {
74
+ hostname: config.hostname,
75
+ os: process.platform,
76
+ arch: process.arch,
77
+ node_version: process.version,
78
+ },
79
+ software: {
80
+ plugin_version: version_1.VERSION,
81
+ openclaw_version: (0, transformer_1.resolveOpenClawVersion)(),
82
+ agent_label: (0, transformer_1.resolveAgentLabel)('main'),
83
+ },
84
+ };
85
+ const platformOk = await (0, sender_1.reportInstance)(instancePayload, config.credentials);
86
+ log.info('bridge', `Instance report → Platform: success=${platformOk}`);
87
+ if (platformOk)
88
+ lastTelemetryAt = now;
89
+ }
90
+ if (entries.length > 0) {
91
+ let envelopes = (0, transformer_1.transformEntries)(entries);
92
+ const { valid: validEnvelopes, quarantined } = (0, validator_1.validate)(envelopes.map(e => e.event));
93
+ if (quarantined > 0) {
94
+ log.warn('bridge', `${quarantined} events quarantined (see ~/.openclaw/shield/data/quarantine.jsonl)`);
95
+ }
96
+ envelopes = envelopes.filter(e => validEnvelopes.includes(e.event));
97
+ if (config.maxEvents > 0 && envelopes.length > config.maxEvents) {
98
+ log.info('bridge', `Limiting to ${config.maxEvents} of ${envelopes.length} events (maxEvents cap)`);
99
+ envelopes = envelopes.slice(0, config.maxEvents);
100
+ }
101
+ if (config.redactionEnabled) {
102
+ envelopes = envelopes.map(env => {
103
+ const redacted = (0, redactor_1.redactEvent)(env);
104
+ log.debug('redactor', `tool=${env.event.tool_name} session=${env.event.session_id}`, { event: redacted.event });
105
+ return redacted;
106
+ });
107
+ }
108
+ log.info('bridge', `${entries.length} entries → ${envelopes.length} envelopes`);
109
+ const results = await (0, sender_1.sendEvents)(envelopes, config);
110
+ for (const r of results) {
111
+ if (r.success) {
112
+ log.info('bridge', `Result: success=${r.success} status=${r.statusCode} events=${r.eventCount}`);
113
+ }
114
+ else {
115
+ log.error('bridge', `Result: FAILED status=${r.statusCode} events=${r.eventCount} body=${r.body?.slice(0, 200)}`);
116
+ }
117
+ }
118
+ const allSuccess = results.every(r => r.success);
119
+ if (allSuccess) {
120
+ (0, fetcher_1.commitCursors)(config, entries);
121
+ if (config.redactionEnabled)
122
+ (0, redactor_1.flush)();
123
+ log.info('bridge', 'Cursors committed');
124
+ consecutiveFailures = 0;
125
+ }
126
+ else {
127
+ consecutiveFailures++;
128
+ log.warn('bridge', 'Some batches failed — cursors NOT updated (will retry next poll)');
129
+ }
130
+ if (config.maxEvents > 0) {
131
+ log.info('bridge', 'Test mode complete, exiting');
132
+ running = false;
133
+ break;
134
+ }
135
+ }
136
+ else {
137
+ consecutiveFailures = 0;
138
+ }
139
+ }
140
+ catch (err) {
141
+ consecutiveFailures++;
142
+ log.error('bridge', 'Poll error', err);
143
+ }
144
+ if (!running)
145
+ break;
146
+ const interval = getBackoffInterval(config.pollIntervalMs);
147
+ if (interval !== config.pollIntervalMs) {
148
+ log.warn('bridge', `Backing off: next poll in ${Math.round(interval / 1000)}s (${consecutiveFailures} consecutive failures)`);
149
+ }
150
+ await new Promise(r => setTimeout(r, interval));
151
+ }
152
+ if (config.redactionEnabled)
153
+ (0, redactor_1.flush)();
154
+ console.log('[Shield Bridge] Stopped');
155
+ }
156
+ function checkConfiguration() {
157
+ if (!(0, fs_1.existsSync)(config_1.SHIELD_CONFIG_PATH)) {
158
+ const { SHIELD_API_URL, SHIELD_INSTANCE_ID, SHIELD_HMAC_SECRET, SHIELD_FINGERPRINT, SHIELD_SECRET } = process.env;
159
+ // Support both new and legacy env var names
160
+ if (SHIELD_API_URL && (SHIELD_INSTANCE_ID || SHIELD_FINGERPRINT) && (SHIELD_HMAC_SECRET || SHIELD_SECRET))
161
+ return true;
162
+ console.log('🛡️ OpenClaw Shield — First Time Setup Required');
163
+ console.log('=================================================\n');
164
+ console.log(`No config found at ${config_1.SHIELD_CONFIG_PATH}`);
165
+ console.log('Run the setup wizard to connect to your Shield API:\n');
166
+ console.log(' npx shield-setup\n');
167
+ return false;
168
+ }
169
+ (0, config_1.injectConfigEnv)();
170
+ const creds = (0, config_1.loadCredentials)();
171
+ if (!creds.apiUrl || !creds.instanceId || !creds.hmacSecret) {
172
+ console.error('❌ Shield config is incomplete. Missing apiUrl, instanceId, or hmacSecret.');
173
+ console.error(` Config file: ${config_1.SHIELD_CONFIG_PATH}`);
174
+ console.error(' Run: npx shield-setup');
175
+ return false;
176
+ }
177
+ return true;
178
+ }
179
+ if (!checkConfiguration()) {
180
+ process.exit(1);
181
+ }
182
+ const config = (0, config_1.loadConfig)();
183
+ process.on('SIGINT', () => {
184
+ console.log('\n[Shield Bridge] Shutting down...');
185
+ if (config.redactionEnabled)
186
+ (0, redactor_1.flush)();
187
+ running = false;
188
+ });
189
+ process.on('SIGTERM', () => {
190
+ console.log('\n[Shield Bridge] Received SIGTERM, shutting down gracefully...');
191
+ if (config.redactionEnabled)
192
+ (0, redactor_1.flush)();
193
+ running = false;
194
+ });
195
+ poll().catch((err) => {
196
+ const msg = err instanceof Error ? err.message : String(err);
197
+ console.error('[Shield Bridge] Fatal error:', msg);
198
+ if (msg.includes('ENOTFOUND') || msg.includes('ECONNREFUSED')) {
199
+ console.error('\n💡 This looks like a network connectivity issue.');
200
+ console.error(' - Check your Shield API URL in the configuration');
201
+ console.error(' - Verify your internet connection');
202
+ console.error(' - Run: npm run setup -- --verify');
203
+ }
204
+ else if (msg.includes('401') || msg.includes('403')) {
205
+ console.error('\n💡 This looks like an authentication issue.');
206
+ console.error(' - Verify your Shield API credentials');
207
+ console.error(' - Run: npm run setup');
208
+ }
209
+ process.exit(1);
210
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * log.ts — Structured logger for the Shield plugin
3
+ *
4
+ * Supports two backends via the LogAdapter interface:
5
+ * 1. ConsoleLogAdapter (default) — used in standalone/docker bridge mode
6
+ * 2. Gateway adapter — injected by the plugin entry via setAdapter()
7
+ *
8
+ * All internal modules use `import * as log from './log'` unchanged.
9
+ * Standalone mode never calls setAdapter(), so console logging works
10
+ * identically to a direct console.log/warn/error setup.
11
+ *
12
+ * Log level is controlled by LOG_LEVEL env var: debug | info | warn | error
13
+ * (default: info). In debug mode, pipeline stages emit detailed per-event
14
+ * output (transformer, validator, redactor, sender).
15
+ */
16
+ export interface LogAdapter {
17
+ debug(tag: string, msg: string, data?: unknown): void;
18
+ info(tag: string, msg: string): void;
19
+ warn(tag: string, msg: string): void;
20
+ error(tag: string, msg: string, err?: unknown): void;
21
+ }
22
+ /**
23
+ * Swap the log backend. Plugin mode calls this with a Gateway-backed adapter.
24
+ * Standalone/docker mode never calls this — console logging remains the default.
25
+ * Also invalidates the cached log level so it is re-resolved on next call.
26
+ */
27
+ export declare function setAdapter(adapter: LogAdapter): void;
28
+ /** Reset to the default console adapter (useful for tests). */
29
+ export declare function resetAdapter(): void;
30
+ /**
31
+ * isDebug — dynamic property check.
32
+ * Accessed as `log.isDebug` (property, not function call) throughout the codebase.
33
+ * Using Object.defineProperty to make it a live getter on the module exports.
34
+ */
35
+ export declare let isDebug: boolean;
36
+ export declare function debug(tag: string, msg: string, data?: unknown): void;
37
+ export declare function info(tag: string, msg: string): void;
38
+ export declare function warn(tag: string, msg: string): void;
39
+ export declare function error(tag: string, msg: string, err?: unknown): void;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ /**
3
+ * log.ts — Structured logger for the Shield plugin
4
+ *
5
+ * Supports two backends via the LogAdapter interface:
6
+ * 1. ConsoleLogAdapter (default) — used in standalone/docker bridge mode
7
+ * 2. Gateway adapter — injected by the plugin entry via setAdapter()
8
+ *
9
+ * All internal modules use `import * as log from './log'` unchanged.
10
+ * Standalone mode never calls setAdapter(), so console logging works
11
+ * identically to a direct console.log/warn/error setup.
12
+ *
13
+ * Log level is controlled by LOG_LEVEL env var: debug | info | warn | error
14
+ * (default: info). In debug mode, pipeline stages emit detailed per-event
15
+ * output (transformer, validator, redactor, sender).
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.isDebug = void 0;
19
+ exports.setAdapter = setAdapter;
20
+ exports.resetAdapter = resetAdapter;
21
+ exports.debug = debug;
22
+ exports.info = info;
23
+ exports.warn = warn;
24
+ exports.error = error;
25
+ const LEVEL_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
26
+ function resolveLevel() {
27
+ const raw = (process.env.LOG_LEVEL || 'info').toLowerCase();
28
+ if (raw in LEVEL_RANK)
29
+ return raw;
30
+ return 'info';
31
+ }
32
+ let cachedLevel = null;
33
+ function getLevel() {
34
+ if (cachedLevel === null)
35
+ cachedLevel = resolveLevel();
36
+ return cachedLevel;
37
+ }
38
+ function fmt(level, tag, msg) {
39
+ const ts = new Date().toISOString().slice(11, 23); // HH:MM:SS.mmm
40
+ return `[${ts}] [${level.toUpperCase().padEnd(5)}] [${tag}] ${msg}`;
41
+ }
42
+ const consoleAdapter = {
43
+ debug(tag, msg, data) {
44
+ if (LEVEL_RANK[getLevel()] > LEVEL_RANK.debug)
45
+ return;
46
+ console.debug(fmt('debug', tag, msg));
47
+ if (data !== undefined)
48
+ console.debug(JSON.stringify(data, null, 2));
49
+ },
50
+ info(tag, msg) {
51
+ if (LEVEL_RANK[getLevel()] > LEVEL_RANK.info)
52
+ return;
53
+ console.log(fmt('info', tag, msg));
54
+ },
55
+ warn(tag, msg) {
56
+ if (LEVEL_RANK[getLevel()] > LEVEL_RANK.warn)
57
+ return;
58
+ console.warn(fmt('warn', tag, msg));
59
+ },
60
+ error(tag, msg, err) {
61
+ console.error(fmt('error', tag, msg));
62
+ if (err)
63
+ console.error(err);
64
+ },
65
+ };
66
+ let activeAdapter = consoleAdapter;
67
+ /**
68
+ * Swap the log backend. Plugin mode calls this with a Gateway-backed adapter.
69
+ * Standalone/docker mode never calls this — console logging remains the default.
70
+ * Also invalidates the cached log level so it is re-resolved on next call.
71
+ */
72
+ function setAdapter(adapter) {
73
+ activeAdapter = adapter;
74
+ cachedLevel = null;
75
+ }
76
+ /** Reset to the default console adapter (useful for tests). */
77
+ function resetAdapter() {
78
+ activeAdapter = consoleAdapter;
79
+ cachedLevel = null;
80
+ }
81
+ /**
82
+ * isDebug — dynamic property check.
83
+ * Accessed as `log.isDebug` (property, not function call) throughout the codebase.
84
+ * Using Object.defineProperty to make it a live getter on the module exports.
85
+ */
86
+ exports.isDebug = LEVEL_RANK[getLevel()] <= LEVEL_RANK.debug;
87
+ Object.defineProperty(exports, 'isDebug', {
88
+ get: () => LEVEL_RANK[getLevel()] <= LEVEL_RANK.debug,
89
+ enumerable: true,
90
+ });
91
+ function debug(tag, msg, data) {
92
+ activeAdapter.debug(tag, msg, data);
93
+ }
94
+ function info(tag, msg) {
95
+ activeAdapter.info(tag, msg);
96
+ }
97
+ function warn(tag, msg) {
98
+ activeAdapter.warn(tag, msg);
99
+ }
100
+ function error(tag, msg, err) {
101
+ activeAdapter.error(tag, msg, err);
102
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * src/redactor/base.ts — Core types for the strategy-based redaction system.
3
+ *
4
+ * Every redaction strategy implements RedactionStrategy. Strategies are pure
5
+ * data transforms: they receive a value and an injected hmac function, and
6
+ * return a redacted string. No I/O, no key management, no side effects.
7
+ */
8
+ /**
9
+ * Produces a deterministic token (e.g. 'user:a3f9b2c1d4e5') and records the
10
+ * original-to-token mapping in the vault. Injected into strategies so they
11
+ * never own key management or storage.
12
+ */
13
+ export type HmacFn = (category: string, value: string) => string;
14
+ /**
15
+ * Contract every redaction strategy must satisfy.
16
+ * The `key` string is what event `FieldRedaction.strategy` references.
17
+ */
18
+ export interface RedactionStrategy {
19
+ /** Unique identifier — must match the `strategy` field in FieldRedaction rules */
20
+ key: string;
21
+ /** Human-readable description for documentation and debugging */
22
+ description: string;
23
+ /**
24
+ * Apply redaction to a single string value.
25
+ * Must be pure: same inputs always produce same outputs.
26
+ * Must not throw on empty strings or unusual inputs.
27
+ */
28
+ redact: (value: string, hmac: HmacFn) => string;
29
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * src/redactor/base.ts — Core types for the strategy-based redaction system.
4
+ *
5
+ * Every redaction strategy implements RedactionStrategy. Strategies are pure
6
+ * data transforms: they receive a value and an injected hmac function, and
7
+ * return a redacted string. No I/O, no key management, no side effects.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * src/redactor/index.ts — Strategy engine, event redaction, and public API.
3
+ *
4
+ * This module wires vault + strategies into a single redaction engine.
5
+ * All imports of './redactor' resolve here (TypeScript: directory + index.ts).
6
+ */
7
+ import type { FieldRedaction } from '../events/base';
8
+ /**
9
+ * Apply a single FieldRedaction rule to an object via dot-notation path.
10
+ * Missing fields and null values are silently skipped.
11
+ * Unknown strategy keys throw a descriptive error.
12
+ */
13
+ export declare function applyFieldRedaction(obj: any, rule: FieldRedaction): void;
14
+ /**
15
+ * Deep-redact a Shield envelope. Returns a new object (does not mutate input).
16
+ * Applies base redactions + schema-specific redactions based on tool_category.
17
+ */
18
+ export declare function redactEvent(envelope: any): any;
19
+ export declare function redactUsername(value: string): string;
20
+ export declare function redactHostname(value: string): string;
21
+ export declare function redactPath(value: string): string;
22
+ export declare function redactCommand(value: string): string;
23
+ export declare function init(): void;
24
+ export declare function flush(): void;
25
+ export declare function reverseLookup(token: string): string | null;
26
+ export declare function getAllMappings(): Record<string, string>;
27
+ export declare function _initForTesting(secret: string): void;