@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 +39 -97
- package/dist/index.js +1 -1
- package/dist/src/cli-cases.js +5 -37
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.js +23 -1
- package/dist/src/transformer.js +13 -15
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/shield/SKILL.md +6 -4
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
|
|
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
|
-
|
|
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
|
-
|
|
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**
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
- **
|
|
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;
|
package/dist/src/cli-cases.js
CHANGED
|
@@ -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'
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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)
|
package/dist/src/config.d.ts
CHANGED
|
@@ -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;
|
package/dist/src/config.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/src/transformer.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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);
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/skills/shield/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|