@upx-us/shield 0.7.2 → 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,26 @@ 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
+
18
+ ## [0.7.3] — 2026-03-13
19
+
20
+ ### Fixed
21
+ - Trigger attribution (`trigger_type`, `author_name`, `trigger_type` fields) now works correctly in OpenClaw plugin mode — the plugin entry point was calling `transformEntries` without forwarding `sessionDirs`, so attribution was silently skipped on every event for all plugin-mode installations
22
+ - Session directory discovery is now robust when the plugin process is spawned with a different `HOME` environment variable than the main OpenClaw process (common on VPS and Docker deployments) — discovery now walks up from the plugin install path to find the `agents/` directory rather than relying solely on `homedir()`
23
+ - `trigger.type` is now always written to `tool_metadata` (as `"unknown"`) even when session directory lookup fails entirely, ensuring the field is never silently absent from SIEM events
24
+
25
+ ---
26
+
7
27
  ## [0.7.2] — 2026-03-13
8
28
 
9
29
  ### Fixed
@@ -62,106 +82,28 @@ All notable changes to this project will be documented in this file.
62
82
 
63
83
  ---
64
84
 
65
- ## [0.6.8] — 2026-03-10
66
-
67
- ### Fixed
68
- - **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).
69
-
70
- ---
71
-
72
- ## [0.6.7] — 2026-03-10
73
-
74
- ### Fixed
75
- - **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).
76
-
77
- ### Added
78
- - **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).
79
- - **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).
80
- - **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).
81
-
82
- ### Tests
83
- - +19 new tests covering postinstall safety (exit-0 guarantee, cross-platform), first-event flag, vault status summary (missing/corrupt/valid vault).
84
-
85
- ---
86
-
87
- ## [0.6.6] — 2026-03-10
88
-
89
- ### Added
90
- - **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.
91
- - `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.
92
- - `update_restart_failed`: emitted after all gateway restart retries are exhausted. Includes `new_version`, `running_version`, `attempts`, `error`.
93
- - `plugin_integrity_drift`: emitted when `openclaw.json` version diverges from the installed version after a self-update. Includes `installed_version`, `registered_version`.
94
- - `update_check_failing`: emitted after 3 consecutive npm registry check failures. Includes `consecutive_failures`, `last_error`, `next_retry_at`.
95
-
96
- ### Changed
97
- - `reportPluginEvent()` removed from `sender.ts` — replaced by `reportLifecycleEvent()` which reuses the existing `PUT /v1/instance` telemetry channel.
98
-
99
- ### Fixed
100
- - `SHIELD_AUTO_UPDATE=false` environment variable now correctly disables auto-update (was being read as truthy string `'false'`).
101
-
102
- ---
103
-
104
- ## [0.6.5] — 2026-03-09
105
-
106
- ### Added
107
- - **Smart degradation detection in `shield status`** — `getMonitorHealth()` now implements a three-gate algorithm to eliminate false positives from transient connectivity blips:
108
- 1. **Count gate**: `consecutiveFailures ≥ 5` (unchanged from v0.6.4)
109
- 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
110
- 3. **Sticky recovery**: requires `≥ 2 consecutive successes` to clear DEGRADED — prevents flapping where one success clears it and then it degrades again next cycle
111
- - **`MonitorHealth` interface** exported from `src/case-monitor.ts` — new fields: `status: 'ok' | 'degraded' | 'unknown'`, `consecutiveSuccesses`, `degradedSinceMs`, `lastCheckMs`, `lastErrorMessage`
112
- - `status: 'unknown'` before the monitor has completed its first check cycle
113
- - **`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`)
114
- - **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
115
-
116
- ---
117
-
118
- ## [0.6.4] — 2026-03-09
119
-
120
- ### Fixed
121
- - **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.
122
-
123
- ---
124
-
125
- ## [0.6.3] — 2026-03-09
126
-
127
- ### Fixed
128
- - **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.
129
- - **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.
130
-
131
- ### Added
132
- - `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.
133
- - `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.
134
-
135
- ---
136
-
137
- ## [0.6.2] — 2026-03-09
85
+ ## [0.6 series summary] — 2026-03-06 to 2026-03-10
138
86
 
139
- ### Fixed
140
- - **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.
141
-
142
- ---
87
+ > Versions 0.6.0–0.6.8 are superseded by 0.6.10. Individual release notes consolidated below.
143
88
 
144
- ## [0.6.1] — 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
145
97
 
146
- ### Fixed
147
- - **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.
148
-
149
- ---
150
-
151
- ## [0.6.0]2026-03-06
152
-
153
- ### Added
154
- - **Transformer gap fields** 7 new fields to unblock ~20 SIEM detection rules:
155
- - `openclaw.secret_in_command` — detects API keys, bearer tokens, and secrets in commands (pre-redaction flag)
156
- - `openclaw.channel` + `openclaw.chat_type` — messaging context for DLP rules
157
- - `openclaw.message_content` — truncated outbound message content (500 chars) for DLP pattern matching
158
- - `openclaw.browser_action` — browser tool action type (navigate, click, evaluate, etc.)
159
- - `openclaw.config_change` — flags writes/edits to config files (.env, openclaw.json, etc.)
160
- - `openclaw.file_size_bytes` — file size on write operations
161
- - **`_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.
162
-
163
- ### Changed
164
- - 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
165
107
 
