@upx-us/shield 0.7.3 → 0.7.4

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,17 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.7.4] — 2026-03-13
8
+
9
+ ### Changed
10
+ - Cases are now instance-scoped by default — the platform API filters cases so each instance only sees its own cases. This eliminates cross-instance notification flooding when multiple instances share an org.
11
+ - Removed `--mine` flag from `shield cases` and `shield cases list` — no longer needed since all returned cases belong to this instance.
12
+ - Removed `[mine]` / `[sibling]` per-case tags from list output.
13
+ - Removed sibling instance notice from `shield cases show`.
14
+ - Removed mixed-ownership attribution summary from case list output.
15
+
16
+ ---
17
+
7
18
  ## [0.7.3] — 2026-03-13
8
19
 
9
20
  ### Fixed
@@ -71,106 +82,28 @@ All notable changes to this project will be documented in this file.
71
82
 
72
83
  ---
73
84
 
74
- ## [0.6.8] — 2026-03-10
75
-
76
- ### Fixed
77
- - **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).
78
-
79
- ---
80
-
81
- ## [0.6.7] — 2026-03-10
82
-
83
- ### Fixed
84
- - **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).
85
-
86
- ### Added
87
- - **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).
88
- - **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).
89
- - **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).
90
-
91
- ### Tests
92
- - +19 new tests covering postinstall safety (exit-0 guarantee, cross-platform), first-event flag, vault status summary (missing/corrupt/valid vault).
93
-
94
- ---
95
-
96
- ## [0.6.6] — 2026-03-10
97
-
98
- ### Added
99
- - **Plugin lifecycle events** — the plugin now reports key lifecycle signals to the USS platform via the existing `PUT /v1/instance` channel (no new endpoint). Signals are forwarded by Cloud Run as an optional `lifecycle_event` field in the instance telemetry PATCH to the platform. Absent on older plugin versions — fully backward compatible.
100
- - `plugin_started`: emitted on every successful startup. Includes `version`, `reason` (`'update'` | `'normal'`), and `previous_version` when applicable. Enables the platform to confirm end-to-end update success.
101
- - `update_restart_failed`: emitted after all gateway restart retries are exhausted. Includes `new_version`, `running_version`, `attempts`, `error`.
102
- - `plugin_integrity_drift`: emitted when `openclaw.json` version diverges from the installed version after a self-update. Includes `installed_version`, `registered_version`.
103
- - `update_check_failing`: emitted after 3 consecutive npm registry check failures. Includes `consecutive_failures`, `last_error`, `next_retry_at`.
104
-
105
- ### Changed
106
- - `reportPluginEvent()` removed from `sender.ts` — replaced by `reportLifecycleEvent()` which reuses the existing `PUT /v1/instance` telemetry channel.
107
-
108
- ### Fixed
109
- - `SHIELD_AUTO_UPDATE=false` environment variable now correctly disables auto-update (was being read as truthy string `'false'`).
85
+ ## [0.6 series summary] — 2026-03-06 to 2026-03-10
110
86
 
111
- ---
87
+ > Versions 0.6.0–0.6.8 are superseded by 0.6.10. Individual release notes consolidated below.
112
88
 
113
- ## [0.6.5] — 2026-03-09
89
+ ### Added across 0.6 series
90
+ - **Transformer gap fields** (0.6.0) — 7 new enrichment fields unblocking ~20 SIEM detection rules: `openclaw.secret_in_command`, `openclaw.channel`, `openclaw.chat_type`, `openclaw.message_content`, `openclaw.browser_action`, `openclaw.config_change`, `openclaw.file_size_bytes`
91
+ - **Plugin lifecycle events** (0.6.6) — startup, update failure, integrity drift, and check-failing signals forwarded to the platform via `PUT /v1/instance`
92
+ - **Smart degradation detection in `shield status`** (0.6.5) — three-gate algorithm (count + time + sticky recovery) eliminating false-positive DEGRADED alerts
93
+ - **`_skill_hint` in RPC responses** (0.6.0) — machine-readable hint in all Shield RPC replies
94
+ - **Postinstall guidance** (0.6.7) — clear warning when plugin is installed outside OpenClaw
95
+ - **First event confirmation log** (0.6.7) — `✅ First event delivered` emitted once per process lifetime
96
+ - **Vault summary in `shield.status`** (0.6.7) — redaction vault breakdown by category
114
97
 
