shroud-privacy 2.2.8 → 2.2.10
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/README.md +32 -10
- package/dist/detectors/context.js +7 -0
- package/dist/detectors/regex.js +33 -5
- package/openclaw.plugin.json +155 -30
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -77,16 +77,16 @@ Shroud does not guarantee compliance — regex-based detection has limitations (
|
|
|
77
77
|
|
|
78
78
|
> **How it works:** Shroud intercepts ALL outbound LLM API calls (Anthropic, OpenAI, Google, any provider) at the `fetch` level and obfuscates detected entities in every message — including assistant history and Slack `<mailto:>` markup — before it leaves the process. On the response side, SSE streaming is deobfuscated per content block with buffered flushing. Every delivery path (Slack, WhatsApp, TUI, Telegram, Discord, Signal, web) gets real text automatically. Zero host patches required.
|
|
79
79
|
|
|
80
|
-
> **Requires OpenClaw 2026.3.
|
|
80
|
+
> **Requires OpenClaw 2026.3.22 or later.**
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
84
84
|
## Install
|
|
85
85
|
|
|
86
|
-
### OpenClaw (2026.3.
|
|
86
|
+
### OpenClaw (2026.3.22+)
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
openclaw --version # ensure 2026.3.
|
|
89
|
+
openclaw --version # ensure 2026.3.22+
|
|
90
90
|
openclaw plugins install shroud-privacy
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -127,7 +127,7 @@ node node_modules/shroud-privacy/app-server.mjs node_modules/shroud-privacy/dist
|
|
|
127
127
|
|
|
128
128
|
Handshake (server writes on startup):
|
|
129
129
|
```json
|
|
130
|
-
{"app":"1.0","engine":"shroud","version":"2.2.
|
|
130
|
+
{"app":"1.0","engine":"shroud","version":"2.2.9","capabilities":["obfuscate","deobfuscate","batch","stats","health","configure","audit","partitions"]}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
Obfuscate:
|
|
@@ -406,14 +406,36 @@ client.stop()
|
|
|
406
406
|
|
|
407
407
|
```bash
|
|
408
408
|
npm install
|
|
409
|
-
npm
|
|
410
|
-
npm run
|
|
411
|
-
npm
|
|
412
|
-
npm run test:
|
|
413
|
-
npm run
|
|
414
|
-
npm run lint # type-check without emitting
|
|
409
|
+
npm run build # compile TypeScript
|
|
410
|
+
npm run lint # type-check without emitting
|
|
411
|
+
npm test # unit + harness (1,229 tests, no Docker)
|
|
412
|
+
npm run test:docker # Docker E2E — real OpenClaw, all channels (192 tests)
|
|
413
|
+
npm run test:all # everything (1,421 tests)
|
|
415
414
|
```
|
|
416
415
|
|
|
416
|
+
### Test layers
|
|
417
|
+
|
|
418
|
+
| Layer | Command | Tests | What it covers |
|
|
419
|
+
|-------|---------|-------|---------------|
|
|
420
|
+
| Unit | `npm run test:unit` | 870 | Obfuscator, detectors, generators, store, config |
|
|
421
|
+
| APP Harness | `npm run test:integration` | 359 | 48 scenario files via mock LLM, no OpenClaw |
|
|
422
|
+
| Docker E2E | `npm run test:docker` | 192 | Real OpenClaw gateway, Slack/WhatsApp/Cron/TUI channels, 153 regression scenarios |
|
|
423
|
+
| Sandbox E2E | `run-compat.sh <ver> --sandbox` | +8 | Docker-in-Docker, sandboxed agent exec, tool call deobfuscation |
|
|
424
|
+
|
|
425
|
+
Docker E2E runs inside an isolated container (`--internal` network, no external routing). Both OpenClaw and Shroud are installed from npm — the same path real users take. A single gateway process handles all tests via WebSocket RPC. Channel tests use mock servers with real SDK code paths (Slack via Bolt HTTP, WhatsApp via Baileys intercept).
|
|
426
|
+
|
|
427
|
+
### OpenClaw compatibility matrix
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
bash compat/run-compat.sh latest # test against latest OpenClaw
|
|
431
|
+
bash compat/run-compat.sh latest --sandbox # include sandboxed agent exec tests
|
|
432
|
+
bash compat/run-matrix.sh # interactive: current or current + last 3
|
|
433
|
+
bash compat/run-matrix.sh --latest 3 # latest 3 versions
|
|
434
|
+
bash compat/run-matrix.sh --parallel # parallel execution
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Supported versions are tracked in `compat/versions.json`. CI checks for new OpenClaw releases daily.
|
|
438
|
+
|
|
417
439
|
---
|
|
418
440
|
|
|
419
441
|
## Disclaimer
|
|
@@ -316,6 +316,13 @@ export class ContextDetector {
|
|
|
316
316
|
"regex:device_name_dotted",
|
|
317
317
|
"regex:device_name_short",
|
|
318
318
|
"context:hostname_propagation",
|
|
319
|
+
// Network credentials — context-dependent detectors that need learning
|
|
320
|
+
// so bare values are re-detected in multi-turn assistant history
|
|
321
|
+
"regex:snmp_community",
|
|
322
|
+
"regex:prose_snmp_community",
|
|
323
|
+
"regex:bgp_neighbor_password",
|
|
324
|
+
"regex:cisco_enable_secret",
|
|
325
|
+
"regex:cisco_type7",
|
|
319
326
|
]);
|
|
320
327
|
for (const e of entities) {
|
|
321
328
|
if (e.confidence >= 0.80 &&
|
package/dist/detectors/regex.js
CHANGED
|
@@ -42,6 +42,24 @@ const PUBLIC_DOMAINS = new Set([
|
|
|
42
42
|
"w3.org",
|
|
43
43
|
"archive.org",
|
|
44
44
|
]);
|
|
45
|
+
/**
|
|
46
|
+
* Operational path prefixes — local system/workspace paths that agents need
|
|
47
|
+
* to function. These are NOT sensitive infrastructure to hide from the LLM.
|
|
48
|
+
*/
|
|
49
|
+
const OPERATIONAL_PATH_PREFIXES = [
|
|
50
|
+
"/home/", // user home directories (workspace, scripts, media)
|
|
51
|
+
"/tmp/", // temp files
|
|
52
|
+
"/proc/", // procfs
|
|
53
|
+
"/sys/", // sysfs
|
|
54
|
+
"/dev/", // devices
|
|
55
|
+
"/run/", // runtime data
|
|
56
|
+
"/snap/", // snap packages
|
|
57
|
+
"/root/", // root home
|
|
58
|
+
"/nix/", // nix store
|
|
59
|
+
// NOTE: /etc/, /usr/, /var/, /bin/, /sbin/, /lib/, /opt/ are NOT included —
|
|
60
|
+
// they may contain infrastructure config paths (nginx, systemd units, etc.)
|
|
61
|
+
// that should be obfuscated in network/OT contexts.
|
|
62
|
+
];
|
|
45
63
|
const DOC_HOSTNAMES = new Set([
|
|
46
64
|
"localhost", "HOSTNAME", "EXAMPLE", "CHANGEME",
|
|
47
65
|
"YOUR_HOST", "YOURHOST", "hostname", "example",
|
|
@@ -114,12 +132,18 @@ export function isDocExample(value, category) {
|
|
|
114
132
|
// Private ASNs are real infra identifiers — don't skip them
|
|
115
133
|
return false;
|
|
116
134
|
case Category.FILE_PATH: {
|
|
117
|
-
// Skip paths that are clearly URL path components from public domains.
|
|
118
|
-
// e.g., /www.npmjs.com/package/shroud-privacy, /github.com/org/repo
|
|
119
|
-
// This is a safety net — the span fix in detect() should prevent these,
|
|
120
|
-
// but production environments may have edge cases we can't reproduce.
|
|
121
135
|
if (value.startsWith("/")) {
|
|
122
136
|
const pathLower = value.toLowerCase();
|
|
137
|
+
// Skip operational/system paths — these are local workspace paths the
|
|
138
|
+
// agent needs to function, not sensitive infrastructure to hide from the LLM.
|
|
139
|
+
// Sensitive paths are things like /opt/network-configs/router.cfg on internal
|
|
140
|
+
// servers — those won't match these prefixes.
|
|
141
|
+
for (const pfx of OPERATIONAL_PATH_PREFIXES) {
|
|
142
|
+
if (pathLower.startsWith(pfx))
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
// Skip paths that are clearly URL path components from public domains.
|
|
146
|
+
// e.g., /www.npmjs.com/package/shroud-privacy, /github.com/org/repo
|
|
123
147
|
for (const d of PUBLIC_DOMAINS) {
|
|
124
148
|
if (pathLower.startsWith(`/${d}/`) || pathLower.startsWith(`/${d}`)
|
|
125
149
|
|| pathLower.startsWith(`/www.${d}/`) || pathLower.startsWith(`/www.${d}`)) {
|
|
@@ -616,7 +640,11 @@ export const BUILTIN_PATTERNS = [
|
|
|
616
640
|
},
|
|
617
641
|
{
|
|
618
642
|
name: "gps_coordinate",
|
|
619
|
-
|
|
643
|
+
// Require realistic lat/lon ranges: lat [-90,90], lon [-180,180].
|
|
644
|
+
// Must be comma-separated (not just whitespace — that matches too many
|
|
645
|
+
// false positives in financial data, ML weights, research metrics).
|
|
646
|
+
// Require 4-8 decimal places (GPS precision).
|
|
647
|
+
pattern: /(?<!\w)-?(?:[0-8]?\d(?:\.\d{4,8})|90(?:\.0{4,8}))\s*,\s*-?(?:1[0-7]\d(?:\.\d{4,8})|0?\d{1,2}(?:\.\d{4,8})|180(?:\.0{4,8}))(?!\w)/g,
|
|
620
648
|
category: Category.GPS_COORDINATE,
|
|
621
649
|
confidence: 0.85,
|
|
622
650
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,33 +1,109 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "shroud-privacy",
|
|
3
3
|
"name": "Shroud",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.10",
|
|
5
5
|
"description": "Privacy obfuscation with deterministic fake values and deobfuscation — PII never reaches the LLM, tool calls still work",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
9
9
|
"properties": {
|
|
10
|
-
"secretKey": {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
"secretKey": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "HMAC secret for deterministic mapping. Auto-generated if empty."
|
|
13
|
+
},
|
|
14
|
+
"persistentSalt": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Fixed salt for cross-session consistency. Empty = random per session."
|
|
17
|
+
},
|
|
18
|
+
"minConfidence": {
|
|
19
|
+
"type": "number",
|
|
20
|
+
"minimum": 0,
|
|
21
|
+
"maximum": 1,
|
|
22
|
+
"default": 0,
|
|
23
|
+
"description": "Minimum detector confidence to obfuscate"
|
|
24
|
+
},
|
|
25
|
+
"allowlist": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"default": [],
|
|
31
|
+
"description": "Values to never obfuscate (supports * and ? wildcards)"
|
|
32
|
+
},
|
|
33
|
+
"denylist": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "string"
|
|
37
|
+
},
|
|
38
|
+
"default": [],
|
|
39
|
+
"description": "Values to always obfuscate"
|
|
40
|
+
},
|
|
41
|
+
"canaryEnabled": {
|
|
42
|
+
"type": "boolean",
|
|
43
|
+
"default": false,
|
|
44
|
+
"description": "Inject tracking tokens to detect data leakage"
|
|
45
|
+
},
|
|
46
|
+
"canaryPrefix": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"default": "SHROUD-CANARY",
|
|
49
|
+
"description": "Prefix for canary tokens"
|
|
50
|
+
},
|
|
51
|
+
"auditEnabled": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"default": false,
|
|
54
|
+
"description": "Track obfuscation events with tamper-evident chain hashing"
|
|
55
|
+
},
|
|
56
|
+
"verboseLogging": {
|
|
57
|
+
"type": "boolean",
|
|
58
|
+
"default": false,
|
|
59
|
+
"description": "Alias for auditEnabled — enable verbose audit lines"
|
|
60
|
+
},
|
|
61
|
+
"auditLogFormat": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"enum": [
|
|
64
|
+
"human",
|
|
65
|
+
"json"
|
|
66
|
+
],
|
|
67
|
+
"default": "human",
|
|
68
|
+
"description": "Audit log output format"
|
|
69
|
+
},
|
|
70
|
+
"auditIncludeProofHashes": {
|
|
71
|
+
"type": "boolean",
|
|
72
|
+
"default": false,
|
|
73
|
+
"description": "Include salted SHA-256 proof hashes in audit lines"
|
|
74
|
+
},
|
|
75
|
+
"auditHashSalt": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"default": "",
|
|
78
|
+
"description": "Salt for proof hashes"
|
|
79
|
+
},
|
|
80
|
+
"auditHashTruncate": {
|
|
81
|
+
"type": "integer",
|
|
82
|
+
"default": 12,
|
|
83
|
+
"minimum": 4,
|
|
84
|
+
"maximum": 64,
|
|
85
|
+
"description": "Truncate proof hashes to N hex chars"
|
|
86
|
+
},
|
|
87
|
+
"auditMaxFakesSample": {
|
|
88
|
+
"type": "integer",
|
|
89
|
+
"default": 0,
|
|
90
|
+
"minimum": 0,
|
|
91
|
+
"maximum": 20,
|
|
92
|
+
"description": "Include up to N fake replacement values in audit log (0 = disabled)"
|
|
93
|
+
},
|
|
24
94
|
"detectorOverrides": {
|
|
25
95
|
"type": "object",
|
|
26
96
|
"additionalProperties": {
|
|
27
97
|
"type": "object",
|
|
28
98
|
"properties": {
|
|
29
|
-
"enabled": {
|
|
30
|
-
|
|
99
|
+
"enabled": {
|
|
100
|
+
"type": "boolean"
|
|
101
|
+
},
|
|
102
|
+
"confidence": {
|
|
103
|
+
"type": "number",
|
|
104
|
+
"minimum": 0,
|
|
105
|
+
"maximum": 1
|
|
106
|
+
}
|
|
31
107
|
}
|
|
32
108
|
},
|
|
33
109
|
"default": {},
|
|
@@ -38,27 +114,76 @@
|
|
|
38
114
|
"items": {
|
|
39
115
|
"type": "object",
|
|
40
116
|
"properties": {
|
|
41
|
-
"name": {
|
|
42
|
-
|
|
43
|
-
|
|
117
|
+
"name": {
|
|
118
|
+
"type": "string"
|
|
119
|
+
},
|
|
120
|
+
"pattern": {
|
|
121
|
+
"type": "string"
|
|
122
|
+
},
|
|
123
|
+
"category": {
|
|
124
|
+
"type": "string"
|
|
125
|
+
}
|
|
44
126
|
},
|
|
45
|
-
"required": [
|
|
127
|
+
"required": [
|
|
128
|
+
"name",
|
|
129
|
+
"pattern"
|
|
130
|
+
]
|
|
46
131
|
},
|
|
47
132
|
"default": [],
|
|
48
133
|
"description": "User-defined regex patterns for custom PII detection"
|
|
49
134
|
},
|
|
50
|
-
"maxToolDepth": {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
135
|
+
"maxToolDepth": {
|
|
136
|
+
"type": "integer",
|
|
137
|
+
"default": 10,
|
|
138
|
+
"minimum": 1,
|
|
139
|
+
"maximum": 100,
|
|
140
|
+
"description": "Max nested tool call depth before warning"
|
|
141
|
+
},
|
|
142
|
+
"redactionLevel": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"enum": [
|
|
145
|
+
"full",
|
|
146
|
+
"masked",
|
|
147
|
+
"stats"
|
|
148
|
+
],
|
|
149
|
+
"default": "full",
|
|
150
|
+
"description": "Output mode: full (fake values), masked (partial masking), stats (category placeholders)"
|
|
151
|
+
},
|
|
152
|
+
"dryRun": {
|
|
153
|
+
"type": "boolean",
|
|
154
|
+
"default": false,
|
|
155
|
+
"description": "Detect entities but don't replace — useful for testing detection rules"
|
|
156
|
+
},
|
|
157
|
+
"maxStoreMappings": {
|
|
158
|
+
"type": "integer",
|
|
159
|
+
"default": 0,
|
|
160
|
+
"minimum": 0,
|
|
161
|
+
"description": "Max mapping store size; oldest entries evicted when exceeded. 0 = unlimited."
|
|
162
|
+
}
|
|
54
163
|
}
|
|
55
164
|
},
|
|
56
165
|
"uiHints": {
|
|
57
|
-
"secretKey": {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
166
|
+
"secretKey": {
|
|
167
|
+
"label": "Secret Key",
|
|
168
|
+
"help": "HMAC secret for deterministic mapping. Auto-generated if empty.",
|
|
169
|
+
"sensitive": true
|
|
170
|
+
},
|
|
171
|
+
"persistentSalt": {
|
|
172
|
+
"label": "Persistent Salt",
|
|
173
|
+
"help": "Fixed salt for cross-session mapping consistency."
|
|
174
|
+
},
|
|
175
|
+
"minConfidence": {
|
|
176
|
+
"label": "Min Confidence",
|
|
177
|
+
"help": "Detection confidence threshold (0.0-1.0)"
|
|
178
|
+
},
|
|
179
|
+
"canaryEnabled": {
|
|
180
|
+
"label": "Enable Canary Tokens",
|
|
181
|
+
"help": "Inject tracking tokens to detect PII leakage"
|
|
182
|
+
},
|
|
183
|
+
"auditEnabled": {
|
|
184
|
+
"label": "Enable Audit Log",
|
|
185
|
+
"help": "Tamper-evident obfuscation event tracking"
|
|
186
|
+
}
|
|
62
187
|
},
|
|
63
188
|
"enterpriseAgent": {
|
|
64
189
|
"adapter": "ncg_adapter.py",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shroud-privacy",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.10",
|
|
4
4
|
"description": "Privacy and infrastructure protection for AI agents — detects sensitive data (PII, network topology, credentials, OT/SCADA) and replaces with deterministic fakes before anything reaches the LLM.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc",
|
|
22
|
-
"test": "npm run test:unit && npm run test:integration
|
|
22
|
+
"test": "npm run test:unit && npm run test:integration",
|
|
23
23
|
"test:unit": "vitest run",
|
|
24
24
|
"test:integration": "node tests/harness/run.mjs",
|
|
25
|
-
"test:
|
|
26
|
-
"test:all": "npm run test:unit && npm run test:integration && npm run test:
|
|
25
|
+
"test:docker": "bash compat/run-compat.sh latest",
|
|
26
|
+
"test:all": "npm run test:unit && npm run test:integration && npm run test:docker",
|
|
27
27
|
"test:watch": "vitest",
|
|
28
28
|
"lint": "tsc --noEmit",
|
|
29
29
|
"prepublishOnly": "npm run lint && npm run test:unit && npm run test:integration && npm run build"
|