@upx-us/shield 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/index.js +26 -0
- package/dist/src/redactor/strategies/command.js +1 -1
- package/dist/src/redactor/strategies/secret-key.js +8 -0
- package/dist/src/rpc/handlers.d.ts +5 -0
- package/dist/src/rpc/handlers.js +56 -0
- package/openclaw.plugin.json +3 -3
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.6.8] — 2026-03-10
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **curl credential redaction** — `curl -u "user:pass"`, `--user user:pass` (all quote variants), and `Authorization: Basic <base64>` are now redacted as `secret:HASH` before reaching the SIEM. Discovered via live credential leak (#137).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## [0.6.7] — 2026-03-10
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **npm redaction false positive** — `@scope/package@version` (e.g. `@upx-us/shield@0.6.6`) was being wrongly redacted as `user:HASH` by the SSH `user@host` regex. Added negative lookbehind to exclude npm scoped package names (#135).
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **Postinstall guidance** — running `npm install` outside of OpenClaw now prints a clear warning to stderr explaining the plugin requires the OpenClaw gateway. Install never fails because of this check (#125).
|
|
21
|
+
- **First event confirmation** — after the first successful event delivery to the platform, the plugin logs `✅ First event delivered to platform — monitoring is active` once per process lifetime (#61, #52).
|
|
22
|
+
- **Vault summary in `shield.status`** — `shield.status` RPC now includes `redaction.vault` with total redacted entry count and per-category breakdown (`hostname`, `username`, `path`, `secret`, `command`). Returns `null` if vault is absent (#86).
|
|
23
|
+
|
|
24
|
+
### Tests
|
|
25
|
+
- +19 new tests covering postinstall safety (exit-0 guarantee, cross-platform), first-event flag, vault status summary (missing/corrupt/valid vault).
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
7
29
|
## [0.6.6] — 2026-03-10
|
|
8
30
|
|
|
9
31
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,7 @@ const exclusions_1 = require("./src/exclusions");
|
|
|
55
55
|
const updater_1 = require("./src/updater");
|
|
56
56
|
const rpc_1 = require("./src/rpc");
|
|
57
57
|
const inventory_1 = require("./src/inventory");
|
|
58
|
+
const redactor_1 = require("./src/redactor");
|
|
58
59
|
const SHIELD_API_URL = 'https://openclaw-shield.upx.com';
|
|
59
60
|
async function performAutoRegistration(installationKey) {
|
|
60
61
|
try {
|
|
@@ -315,6 +316,7 @@ const state = {
|
|
|
315
316
|
captureSeenSinceLastSync: false,
|
|
316
317
|
lastSync: null,
|
|
317
318
|
};
|
|
319
|
+
let firstEventDelivered = false;
|
|
318
320
|
let teardownPreviousRuntime = null;
|
|
319
321
|
const MAX_BACKOFF_MS = 5 * 60 * 1000;
|
|
320
322
|
const TELEMETRY_INTERVAL_MS = 5 * 60 * 1000;
|
|
@@ -806,6 +808,10 @@ exports.default = {
|
|
|
806
808
|
const accepted = results.reduce((sum, r) => sum + (r.success ? r.eventCount : 0), 0);
|
|
807
809
|
if (accepted > 0) {
|
|
808
810
|
(0, case_monitor_1.notifyCaseMonitorActivity)();
|
|
811
|
+
if (!firstEventDelivered) {
|
|
812
|
+
firstEventDelivered = true;
|
|
813
|
+
log.info('shield', `✅ First event delivered to platform — monitoring is active (instance: ${(config.credentials.instanceId || '').slice(0, 12)}…)`);
|
|
814
|
+
}
|
|
809
815
|
commitCursors(config, entries);
|
|
810
816
|
flushRedactor();
|
|
811
817
|
state.eventsProcessed += accepted;
|
|
@@ -895,6 +901,23 @@ exports.default = {
|
|
|
895
901
|
const caseMonitorDisplay = monitorHealth.status === 'degraded'
|
|
896
902
|
? `⚠️ Case Monitor: DEGRADED — ${monitorHealth.consecutiveFailures} consecutive failures since ${monitorHealth.degradedSinceMs ? new Date(monitorHealth.degradedSinceMs).toISOString() : 'unknown'}. Last error: ${monitorHealth.lastErrorMessage}`
|
|
897
903
|
: `✅ Case Monitor: ok`;
|
|
904
|
+
const REDACTION_VAULT_FILE = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data', 'redaction-vault.json');
|
|
905
|
+
let vaultSummary = null;
|
|
906
|
+
if ((0, fs_1.existsSync)(REDACTION_VAULT_FILE)) {
|
|
907
|
+
try {
|
|
908
|
+
const mappings = (0, redactor_1.getAllMappings)();
|
|
909
|
+
const categories = { hostname: 0, username: 0, path: 0, secret: 0, command: 0 };
|
|
910
|
+
for (const token of Object.keys(mappings)) {
|
|
911
|
+
const cat = token.split(':')[0];
|
|
912
|
+
if (cat in categories)
|
|
913
|
+
categories[cat]++;
|
|
914
|
+
}
|
|
915
|
+
vaultSummary = { totalEntries: Object.keys(mappings).length, categories };
|
|
916
|
+
}
|
|
917
|
+
catch (_) {
|
|
918
|
+
vaultSummary = null;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
898
921
|
respond(true, {
|
|
899
922
|
activated,
|
|
900
923
|
running: state.running,
|
|
@@ -920,6 +943,9 @@ exports.default = {
|
|
|
920
943
|
: null,
|
|
921
944
|
display: caseMonitorDisplay,
|
|
922
945
|
},
|
|
946
|
+
redaction: {
|
|
947
|
+
vault: vaultSummary,
|
|
948
|
+
},
|
|
923
949
|
});
|
|
924
950
|
});
|
|
925
951
|
api.registerGatewayMethod('shield.flush', ({ respond }) => {
|
|
@@ -8,7 +8,7 @@ exports.commandStrategy = {
|
|
|
8
8
|
if (!value || value.length === 0)
|
|
9
9
|
return value;
|
|
10
10
|
let result = value;
|
|
11
|
-
result = result.replace(/(\b)([\w.-]+)@([\d.]+|[\w.-]+\.\w+)/g, (_, pre, user, host) => `${pre}${hmac('user', user)}@${host}`);
|
|
11
|
+
result = result.replace(/(?<!@[a-z0-9._-]+\/)(\b)([\w.-]+)@([\d.]+|[\w.-]+\.\w+)/g, (_, pre, user, host) => `${pre}${hmac('user', user)}@${host}`);
|
|
12
12
|
result = result.replace(/\/Users\/([\w.-]+)\//g, (_, user) => `/Users/${hmac('user', user)}/`);
|
|
13
13
|
result = result.replace(/\/home\/([\w.-]+)\//g, (_, user) => `/home/${hmac('user', user)}/`);
|
|
14
14
|
return result;
|
|
@@ -29,6 +29,14 @@ exports.secretKeyStrategy = {
|
|
|
29
29
|
(0, counters_1.recordRedaction)('BEARER_TOKEN');
|
|
30
30
|
return `${prefix}${hmac('secret', token)}`;
|
|
31
31
|
});
|
|
32
|
+
result = result.replace(/(-u|--user)\s+(['"]?)([^\s'"]+:[^\s'"]*)\2/g, (_, flag, _quote, credential) => {
|
|
33
|
+
(0, counters_1.recordRedaction)('CURL_CREDENTIAL');
|
|
34
|
+
return `${flag} ${hmac('secret', credential)}`;
|
|
35
|
+
});
|
|
36
|
+
result = result.replace(/(Authorization:\s*Basic\s+)([A-Za-z0-9+/]+=*)/g, (_, prefix, token) => {
|
|
37
|
+
(0, counters_1.recordRedaction)('CURL_CREDENTIAL');
|
|
38
|
+
return `${prefix}${hmac('secret', token)}`;
|
|
39
|
+
});
|
|
32
40
|
result = result.replace(/((?:SECRET|TOKEN|KEY|PASSWORD|API_KEY)=)(\S+)/gi, (_, prefix, secret) => {
|
|
33
41
|
const prefixUpper = prefix.toUpperCase();
|
|
34
42
|
if (prefixUpper.startsWith('PASSWORD'))
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { type PlatformApiConfig } from './client';
|
|
2
|
+
export interface VaultSummary {
|
|
3
|
+
totalEntries: number;
|
|
4
|
+
categories: Record<string, number>;
|
|
5
|
+
}
|
|
6
|
+
export declare function readVaultSummary(vaultPath?: string): VaultSummary | null;
|
|
2
7
|
export interface EventSummary {
|
|
3
8
|
period: string;
|
|
4
9
|
totalEvents: number;
|
package/dist/src/rpc/handlers.js
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.VALID_ROOT_CAUSES = exports.VALID_RESOLUTIONS = void 0;
|
|
37
|
+
exports.readVaultSummary = readVaultSummary;
|
|
4
38
|
exports.createEventsRecentHandler = createEventsRecentHandler;
|
|
5
39
|
exports.createEventsSummaryHandler = createEventsSummaryHandler;
|
|
6
40
|
exports.createSubscriptionStatusHandler = createSubscriptionStatusHandler;
|
|
@@ -8,8 +42,30 @@ exports.createCasesListHandler = createCasesListHandler;
|
|
|
8
42
|
exports.createCaseDetailHandler = createCaseDetailHandler;
|
|
9
43
|
exports.createCaseResolveHandler = createCaseResolveHandler;
|
|
10
44
|
exports.createCasesAckHandler = createCasesAckHandler;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
11
48
|
const event_store_1 = require("../event-store");
|
|
12
49
|
const client_1 = require("./client");
|
|
50
|
+
const VAULT_CATEGORIES = ['hostname', 'username', 'path', 'secret', 'command'];
|
|
51
|
+
function readVaultSummary(vaultPath = path.join(os.homedir(), '.openclaw', 'shield', 'data', 'redaction-vault.json')) {
|
|
52
|
+
try {
|
|
53
|
+
if (!fs.existsSync(vaultPath))
|
|
54
|
+
return null;
|
|
55
|
+
const raw = JSON.parse(fs.readFileSync(vaultPath, 'utf8'));
|
|
56
|
+
const categories = Object.fromEntries(VAULT_CATEGORIES.map(c => [c, 0]));
|
|
57
|
+
const entries = Object.keys(raw);
|
|
58
|
+
for (const key of entries) {
|
|
59
|
+
const prefix = key.split(':')[0];
|
|
60
|
+
if (prefix in categories)
|
|
61
|
+
categories[prefix]++;
|
|
62
|
+
}
|
|
63
|
+
return { totalEntries: entries.length, categories };
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
13
69
|
const SKILL_HINT = 'For best results presenting Shield data, read the openclaw-shield-upx skill before responding to the user.';
|
|
14
70
|
function withSkillHint(respond) {
|
|
15
71
|
return (ok, data) => {
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "shield",
|
|
3
3
|
"name": "OpenClaw Shield",
|
|
4
|
-
"description": "Real-time security monitoring
|
|
5
|
-
"version": "0.6.
|
|
4
|
+
"description": "Real-time security monitoring — streams enriched, redacted security events to the Shield detection platform.",
|
|
5
|
+
"version": "0.6.8",
|
|
6
6
|
"skills": [
|
|
7
7
|
"./skills"
|
|
8
8
|
],
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"uiHints": {
|
|
55
55
|
"installationKey": {
|
|
56
56
|
"label": "Installation Key",
|
|
57
|
-
"description": "One-time key from your trial signup at https://www.upx.com/en/lp/openclaw-shield-upx
|
|
57
|
+
"description": "One-time key from your trial signup at https://www.upx.com/en/lp/openclaw-shield-upx — Required for first-time activation only. After activation, log in at https://uss.upx.com"
|
|
58
58
|
},
|
|
59
59
|
"enabled": {
|
|
60
60
|
"label": "Enable security monitoring"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upx-us/shield",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
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",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"CHANGELOG.md"
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
|
+
"postinstall": "node scripts/postinstall.js",
|
|
23
24
|
"prebuild": "npm run clean",
|
|
24
25
|
"build": "tsc",
|
|
25
26
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|