115
- ### Added
116
- - **Smart degradation detection in `shield status`** — `getMonitorHealth()` now implements a three-gate algorithm to eliminate false positives from transient connectivity blips:
117
- 1. **Count gate**: `consecutiveFailures 5` (unchanged from v0.6.4)
118
- 2. **Time gate**: first failure in the current streak must be `> 3 minutes` old a 1-minute connectivity blip (3 failures at 1m intervals) can never trigger DEGRADED
119
- 3. **Sticky recovery**: requires `≥ 2 consecutive successes` to clear DEGRADED prevents flapping where one success clears it and then it degrades again next cycle
120
- - **`MonitorHealth` interface** exported from `src/case-monitor.ts`new fields: `status: 'ok' | 'degraded' | 'unknown'`, `consecutiveSuccesses`, `degradedSinceMs`, `lastCheckMs`, `lastErrorMessage`
121
- - `status: 'unknown'` before the monitor has completed its first check cycle
122
- - **`shield.status` RPC** `caseMonitor` section now includes smart health fields: `status`, `degradedSince` (ISO string), `consecutiveFailures`, `lastError`, `lastCheck` (ISO string), and a pre-formatted `display` string (e.g. `⚠️ Case Monitor: DEGRADED 7 consecutive failures since 2026-03-09T12:00:00.000Z. Last error: ...` or `✅ Case Monitor: ok`)
123
- - **7 new tests** covering all degradation scenarios: unknown initial state, transient blip (count gate), time gate, true DEGRADED positive, sticky recovery (1 success not enough), recovery (2 successes clear), and RPC handler shape
124
-
125
- ---
126
-
127
- ## [0.6.4] — 2026-03-09
128
-
129
- ### Fixed
130
- - **Case monitor — "Exclusions not initialized" (Telegram alerts never delivered)** — Exclusions are an optimization (noise filter), not a hard dependency. The `checkForNewCases()` loop called `isExcluded()` which threw `'Exclusions not initialized'` on every poll cycle because `initExclusions()` was never called in `src/index.ts`. Every check silently failed, resulting in zero Telegram notifications despite the platform returning open cases. Fix: replaced the hard dependency on exclusions with a soft check — if exclusions aren't initialized, a one-time warning is logged and all cases are forwarded unfiltered (over-notify is safer than silently dropping alerts). Root cause: `src/case-monitor.ts`, `checkForNewCases()` exclusion filter block — `storePath()` unconditionally threw when `_dataDir` was null.
131
-
132
- ---
133
-
134
- ## [0.6.3] — 2026-03-09
135
-
136
- ### Fixed
137
- - **Version normalization** — `resolveOpenClawVersion()` now normalizes messy version strings from `openclaw --version` output and the `lastTouchedVersion` config field (e.g. `"OpenClaw 2026.3.8 (3caab92)"` → `"2026.3.8"`). Previously these strings caused false "out of date" alerts on the Shield dashboard.
138
- - **Missing Network info** — `PUT /v1/instance` now always includes `public_ip` in the machine payload, even when the IP hasn't been resolved yet (sends `""` instead of omitting the field). This ensures the platform can attempt server-side geo-enrichment (Provider/Location) instead of silently skipping it.
139
-
140
- ### Added
141
- - `normalizeSoftwareVersion(v: string): string` — exported helper that applies the full version normalization pipeline (strip `"OpenClaw "` prefix, parenthetical hash, leading `"v"`). Used internally by `resolveOpenClawVersion()` for all auto-detected version paths.
142
- - `isPrivateIp(ip: string): boolean` — exported helper that detects RFC-1918/loopback addresses. Used by the ingest service to identify when an agent behind NAT (macOS, home/office Linux) self-reports a LAN IP and override it with the authoritative request IP. Ensures correct Network info (Provider, Location) on the Shield dashboard for all platforms.
143
-
144
- ---
145
-
146
- ## [0.6.2] — 2026-03-09
147
-
148
- ### Fixed
149
- - **ANSI field prefix bug** — `ansi_content_detected` and `ansi_sequences` were emitted without the `openclaw.` namespace prefix in `tool-result/enrich.ts`. YARAL rules reference them as `additional.fields["openclaw.ansi_content_detected"]` and `additional.fields["openclaw.ansi_sequences"]`. Fields now correctly namespaced. Unblocks `openclaw_ansi_injection` detection rule.
150
-
151
- ---
152
-
153
- ## [0.6.1] — 2026-03-09
154
-
155
- ### Fixed
156
- - **Auto-update restart resilience** — `requestGatewayRestart()` failures no longer leave the plugin in a split-brain state. The updater now tracks `pendingRestart` in `update-state.json` and retries the gateway restart on every subsequent poll cycle. After 5 failed attempts (~2.5 min), automatically rolls back to the previous backup version. Closes #112.
157
-
158
- ---
159
-
160
- ## [0.6.0] — 2026-03-06
161
-
162
- ### Added
163
- - **Transformer gap fields** — 7 new fields to unblock ~20 SIEM detection rules:
164
- - `openclaw.secret_in_command` — detects API keys, bearer tokens, and secrets in commands (pre-redaction flag)
165
- - `openclaw.channel` + `openclaw.chat_type` — messaging context for DLP rules
166
- - `openclaw.message_content` — truncated outbound message content (500 chars) for DLP pattern matching
167
- - `openclaw.browser_action` — browser tool action type (navigate, click, evaluate, etc.)
168
- - `openclaw.config_change` — flags writes/edits to config files (.env, openclaw.json, etc.)
169
- - `openclaw.file_size_bytes` — file size on write operations
170
- - **`_skill_hint` in RPC responses** — all successful Shield RPCs include a machine-readable hint nudging agents to read the SKILL.md before presenting data to users.
171
-
172
- ### Changed
173
- - npm registry cleaned: removed 26 old 0.5.x versions (kept 0.5.38 as fallback)
98
+ ### Fixed across 0.6 series
99
+ - **Auto-update restart resilience** (0.6.1) — `pendingRestart` tracked in state, retried for 5 cycles then rolls back to backup
100
+ - **ANSI field prefix** (0.6.2) `ansi_content_detected`/`ansi_sequences` now correctly namespaced as `openclaw.*`
101
+ - **Version normalization** (0.6.3)messy `openclaw --version` strings normalized to clean semver
102
+ - **Missing public IP in telemetry** (0.6.3)`PUT /v1/instance` always includes `public_ip` (sends `""` instead of omitting)
103
+ - **Case monitor exclusions crash** (0.6.4)soft dependency: if exclusions not initialized, cases forwarded unfiltered instead of throwing
104
+ - **npm redaction false positive** (0.6.7) `@scope/package@version` no longer matched by SSH `user@host` regex
105
+ - **curl credential redaction** (0.6.8) `curl -u`, `--user`, and `Authorization: Basic` all redacted as `secret:HASH`
106
+ - **`SHIELD_AUTO_UPDATE=false` ignored** (0.6.6) env var now correctly parsed as boolean
174
107
 
