@upx-us/shield 0.2.15-beta → 0.2.16-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.
- package/dist/index.js +18 -0
- package/dist/src/sender.d.ts +5 -0
- package/dist/src/sender.js +32 -3
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -282,6 +282,24 @@ exports.default = {
|
|
|
282
282
|
envelopes = envelopes.map(e => redactEvent(e));
|
|
283
283
|
}
|
|
284
284
|
const results = await sendEvents(envelopes, config);
|
|
285
|
+
// 403 needs_registration → self-halt cleanly (no point retrying)
|
|
286
|
+
const needsReg = results.some(r => r.needsRegistration);
|
|
287
|
+
if (needsReg) {
|
|
288
|
+
log.error('shield', 'Instance not registered on platform — Shield deactivated. Re-run: npx shield-setup');
|
|
289
|
+
state.running = false;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
// 202 pending_namespace → hold cursors, don't count as failure, use retry_after
|
|
293
|
+
const pending = results.find(r => r.pendingNamespace);
|
|
294
|
+
if (pending) {
|
|
295
|
+
const waitMs = pending.retryAfterMs ?? 300_000;
|
|
296
|
+
log.warn('shield', `Namespace allocation in progress — holding events, backing off ${Math.round(waitMs / 1000)}s`);
|
|
297
|
+
state.lastPollAt = Date.now();
|
|
298
|
+
persistState();
|
|
299
|
+
// Override next poll interval by sleeping here (schedulePoll will use normal backoff after)
|
|
300
|
+
await new Promise(r => setTimeout(r, waitMs));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
285
303
|
const accepted = results.reduce((sum, r) => sum + (r.success ? r.eventCount : 0), 0);
|
|
286
304
|
if (accepted > 0) {
|
|
287
305
|
commitCursors(config, entries);
|
package/dist/src/sender.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface SendResult {
|
|
|
15
15
|
statusCode?: number;
|
|
16
16
|
body?: string;
|
|
17
17
|
eventCount: number;
|
|
18
|
+
/** True when Cloud Run returns 202 pending_namespace — hold cursors, backoff retry_after */
|
|
19
|
+
pendingNamespace?: boolean;
|
|
20
|
+
retryAfterMs?: number;
|
|
21
|
+
/** True when Cloud Run returns 403 needs_registration — plugin must self-halt */
|
|
22
|
+
needsRegistration?: boolean;
|
|
18
23
|
}
|
|
19
24
|
export declare function sendEvents(events: EnvelopeEvent[], config: Config): Promise<SendResult[]>;
|
|
20
25
|
/**
|
package/dist/src/sender.js
CHANGED
|
@@ -115,7 +115,8 @@ async function sendEvents(events, config) {
|
|
|
115
115
|
method: 'POST',
|
|
116
116
|
headers: {
|
|
117
117
|
'Content-Type': 'application/json',
|
|
118
|
-
'X-Shield-Instance-Id': instanceId,
|
|
118
|
+
'X-Shield-Instance-Id': instanceId, // legacy name
|
|
119
|
+
'X-Shield-Fingerprint': instanceId, // canonical name
|
|
119
120
|
'X-Shield-Nonce': nonce,
|
|
120
121
|
'X-Shield-Signature': signature,
|
|
121
122
|
'X-Shield-Version': version_1.VERSION,
|
|
@@ -125,10 +126,37 @@ async function sendEvents(events, config) {
|
|
|
125
126
|
});
|
|
126
127
|
const data = await res.text();
|
|
127
128
|
log.info('sender', `Batch ${batchNum}: ${batch.length} events (HTTP ${res.status})`);
|
|
128
|
-
if (res.ok) {
|
|
129
|
+
if (res.ok && res.status === 200) {
|
|
129
130
|
results.push({ success: true, statusCode: res.status, body: data, eventCount: batch.length });
|
|
130
131
|
break;
|
|
131
132
|
}
|
|
133
|
+
// 202 pending_namespace — namespace not yet allocated by SIEM.
|
|
134
|
+
// Events must NOT be committed; plugin should hold and retry after retry_after.
|
|
135
|
+
if (res.status === 202) {
|
|
136
|
+
let retryAfterMs = 300_000; // default 5 min
|
|
137
|
+
try {
|
|
138
|
+
retryAfterMs = (JSON.parse(data).retry_after ?? 300) * 1000;
|
|
139
|
+
}
|
|
140
|
+
catch { }
|
|
141
|
+
log.warn('sender', `Batch ${batchNum} — namespace pending (202). Holding events, retry in ${retryAfterMs / 1000}s`);
|
|
142
|
+
results.push({ success: false, statusCode: 202, body: data, eventCount: batch.length,
|
|
143
|
+
pendingNamespace: true, retryAfterMs });
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
// 403 needs_registration — instance unknown or inactive, plugin must self-halt.
|
|
147
|
+
if (res.status === 403) {
|
|
148
|
+
let needsReg = false;
|
|
149
|
+
try {
|
|
150
|
+
needsReg = JSON.parse(data).needs_registration === true;
|
|
151
|
+
}
|
|
152
|
+
catch { }
|
|
153
|
+
if (needsReg) {
|
|
154
|
+
log.error('sender', `Batch ${batchNum} — instance not registered (403). Shield deactivated — re-run wizard.`);
|
|
155
|
+
results.push({ success: false, statusCode: 403, body: data, eventCount: batch.length,
|
|
156
|
+
needsRegistration: true });
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
132
160
|
if (res.status >= 500 && attempt === 0) {
|
|
133
161
|
log.warn('sender', `Batch ${batchNum} attempt ${attempt + 1} — HTTP ${res.status}, retrying...`);
|
|
134
162
|
continue;
|
|
@@ -170,7 +198,8 @@ async function reportInstance(payload, credentials) {
|
|
|
170
198
|
method: 'PUT',
|
|
171
199
|
headers: {
|
|
172
200
|
'Content-Type': 'application/json',
|
|
173
|
-
'X-Shield-Instance-Id': instanceId,
|
|
201
|
+
'X-Shield-Instance-Id': instanceId, // legacy
|
|
202
|
+
'X-Shield-Fingerprint': instanceId, // canonical
|
|
174
203
|
'X-Shield-Nonce': nonce,
|
|
175
204
|
'X-Shield-Signature': signature,
|
|
176
205
|
'X-Shield-Version': version_1.VERSION,
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upx-us/shield",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16-beta",
|
|
4
4
|
"description": "Security monitoring plugin for OpenClaw agents — streams enriched security events to the Shield detection platform",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|