@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 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;
@@ -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) => {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "shield",
3
3
  "name": "OpenClaw Shield",
4
- "description": "Real-time security monitoring \u2014 streams enriched, redacted security events to the Shield detection platform.",
5
- "version": "0.6.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 \u2014 Required for first-time activation only. After activation, log in at https://uss.upx.com"
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.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})\"",