175
108
  ---
176
109
 
@@ -48,7 +48,7 @@ function registerCasesCli(shieldCommand) {
48
48
  const cases = shieldCommand.command('cases')
49
49
  .description('List and manage Shield security cases');
50
50
  cases.action(async () => {
51
- await listCases({ status: 'open', limit: '20', format: 'table', mine: false });
51
+ await listCases({ status: 'open', limit: '20', format: 'table' });
52
52
  });
53
53
  cases
54
54
  .command('list')
@@ -56,7 +56,6 @@ function registerCasesCli(shieldCommand) {
56
56
  .option('--status <status>', 'Filter: open, resolved, all', 'open')
57
57
  .option('--limit <n>', 'Max results', '20')
58
58
  .option('--format <fmt>', 'Output format: table or json', 'table')
59
- .option('--mine', 'Show only cases from this instance')
60
59
  .action(listCases);
61
60
  cases
62
61
  .command('show')
@@ -109,37 +108,15 @@ async function listCases(opts) {
109
108
  console.log(JSON.stringify(result, null, 2));
110
109
  return;
111
110
  }
112
- const own = result.cases.filter((c) => c.ownership === 'own').length;
113
- const sibling = result.cases.filter((c) => c.ownership === 'sibling').length;
114
- const unknown = result.cases.filter((c) => c.ownership === 'unknown' || c.ownership == null).length;
115
- const visible = opts.mine
116
- ? result.cases.filter((c) => c.is_mine === true)
117
- : result.cases;
118
- if (visible.length === 0 && opts.mine && result.cases.length > 0) {
119
- console.log('No cases attributed to this instance. Other org instances have open cases \u2014 run without --mine to see them.');
120
- return;
121
- }
122
- if (visible.length === 0) {
111
+ if (result.cases.length === 0) {
123
112
  console.log('No open cases. \u2705');
124
113
  return;
125
114
  }
126
- if (sibling > 0 || (own > 0 && unknown > 0)) {
127
- const parts = [];
128
- if (own > 0)
129
- parts.push(`${own} from this instance`);
130
- if (sibling > 0)
131
- parts.push(`${sibling} from other instances`);
132
- if (unknown > 0)
133
- parts.push(`${unknown} unattributed`);
134
- console.log(`Attribution: ${parts.join(', ')} (use --mine to filter)\n`);
135
- }
136
- console.log(`Cases (${visible.length} of ${result.total}):\n`);
137
- for (const c of visible) {
115
+ console.log(`Cases (${result.cases.length} of ${result.total}):\n`);
116
+ for (const c of result.cases) {
138
117
  const sev = (c.severity || '').padEnd(8);
139
118
  const time = new Date(c.created_at).toLocaleString();
140
- const ownershipTag = c.ownership === 'own' ? ' [mine]' :
141
- c.ownership === 'sibling' ? ' [sibling]' : '';
142
- console.log(` [${sev}]${ownershipTag} ${c.id}`);
119
+ console.log(` [${sev}] ${c.id}`);
143
120
  console.log(` ${c.rule_title || c.rule_id}`);
144
121
  console.log(` ${c.summary || ''}`);
145
122
  console.log(` Created: ${time} Events: ${c.event_count || 0}\n`);
@@ -161,19 +138,10 @@ async function showCase(id, opts) {
161
138
  }
162
139
  console.log(`Case: ${result.id}`);
163
140
  console.log(`Status: ${result.status} Severity: ${result.severity}`);
164
- if (result.ownership === 'sibling') {
165
- console.log(`Instance: sibling (belongs to another instance in your org \u2014 you can resolve it, but local log access is unavailable)`);
166
- }
167
- else if (result.ownership === 'own') {
168
- console.log(`Instance: this instance`);
169
- }
170
141
  console.log(`Rule: ${result.rule_title || result.rule_id}`);
171
142
  console.log(`Summary: ${result.summary || '\u2014'}`);
172
143
  console.log(`Created: ${new Date(result.created_at).toLocaleString()}`);
173
144
  console.log(`Events: ${result.event_count || 0}`);
174
- if (result.attribution) {
175
- console.log(`\n${result.attribution}`);
176
- }
177
145
  if (result.rule) {
178
146
  console.log(`\nRule: ${result.rule.description || '\u2014'}`);
179
147
  if (result.rule.mitre_tactic)
@@ -2,7 +2,7 @@
2
2
  "id": "shield",
3
3
  "name": "OpenClaw Shield",
4
4
  "description": "Real-time security monitoring \u2014 streams enriched, redacted security events to the Shield detection platform.",
5
- "version": "0.7.3",
5
+ "version": "0.7.4",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upx-us/shield",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
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",
@@ -31,9 +31,7 @@ Shield requires the `@upx-us/shield` plugin and an active subscription.
31
31
  | `openclaw shield logs --type TOOL_CALL --since 1h` | Filter by event type or time window |
32
32
  | `openclaw shield logs --format json` | JSON output |
33
33
  | `openclaw shield vault show` | Agent and workspace inventory, redaction summary (hashed IDs) |
34
- | `openclaw shield cases` | List open security cases |
35
- | `openclaw shield cases --mine` | List only cases from this instance |
36
- | `openclaw shield cases list --mine` | Show only cases from this instance |
34
+ | `openclaw shield cases` | List open security cases (scoped to this instance) |
37
35
  | `openclaw shield cases show <ID>` | Full case detail with events, rule, playbook |
38
36
  | `openclaw shield cases resolve <ID>` | Resolve a case (--resolution, --root-cause, --comment) |
39
37
  | `openclaw shield monitor` | Case notification cron — status, --on, --off, --interval |
@@ -85,7 +83,7 @@ Proceed normally. No onboarding message needed.
85
83
 
86
84
  When a Shield case fires or the user asks about an alert: use `openclaw shield cases` to list open cases and `openclaw shield cases --id <id>` for full detail (timeline, matched events, playbook). Severity guidance: **CRITICAL/HIGH** → surface immediately and ask if they want to investigate; **MEDIUM** → present and offer a playbook walkthrough; **LOW/INFO** → mention without interrupting the current task. Always include: rule name, what it detects, when it fired, and the first recommended remediation step. Confirm with the user before resolving — never resolve autonomously.
87
85
 
88
- When listing cases, note how many belong to this instance vs other org instances. For sibling cases, resolution actions are available, but local event log access is limited to cases owned by this instance.
86
+ Cases returned by `shield cases` are always scoped to this instance the platform filters at the API level so you only see cases triggered by your agent.
89
87
 
90
88
  Shield now stamps each event with a `trigger_type` — who or what initiated the session. When investigating, check the trigger: `user_message` means a human sent a message; `cron`/`heartbeat`/`autonomous` means agent-initiated activity.
91
89