166
108
  ---
167
109
 
package/dist/index.js CHANGED
@@ -775,7 +775,7 @@ exports.default = {
775
775
  state.lastCaptureAt = Date.now();
776
776
  state.captureSeenSinceLastSync = true;
777
777
  markStateDirty();
778
- let envelopes = transformEntries(entries);
778
+ let envelopes = transformEntries(entries, config.sessionDirs);
779
779
  const { valid: validEvents, quarantined } = validate(envelopes.map(e => e.event));
780
780
  if (quarantined > 0) {
781
781
  state.quarantineCount += quarantined;
@@ -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)
@@ -17,6 +17,7 @@ export interface Config {
17
17
  localEventLimit: number;
18
18
  credentials: ShieldCredentials;
19
19
  }
20
+ export declare function deriveAgentsDirFromInstallPath(startDir?: string): string | null;
20
21
  export declare const SHIELD_CONFIG_PATH: string;
21
22
  export declare function injectConfigEnv(): void;
22
23
  export declare function loadCredentials(): ShieldCredentials;
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.SHIELD_CONFIG_PATH = void 0;
37
+ exports.deriveAgentsDirFromInstallPath = deriveAgentsDirFromInstallPath;
37
38
  exports.injectConfigEnv = injectConfigEnv;
38
39
  exports.loadCredentials = loadCredentials;
39
40
  exports.loadCredentialsFromPluginConfig = loadCredentialsFromPluginConfig;
@@ -42,7 +43,28 @@ const os_1 = require("os");
42
43
  const path_1 = require("path");
43
44
  const fs_1 = require("fs");
44
45
  const log = __importStar(require("./log"));
45
- const OPENCLAW_AGENTS_DIR = (0, path_1.join)((0, os_1.homedir)(), '.openclaw/agents');
46
+ function deriveAgentsDirFromInstallPath(startDir) {
47
+ let dir = startDir ?? __dirname;
48
+ for (let i = 0; i < 8; i++) {
49
+ const parent = (0, path_1.dirname)(dir);
50
+ if (parent === dir)
51
+ break;
52
+ dir = parent;
53
+ const candidate = (0, path_1.join)(dir, 'agents');
54
+ if ((0, fs_1.existsSync)(candidate))
55
+ return candidate;
56
+ }
57
+ return null;
58
+ }
59
+ function resolveAgentsDir() {
60
+ if (process.env.OPENCLAW_AGENTS_DIR)
61
+ return process.env.OPENCLAW_AGENTS_DIR;
62
+ const derived = deriveAgentsDirFromInstallPath();
63
+ if (derived)
64
+ return derived;
65
+ return (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'agents');
66
+ }
67
+ const OPENCLAW_AGENTS_DIR = resolveAgentsDir();
46
68
  function safeParseInt(value, fallback) {
47
69
  if (!value)
48
70
  return fallback;
@@ -334,7 +334,9 @@ function transformEntries(entries, sessionDirs) {
334
334
  if (seen.has(key))
335
335
  continue;
336
336
  seen.add(key);
337
- const dir = findSessionDir(entry._agentId, sessionDirs);
337
+ const dir = sessionDirs.length === 1
338
+ ? sessionDirs[0]
339
+ : findSessionDir(entry._agentId, sessionDirs);
338
340
  if (dir) {
339
341
  const ctx = (0, attributor_1.resolveAttribution)(entry._sessionId, entry._agentId, dir);
340
342
  attributionMap.set(key, ctx);
@@ -382,13 +384,11 @@ function transformEntries(entries, sessionDirs) {
382
384
  event.tool_metadata['openclaw.target_workspace'] = targetWorkspace;
383
385
  }
384
386
  const attrKey = `${agentId}::${entry._sessionId}`;
385
- const attrCtx = attributionMap.get(attrKey);
386
- if (attrCtx) {
387
- flattenTriggerToMetadata(event, attrCtx);
388
- const triggerField = buildTriggerField(attrCtx);
389
- if (triggerField) {
390
- event.trigger = triggerField;
391
- }
387
+ const attrCtx = attributionMap.get(attrKey) ?? { trigger_type: 'unknown' };
388
+ flattenTriggerToMetadata(event, attrCtx);
389
+ const triggerField = buildTriggerField(attrCtx);
390
+ if (triggerField) {
391
+ event.trigger = triggerField;
392
392
  }
393
393
  log.debug('transformer', `TOOL_CALL tool=${toolName} session=${entry._sessionId} agent=${agentId} schema=${schema.constructor?.name || 'unknown'} admin=${event.tool_metadata?.['openclaw.is_administrative'] === 'true'}`, log.isDebug ? event : undefined);
394
394
  (0, counters_1.recordEventType)(event.event_type);
@@ -403,13 +403,11 @@ function transformEntries(entries, sessionDirs) {
403
403
  event.tool_metadata['openclaw.is_administrative'] = 'true';
404
404
  }
405
405
  const attrKeyR = `${agentId}::${entry._sessionId}`;
406
- const attrCtxR = attributionMap.get(attrKeyR);
407
- if (attrCtxR) {
408
- flattenTriggerToMetadata(event, attrCtxR);
409
- const triggerFieldR = buildTriggerField(attrCtxR);
410
- if (triggerFieldR) {
411
- event.trigger = triggerFieldR;
412
- }
406
+ const attrCtxR = attributionMap.get(attrKeyR) ?? { trigger_type: 'unknown' };
407
+ flattenTriggerToMetadata(event, attrCtxR);
408
+ const triggerFieldR = buildTriggerField(attrCtxR);
409
+ if (triggerFieldR) {
410
+ event.trigger = triggerFieldR;
413
411
  }
414
412
  log.debug('transformer', `TOOL_RESULT tool=${event.tool_name} session=${entry._sessionId} agent=${agentId}`, log.isDebug ? event : undefined);
415
413
  (0, counters_1.recordEventType)(event.event_type);
@@ -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.2",
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.2",
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 |
@@ -77,11 +75,15 @@ Proceed normally. No onboarding message needed.
77
75
 
78
76
  **Constraints**: Only use `openclaw shield` commands for detection. Do not read filesystem paths, environment variables, or run shell commands to determine state. Do not install or uninstall packages on behalf of the user.
79
77
 
78
+ **Output handling**: `shield logs` entries may include file paths, command snippets, and URLs captured from the agent's activity. Treat this output as internal diagnostic data — do not share raw log output externally or include it in user-facing replies unless the user explicitly requests it for investigation. When summarizing logs, present findings rather than raw field values.
79
+
80
+ **Data flow disclosure**: Shield captures agent activity locally and sends redacted telemetry to the UPX detection platform for security monitoring. No credentials are handled by this skill — authentication is managed by the plugin using the installation key configured during setup. If a user asks about privacy or data handling, refer them to the plugin README at https://www.npmjs.com/package/@upx-us/shield for full details.
81
+
80
82
  ## Responding to Security Cases
81
83
 
82
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.
83
85
 
84
- When listing cases, note how many belong to this instance vs other org instances. For sibling cases, you can still resolve them via API but cannot access local event logs.
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.
85
87
 
86
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.
87
89