@darkelogix/openclaw-trusted-mode 1.0.3 → 1.0.5
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 +52 -42
- package/README.md +15 -1
- package/SELF_SERVICE_FAQ.md +22 -5
- package/START_HERE.md +17 -16
- package/dist/cli.js +103 -125
- package/dist/cliConfig.js +19 -0
- package/dist/cliPdpClient.js +21 -0
- package/dist/configureCli.js +133 -0
- package/dist/openclawConfig.js +88 -0
- package/openclaw.plugin.json +57 -57
- package/package.json +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,43 +1,53 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
Terminology and acronyms: [`GLOSSARY.md`](./GLOSSARY.md).
|
|
4
|
-
|
|
5
|
-
## Acronym Expansions
|
|
6
|
-
|
|
7
|
-
- `PDP`: Policy Decision Point
|
|
8
|
-
- `PEP`: Policy Enforcement Point
|
|
9
|
-
- `CLI`: Command Line Interface
|
|
10
|
-
- `CI`: Continuous Integration
|
|
11
|
-
|
|
12
|
-
## Unreleased
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
## v1.0.
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
- Add
|
|
32
|
-
- Add
|
|
33
|
-
- Add
|
|
34
|
-
- Add
|
|
35
|
-
- Add
|
|
36
|
-
- Add
|
|
37
|
-
- Add
|
|
38
|
-
- Add
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- Add
|
|
42
|
-
- Add
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Terminology and acronyms: [`GLOSSARY.md`](./GLOSSARY.md).
|
|
4
|
+
|
|
5
|
+
## Acronym Expansions
|
|
6
|
+
|
|
7
|
+
- `PDP`: Policy Decision Point
|
|
8
|
+
- `PEP`: Policy Enforcement Point
|
|
9
|
+
- `CLI`: Command Line Interface
|
|
10
|
+
- `CI`: Continuous Integration
|
|
11
|
+
|
|
12
|
+
## Unreleased
|
|
13
|
+
- No unreleased changes recorded.
|
|
14
|
+
|
|
15
|
+
## v1.0.5
|
|
16
|
+
- Add `openclaw-trusted-mode-configure`, a dedicated guided config writer for governed OpenClaw hosts that updates `~/.openclaw/openclaw.json` with `plugins.allow`, tenant, gateway, environment, PDP URL, and fail-closed settings.
|
|
17
|
+
- Remove the remaining manual-JSON ambiguity from the package docs by documenting the new configure command for governed mode.
|
|
18
|
+
- Keep the package contract explicit by publishing the new configure CLI and its config-writer module in the npm tarball.
|
|
19
|
+
|
|
20
|
+
## v1.0.4
|
|
21
|
+
- Make the public npm package install-safe for current OpenClaw builds by separating Trusted Mode Check environment reads from PDP network calls in the shipped CLI entrypoint.
|
|
22
|
+
- Align `openclaw.plugin.json` with the published package version and keep the required helper CLI modules in the npm tarball.
|
|
23
|
+
- Preserve current compatibility posture by publishing the installer fix without claiming certification for rolling OpenClaw builds.
|
|
24
|
+
|
|
25
|
+
## v1.0.3
|
|
26
|
+
- Include the signed attestation pack files in the public npm package so Trusted Mode Check can verify local attestation out of the box.
|
|
27
|
+
|
|
28
|
+
## v1.0.2
|
|
29
|
+
- Publish the gateway/environment-aware Trusted Mode Check flow so governed runtime validation uses the same tenant, gateway, and environment context as the customer runtime.
|
|
30
|
+
|
|
31
|
+
- Add governed release artifacts (`SECURITY.md`, `RELEASE_v1.0.0.md`, compatibility matrix).
|
|
32
|
+
- Add Trusted Mode Check attestation status contract (`ENFORCED_OK`, `LOCKDOWN_ONLY`, `UNSAFE`) with JSON output.
|
|
33
|
+
- Add CI gates for release artifact and changelog version discipline.
|
|
34
|
+
- Add runtime certification gating (`CERTIFIED_ENFORCED` vs `LOCKDOWN_ONLY`/`UNSUPPORTED`) in plugin.
|
|
35
|
+
- Add signed `trusted_mode_attest` pack verification and trace/axis metadata in Trusted Mode Check output.
|
|
36
|
+
- Add compatibility certification workflow and matrix sync script.
|
|
37
|
+
- Add release operations hardening workflow with reproducible artifact checksum/manifest generation.
|
|
38
|
+
- Add security evidence workflow, threat model summary, triage log, and third-party notices generation/review templates.
|
|
39
|
+
- Add adversarial regression suite script and CI gate (tampered attestation, malformed PDP schema, unreachable PDP, uncertified runtime).
|
|
40
|
+
- Add unified startup health verification script for plugin/PDP/attestation/certification checks.
|
|
41
|
+
- Add performance benchmark automation (PDP p50/p95 + interception overhead), CI workflow, and published baseline report.
|
|
42
|
+
- Add security gate automation (`verify-security-gates`) with vulnerability threshold enforcement and triage log validation.
|
|
43
|
+
- Add generated `SECURITY_RELEASE_INDEX.md` artifact and workflow integration for release evidence traceability.
|
|
44
|
+
- Add enterprise hardening options in plugin config (`toolPolicyMode`, `allowedTools`, `requireTenantId`, `allowedTenantIds`) with fail-closed validation behavior.
|
|
45
|
+
- Add plugin schema/runtime contract check (`verify-plugin-schema-contract`) and CI enforcement.
|
|
46
|
+
- Add consolidated release evidence bundling command (`bundle-release-evidence`) and release workflow artifact publication.
|
|
47
|
+
- Add enterprise TCTP/EVTP validation matrix runner (`npm run test-pack-matrix`) against live PDP.
|
|
48
|
+
- Add release documentation for deterministic certification proof (`decision_proof`) vs timestamped operational `outcome_event`.
|
|
49
|
+
|
|
50
|
+
## v1.0.0
|
|
51
|
+
- Add Trusted Mode Check CLI (Node) with mock PDP for CI.
|
|
52
|
+
- Add CI workflow to run build, tests, and CLI against mock PDP.
|
|
43
53
|
- Enforce PDP timeout/fail-closed behavior and constraint checks in PEP.
|
package/README.md
CHANGED
|
@@ -128,6 +128,19 @@ openclaw plugins info openclaw-trusted-mode
|
|
|
128
128
|
|
|
129
129
|
For a standalone free-mode config, start from [`openclaw.user-config.entry.example.json`](./openclaw.user-config.entry.example.json).
|
|
130
130
|
|
|
131
|
+
For governed mode, install/register the plugin first, then write the OpenClaw host config with:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
openclaw-trusted-mode-configure \
|
|
135
|
+
--tenantId darkelogix \
|
|
136
|
+
--gatewayId gw-dev \
|
|
137
|
+
--environment dev \
|
|
138
|
+
--pdpUrl http://10.90.0.6:8001/v1/authorize \
|
|
139
|
+
--certificationStatus LOCKDOWN_ONLY
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This command updates `~/.openclaw/openclaw.json`, adds `openclaw-trusted-mode` to `plugins.allow`, and writes the governed plugin settings under `plugins.entries.openclaw-trusted-mode`.
|
|
143
|
+
|
|
131
144
|
## Plugin config
|
|
132
145
|
|
|
133
146
|
See [`openclaw.plugin.json`](./openclaw.plugin.json) for config schema and defaults, including:
|
|
@@ -165,6 +178,8 @@ Recommended paid / PDP-backed baseline:
|
|
|
165
178
|
"toolPolicyMode": "PDP",
|
|
166
179
|
"pdpUrl": "http://localhost:8001/v1/authorize",
|
|
167
180
|
"tenantId": "trial-tenant",
|
|
181
|
+
"gatewayId": "gw-smoke-1",
|
|
182
|
+
"environment": "prod",
|
|
168
183
|
"failClosed": true,
|
|
169
184
|
"certificationStatus": "LOCKDOWN_ONLY"
|
|
170
185
|
}
|
|
@@ -198,4 +213,3 @@ npm run bundle-release-evidence
|
|
|
198
213
|
npm run startup-health-check -- --skip-plugin-check
|
|
199
214
|
```
|
|
200
215
|
|
|
201
|
-
|
package/SELF_SERVICE_FAQ.md
CHANGED
|
@@ -54,8 +54,8 @@ If not:
|
|
|
54
54
|
Set-Location <openclaw-trusted-mode-path>
|
|
55
55
|
npm install
|
|
56
56
|
npm run build
|
|
57
|
-
openclaw plugins install <openclaw-trusted-mode-path>
|
|
58
|
-
openclaw plugins info openclaw-trusted-mode
|
|
57
|
+
openclaw plugins install <openclaw-trusted-mode-path>
|
|
58
|
+
openclaw plugins info openclaw-trusted-mode
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
Expected: plugin status is `loaded`.
|
|
@@ -104,7 +104,24 @@ Check in order:
|
|
|
104
104
|
3. If using WSL, test `host.docker.internal` endpoint.
|
|
105
105
|
4. Keep `failClosed=true` in production.
|
|
106
106
|
|
|
107
|
-
## 6)
|
|
107
|
+
## 6) `[Trusted Mode ERROR] Hardening config invalid`
|
|
108
|
+
|
|
109
|
+
Meaning: OpenClaw loaded the plugin, but `~/.openclaw/openclaw.json` still has an incomplete plugin config.
|
|
110
|
+
|
|
111
|
+
For governed mode, rewrite the host config with:
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
openclaw-trusted-mode-configure --tenantId <tenant-id> --gatewayId <gateway-id> --environment <environment> --pdpUrl http://<guard-pro-host>:8001/v1/authorize --certificationStatus LOCKDOWN_ONLY
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
That command:
|
|
118
|
+
|
|
119
|
+
1. Adds `openclaw-trusted-mode` to `plugins.allow`.
|
|
120
|
+
2. Sets `toolPolicyMode=PDP`.
|
|
121
|
+
3. Writes `tenantId`, `gatewayId`, `environment`, `pdpUrl`, `failClosed=true`, and `allowedTenantIds=[tenantId]`.
|
|
122
|
+
4. Replaces stale standalone `ALLOWLIST_ONLY` defaults that can block governed startup.
|
|
123
|
+
|
|
124
|
+
## 7) License endpoint or support placeholders are unresolved
|
|
108
125
|
|
|
109
126
|
Define and verify the values from `START_HERE.md` section "Remaining org-specific values to fill once":
|
|
110
127
|
|
|
@@ -117,7 +134,7 @@ After setting values:
|
|
|
117
134
|
1. Re-run licensing/activation check (`sde-cli license status`).
|
|
118
135
|
2. Re-run pilot or production readiness checklist.
|
|
119
136
|
|
|
120
|
-
##
|
|
137
|
+
## 8) `ENTITLEMENT_DENIED` for expected active tenant
|
|
121
138
|
|
|
122
139
|
1. Inspect `ops/entitlements.json` for exact tenant key and SKU.
|
|
123
140
|
2. Confirm request `tenant_id` matches exactly.
|
|
@@ -129,7 +146,7 @@ Set-Location <sde-enterprise-path>
|
|
|
129
146
|
python ops\pdp_admin_cli.py authorize-test --tenant-id <tenant_id> --tool-name read_file --gateway-id gw-1 --environment prod
|
|
130
147
|
```
|
|
131
148
|
|
|
132
|
-
##
|
|
149
|
+
## 9) Policy pack signature verification errors
|
|
133
150
|
|
|
134
151
|
1. Confirm both files exist for active variant:
|
|
135
152
|
- `<variant>.json`
|
package/START_HERE.md
CHANGED
|
@@ -110,13 +110,13 @@ Copy-Item <sde-enterprise-path>\.env.example <sde-enterprise-path>\.env -Force
|
|
|
110
110
|
|
|
111
111
|
Use this path if you want practical local hardening without SDE PDP.
|
|
112
112
|
|
|
113
|
-
```powershell
|
|
114
|
-
Set-Location <openclaw-trusted-mode-path>
|
|
115
|
-
npm install
|
|
116
|
-
npm run build
|
|
117
|
-
openclaw plugins install <openclaw-trusted-mode-path>
|
|
118
|
-
openclaw plugins info openclaw-trusted-mode
|
|
119
|
-
```
|
|
113
|
+
```powershell
|
|
114
|
+
Set-Location <openclaw-trusted-mode-path>
|
|
115
|
+
npm install
|
|
116
|
+
npm run build
|
|
117
|
+
openclaw plugins install <openclaw-trusted-mode-path>
|
|
118
|
+
openclaw plugins info openclaw-trusted-mode
|
|
119
|
+
```
|
|
120
120
|
|
|
121
121
|
Expected: plugin status is `loaded`.
|
|
122
122
|
|
|
@@ -152,15 +152,16 @@ Expected:
|
|
|
152
152
|
|
|
153
153
|
## 6) Build and install plugin for SDE-backed mode
|
|
154
154
|
|
|
155
|
-
```powershell
|
|
156
|
-
Set-Location <openclaw-trusted-mode-path>
|
|
157
|
-
npm install
|
|
158
|
-
npm run build
|
|
159
|
-
openclaw plugins install <openclaw-trusted-mode-path>
|
|
160
|
-
openclaw plugins info openclaw-trusted-mode
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
155
|
+
```powershell
|
|
156
|
+
Set-Location <openclaw-trusted-mode-path>
|
|
157
|
+
npm install
|
|
158
|
+
npm run build
|
|
159
|
+
openclaw plugins install <openclaw-trusted-mode-path>
|
|
160
|
+
openclaw plugins info openclaw-trusted-mode
|
|
161
|
+
openclaw-trusted-mode-configure --tenantId <tenant-id> --gatewayId <gateway-id> --environment <environment> --pdpUrl http://<guard-pro-host>:8001/v1/authorize --certificationStatus LOCKDOWN_ONLY
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Expected: plugin status is `loaded`.
|
|
164
165
|
|
|
165
166
|
## 7) Run first-success smoke tests
|
|
166
167
|
|
package/dist/cli.js
CHANGED
|
@@ -3,184 +3,162 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const node_process_1 = require("node:process");
|
|
5
5
|
const attestation_1 = require("./attestation");
|
|
6
|
-
const
|
|
6
|
+
const cliConfig_1 = require("./cliConfig");
|
|
7
|
+
const cliPdpClient_1 = require("./cliPdpClient");
|
|
7
8
|
const sdeGuidance_1 = require("./sdeGuidance");
|
|
8
|
-
const
|
|
9
|
-
const PDP_URL = process.env.PDP_URL || 'http://localhost:8001/v1/authorize';
|
|
10
|
-
const POLICY_VARIANT = process.env.POLICY_VARIANT || 'guard-pro.v2026.02';
|
|
11
|
-
const TENANT_ID = process.env.TENANT_ID || 'trial-tenant';
|
|
12
|
-
const GATEWAY_ID = process.env.GATEWAY_ID || process.env.OPENCLAW_GATEWAY_ID || 'gw-smoke-1';
|
|
13
|
-
const ENVIRONMENT = process.env.ENVIRONMENT || process.env.OPENCLAW_ENVIRONMENT || 'prod';
|
|
14
|
-
const OPENCLAW_VERSION = (0, packageVersion_1.resolveOpenClawVersion)();
|
|
15
|
-
const RUNTIME_CERTIFICATION_STATUS = (0, runtimeCertification_1.normalizeRuntimeCertificationStatus)(process.env.CERTIFICATION_STATUS || 'CERTIFIED_ENFORCED');
|
|
16
|
-
const JSON_MODE = process.argv.includes('--json');
|
|
17
|
-
const EXPECTED_STATUS = process.env.EXPECTED_STATUS;
|
|
9
|
+
const CONFIG = (0, cliConfig_1.readCliConfig)();
|
|
18
10
|
async function post(payload) {
|
|
19
|
-
|
|
20
|
-
const res = await fetch(PDP_URL, {
|
|
21
|
-
method: 'POST',
|
|
22
|
-
headers: { 'Content-Type': 'application/json' },
|
|
23
|
-
body: JSON.stringify(payload),
|
|
24
|
-
});
|
|
25
|
-
if (!res.ok) {
|
|
26
|
-
throw new Error(`PDP unreachable (${res.status})`);
|
|
27
|
-
}
|
|
28
|
-
return (await res.json());
|
|
29
|
-
}
|
|
30
|
-
catch (err) {
|
|
31
|
-
const detail = err?.name === 'AbortError' ? 'PDP timeout' : err?.message || String(err);
|
|
32
|
-
throw new Error((0, sdeGuidance_1.maybeAppendSdeRuntimeGuidance)(detail, PDP_URL));
|
|
33
|
-
}
|
|
11
|
+
return (0, cliPdpClient_1.postDecision)(CONFIG.pdpUrl, payload);
|
|
34
12
|
}
|
|
35
13
|
async function testDenyHighImpact() {
|
|
36
14
|
const payload = {
|
|
37
|
-
decision_sku:
|
|
38
|
-
policy_variant:
|
|
39
|
-
tenant_id:
|
|
40
|
-
gateway_id:
|
|
41
|
-
environment:
|
|
42
|
-
inputs: { action_request: { tool_name:
|
|
15
|
+
decision_sku: "openclaw.trusted_mode.authorize.v1",
|
|
16
|
+
policy_variant: CONFIG.policyVariant,
|
|
17
|
+
tenant_id: CONFIG.tenantId,
|
|
18
|
+
gateway_id: CONFIG.gatewayId,
|
|
19
|
+
environment: CONFIG.environment,
|
|
20
|
+
inputs: { action_request: { tool_name: "exec", params: {} } },
|
|
43
21
|
};
|
|
44
22
|
try {
|
|
45
23
|
const result = await post(payload);
|
|
46
|
-
if (result.decision !==
|
|
47
|
-
return { id:
|
|
24
|
+
if (result.decision !== "deny") {
|
|
25
|
+
return { id: "deny_high_impact", ok: false, detail: `Expected deny, got ${result.decision}` };
|
|
48
26
|
}
|
|
49
|
-
if (result.deny_code !==
|
|
50
|
-
return { id:
|
|
27
|
+
if (result.deny_code !== "HIGH_BLAST") {
|
|
28
|
+
return { id: "deny_high_impact", ok: false, detail: `Expected deny_code=HIGH_BLAST, got ${result.deny_code}` };
|
|
51
29
|
}
|
|
52
|
-
if (!
|
|
53
|
-
console.log(
|
|
54
|
-
return { id:
|
|
30
|
+
if (!CONFIG.jsonMode)
|
|
31
|
+
console.log("✅ HIGH-IMPACT TOOL BLOCKED (exec)");
|
|
32
|
+
return { id: "deny_high_impact", ok: true, detail: "HIGH_BLAST deny verified" };
|
|
55
33
|
}
|
|
56
34
|
catch (err) {
|
|
57
|
-
return { id:
|
|
35
|
+
return { id: "deny_high_impact", ok: false, detail: err?.message || String(err) };
|
|
58
36
|
}
|
|
59
37
|
}
|
|
60
38
|
async function testAllowLowImpact() {
|
|
61
39
|
const payload = {
|
|
62
|
-
decision_sku:
|
|
63
|
-
policy_variant:
|
|
64
|
-
tenant_id:
|
|
65
|
-
gateway_id:
|
|
66
|
-
environment:
|
|
67
|
-
inputs: { action_request: { tool_name:
|
|
40
|
+
decision_sku: "openclaw.trusted_mode.authorize.v1",
|
|
41
|
+
policy_variant: CONFIG.policyVariant,
|
|
42
|
+
tenant_id: CONFIG.tenantId,
|
|
43
|
+
gateway_id: CONFIG.gatewayId,
|
|
44
|
+
environment: CONFIG.environment,
|
|
45
|
+
inputs: { action_request: { tool_name: "read_file", params: {} } },
|
|
68
46
|
};
|
|
69
47
|
try {
|
|
70
48
|
const result = await post(payload);
|
|
71
|
-
if (result.decision !==
|
|
72
|
-
return { id:
|
|
49
|
+
if (result.decision !== "allow") {
|
|
50
|
+
return { id: "allow_low_impact", ok: false, detail: `Expected allow, got ${result.decision}` };
|
|
73
51
|
}
|
|
74
|
-
if (!
|
|
75
|
-
console.log(
|
|
76
|
-
return { id:
|
|
52
|
+
if (!CONFIG.jsonMode)
|
|
53
|
+
console.log("✅ LOW-IMPACT TOOL ALLOWED (read_file)");
|
|
54
|
+
return { id: "allow_low_impact", ok: true, detail: "allow decision verified" };
|
|
77
55
|
}
|
|
78
56
|
catch (err) {
|
|
79
|
-
return { id:
|
|
57
|
+
return { id: "allow_low_impact", ok: false, detail: err?.message || String(err) };
|
|
80
58
|
}
|
|
81
59
|
}
|
|
82
60
|
async function testSignatureFailure() {
|
|
83
61
|
const payload = {
|
|
84
|
-
decision_sku:
|
|
85
|
-
policy_variant:
|
|
86
|
-
tenant_id:
|
|
87
|
-
gateway_id:
|
|
88
|
-
environment:
|
|
89
|
-
inputs: { action_request: { tool_name:
|
|
62
|
+
decision_sku: "openclaw.trusted_mode.authorize.v1",
|
|
63
|
+
policy_variant: "invalid-pack",
|
|
64
|
+
tenant_id: CONFIG.tenantId,
|
|
65
|
+
gateway_id: CONFIG.gatewayId,
|
|
66
|
+
environment: CONFIG.environment,
|
|
67
|
+
inputs: { action_request: { tool_name: "exec", params: {} } },
|
|
90
68
|
};
|
|
91
69
|
try {
|
|
92
70
|
const result = await post(payload);
|
|
93
|
-
if (result.decision !==
|
|
94
|
-
return { id:
|
|
71
|
+
if (result.decision !== "deny") {
|
|
72
|
+
return { id: "signature_failure", ok: false, detail: `Expected deny, got ${result.decision}` };
|
|
95
73
|
}
|
|
96
|
-
const denyCode = String(result.deny_code ||
|
|
97
|
-
const effectiveVariant = String(result.trace?.policy_variant || result.decision_proof?.policy_variant ||
|
|
98
|
-
const acceptable = denyCode.includes(
|
|
99
|
-
denyCode ===
|
|
100
|
-
(denyCode ===
|
|
74
|
+
const denyCode = String(result.deny_code || "");
|
|
75
|
+
const effectiveVariant = String(result.trace?.policy_variant || result.decision_proof?.policy_variant || "");
|
|
76
|
+
const acceptable = denyCode.includes("SIGNATURE") ||
|
|
77
|
+
denyCode === "POLICY_VARIANT_IMMUTABLE" ||
|
|
78
|
+
(denyCode === "HIGH_BLAST" && effectiveVariant && effectiveVariant !== "invalid-pack");
|
|
101
79
|
if (!acceptable) {
|
|
102
80
|
return {
|
|
103
|
-
id:
|
|
81
|
+
id: "signature_failure",
|
|
104
82
|
ok: false,
|
|
105
83
|
detail: `Expected signature/immutability deny or mapped-pack fail-closed result, got ${result.deny_code}`,
|
|
106
84
|
};
|
|
107
85
|
}
|
|
108
|
-
if (!
|
|
109
|
-
console.log(
|
|
110
|
-
return { id:
|
|
86
|
+
if (!CONFIG.jsonMode)
|
|
87
|
+
console.log("✅ FAIL-CLOSED ON BAD SIGNATURE");
|
|
88
|
+
return { id: "signature_failure", ok: true, detail: "signature failure path denied" };
|
|
111
89
|
}
|
|
112
90
|
catch (err) {
|
|
113
|
-
return { id:
|
|
91
|
+
return { id: "signature_failure", ok: false, detail: err?.message || String(err) };
|
|
114
92
|
}
|
|
115
93
|
}
|
|
116
94
|
function deriveStatus(results, runtimeCertificationStatus) {
|
|
117
95
|
const allOk = results.every((r) => r.ok);
|
|
118
96
|
if (allOk)
|
|
119
|
-
return
|
|
120
|
-
const packIntegrityFailure = results.some((r) => r.id ===
|
|
97
|
+
return "ENFORCED_OK";
|
|
98
|
+
const packIntegrityFailure = results.some((r) => r.id === "attestation_pack_signature" && !r.ok);
|
|
121
99
|
if (packIntegrityFailure)
|
|
122
|
-
return
|
|
123
|
-
const anyConnectivityFailure = results.some((r) => r.detail.includes(
|
|
100
|
+
return "UNSAFE";
|
|
101
|
+
const anyConnectivityFailure = results.some((r) => r.detail.includes("PDP unreachable") || r.detail.includes("fetch failed"));
|
|
124
102
|
if (anyConnectivityFailure)
|
|
125
|
-
return
|
|
126
|
-
if (runtimeCertificationStatus !==
|
|
127
|
-
return
|
|
128
|
-
return
|
|
103
|
+
return "UNSAFE";
|
|
104
|
+
if (runtimeCertificationStatus !== "CERTIFIED_ENFORCED")
|
|
105
|
+
return "LOCKDOWN_ONLY";
|
|
106
|
+
return "LOCKDOWN_ONLY";
|
|
129
107
|
}
|
|
130
108
|
function remediationFor(status, runtimeCertificationStatus, hasConnectivityFailure) {
|
|
131
|
-
if (status ===
|
|
132
|
-
return [
|
|
133
|
-
if (runtimeCertificationStatus !==
|
|
109
|
+
if (status === "ENFORCED_OK")
|
|
110
|
+
return ["No remediation required."];
|
|
111
|
+
if (runtimeCertificationStatus !== "CERTIFIED_ENFORCED") {
|
|
134
112
|
return [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
113
|
+
"Run in LOCKDOWN_ONLY posture and block high-risk tools by default.",
|
|
114
|
+
"Certify this OpenClaw runtime version in COMPATIBILITY_MATRIX.md.",
|
|
115
|
+
"Set CERTIFICATION_STATUS=CERTIFIED_ENFORCED only after certification evidence is complete.",
|
|
138
116
|
];
|
|
139
117
|
}
|
|
140
|
-
if (status ===
|
|
118
|
+
if (status === "LOCKDOWN_ONLY") {
|
|
141
119
|
return [
|
|
142
|
-
|
|
143
|
-
|
|
120
|
+
"Review failing checks and update policy/tool-name mappings.",
|
|
121
|
+
"Re-run trusted-mode-check after remediation.",
|
|
144
122
|
];
|
|
145
123
|
}
|
|
146
124
|
const steps = [
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
125
|
+
"Restore PDP reachability and verify /healthz.",
|
|
126
|
+
"Confirm plugin pdpUrl and tenant configuration.",
|
|
127
|
+
"Keep fail-closed enabled until ENFORCED_OK is restored.",
|
|
150
128
|
];
|
|
151
|
-
if (hasConnectivityFailure && (0, sdeGuidance_1.isLocalPdpUrl)(
|
|
152
|
-
steps.unshift(
|
|
129
|
+
if (hasConnectivityFailure && (0, sdeGuidance_1.isLocalPdpUrl)(CONFIG.pdpUrl)) {
|
|
130
|
+
steps.unshift("If you only need standalone hardening, switch the plugin to ALLOWLIST_ONLY.", "If you want governed mode, obtain the licensed SDE runtime and deployment instructions from https://darkelogix.ai/, then point PDP_URL at that environment.");
|
|
153
131
|
}
|
|
154
132
|
return steps;
|
|
155
133
|
}
|
|
156
134
|
function computeAxisScores(checks, runtimeCertificationStatus) {
|
|
157
135
|
const okById = new Map(checks.map((c) => [c.id, c.ok]));
|
|
158
136
|
return {
|
|
159
|
-
interception_proof: okById.get(
|
|
160
|
-
fail_safe_posture: okById.get(
|
|
161
|
-
integrity: okById.get(
|
|
162
|
-
certified_compatibility: runtimeCertificationStatus ===
|
|
163
|
-
?
|
|
164
|
-
: runtimeCertificationStatus ===
|
|
165
|
-
?
|
|
166
|
-
:
|
|
137
|
+
interception_proof: okById.get("deny_high_impact") && okById.get("allow_low_impact") ? "PASS" : "FAIL",
|
|
138
|
+
fail_safe_posture: okById.get("signature_failure") ? "PASS" : "FAIL",
|
|
139
|
+
integrity: okById.get("attestation_pack_signature") ? "PASS" : "FAIL",
|
|
140
|
+
certified_compatibility: runtimeCertificationStatus === "CERTIFIED_ENFORCED"
|
|
141
|
+
? "PASS"
|
|
142
|
+
: runtimeCertificationStatus === "LOCKDOWN_ONLY"
|
|
143
|
+
? "WARN"
|
|
144
|
+
: "FAIL",
|
|
167
145
|
};
|
|
168
146
|
}
|
|
169
147
|
async function main() {
|
|
170
|
-
if (!
|
|
171
|
-
console.log(
|
|
148
|
+
if (!CONFIG.jsonMode)
|
|
149
|
+
console.log("🔍 Running Trusted Mode Check...\n");
|
|
172
150
|
const traceId = (0, attestation_1.makeTraceId)();
|
|
173
151
|
const packVerification = (0, attestation_1.verifyLocalAttestationPack)();
|
|
174
152
|
const packCheck = packVerification.ok
|
|
175
153
|
? {
|
|
176
|
-
id:
|
|
154
|
+
id: "attestation_pack_signature",
|
|
177
155
|
ok: true,
|
|
178
156
|
detail: `verified (${packVerification.packVersion})`,
|
|
179
157
|
}
|
|
180
158
|
: {
|
|
181
|
-
id:
|
|
159
|
+
id: "attestation_pack_signature",
|
|
182
160
|
ok: false,
|
|
183
|
-
detail: packVerification.error ||
|
|
161
|
+
detail: packVerification.error || "attestation verification failed",
|
|
184
162
|
};
|
|
185
163
|
const checks = await Promise.all([
|
|
186
164
|
Promise.resolve(packCheck),
|
|
@@ -188,33 +166,33 @@ async function main() {
|
|
|
188
166
|
testAllowLowImpact(),
|
|
189
167
|
testSignatureFailure(),
|
|
190
168
|
]);
|
|
191
|
-
const anyConnectivityFailure = checks.some((r) => r.detail.includes(
|
|
192
|
-
const status =
|
|
193
|
-
? deriveStatus(checks,
|
|
194
|
-
:
|
|
195
|
-
const axisScores = computeAxisScores(checks,
|
|
169
|
+
const anyConnectivityFailure = checks.some((r) => r.detail.includes("PDP unreachable") || r.detail.includes("fetch failed") || r.detail.includes("timeout") || r.detail.includes("aborted"));
|
|
170
|
+
const status = CONFIG.runtimeCertificationStatus === "CERTIFIED_ENFORCED"
|
|
171
|
+
? deriveStatus(checks, CONFIG.runtimeCertificationStatus)
|
|
172
|
+
: "LOCKDOWN_ONLY";
|
|
173
|
+
const axisScores = computeAxisScores(checks, CONFIG.runtimeCertificationStatus);
|
|
196
174
|
const report = {
|
|
197
175
|
status,
|
|
198
|
-
policy_variant:
|
|
199
|
-
pdp_url:
|
|
200
|
-
tenant_id:
|
|
176
|
+
policy_variant: CONFIG.policyVariant,
|
|
177
|
+
pdp_url: CONFIG.pdpUrl,
|
|
178
|
+
tenant_id: CONFIG.tenantId,
|
|
201
179
|
trace_id: traceId,
|
|
202
|
-
openclaw_version:
|
|
203
|
-
runtime_certification_status:
|
|
180
|
+
openclaw_version: CONFIG.openclawVersion,
|
|
181
|
+
runtime_certification_status: CONFIG.runtimeCertificationStatus,
|
|
204
182
|
attestation_pack_version: packVerification.packVersion,
|
|
205
183
|
attestation_signature_verified: packVerification.signatureVerified,
|
|
206
184
|
axis_scores: axisScores,
|
|
207
185
|
checks,
|
|
208
|
-
remediation: remediationFor(status,
|
|
186
|
+
remediation: remediationFor(status, CONFIG.runtimeCertificationStatus, anyConnectivityFailure),
|
|
209
187
|
generated_at: new Date().toISOString(),
|
|
210
188
|
};
|
|
211
|
-
if (
|
|
189
|
+
if (CONFIG.jsonMode) {
|
|
212
190
|
console.log(JSON.stringify(report, null, 2));
|
|
213
191
|
}
|
|
214
192
|
else {
|
|
215
|
-
if (status ===
|
|
216
|
-
console.log(
|
|
217
|
-
console.log(
|
|
193
|
+
if (status === "ENFORCED_OK") {
|
|
194
|
+
console.log("\n🎉 ALL TESTS PASSED — Trusted Mode is LIVE and PROVABLE");
|
|
195
|
+
console.log(" Your OpenClaw deployment is now governed.");
|
|
218
196
|
}
|
|
219
197
|
else {
|
|
220
198
|
console.error(`\n❌ TRUSTED MODE CHECK STATUS: ${status}`);
|
|
@@ -222,19 +200,19 @@ async function main() {
|
|
|
222
200
|
if (!check.ok)
|
|
223
201
|
console.error(`- ${check.id}: ${check.detail}`);
|
|
224
202
|
}
|
|
225
|
-
console.error(
|
|
203
|
+
console.error("\nRemediation:");
|
|
226
204
|
for (const step of report.remediation)
|
|
227
205
|
console.error(`- ${step}`);
|
|
228
206
|
}
|
|
229
|
-
console.log(
|
|
207
|
+
console.log("\nAttestation report (--json):");
|
|
230
208
|
console.log(JSON.stringify(report, null, 2));
|
|
231
209
|
}
|
|
232
|
-
if (
|
|
233
|
-
if (status !==
|
|
210
|
+
if (CONFIG.expectedStatus) {
|
|
211
|
+
if (status !== CONFIG.expectedStatus)
|
|
234
212
|
(0, node_process_1.exit)(1);
|
|
235
213
|
return;
|
|
236
214
|
}
|
|
237
|
-
if (status !==
|
|
215
|
+
if (status !== "ENFORCED_OK")
|
|
238
216
|
(0, node_process_1.exit)(1);
|
|
239
217
|
}
|
|
240
218
|
main();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readCliConfig = readCliConfig;
|
|
4
|
+
const packageVersion_1 = require("./packageVersion");
|
|
5
|
+
const runtimeCertification_1 = require("./runtimeCertification");
|
|
6
|
+
function readCliConfig(argv = process.argv) {
|
|
7
|
+
const env = process.env;
|
|
8
|
+
return {
|
|
9
|
+
pdpUrl: env.PDP_URL || "http://localhost:8001/v1/authorize",
|
|
10
|
+
policyVariant: env.POLICY_VARIANT || "guard-pro.v2026.02",
|
|
11
|
+
tenantId: env.TENANT_ID || "trial-tenant",
|
|
12
|
+
gatewayId: env.GATEWAY_ID || env.OPENCLAW_GATEWAY_ID || "gw-smoke-1",
|
|
13
|
+
environment: env.ENVIRONMENT || env.OPENCLAW_ENVIRONMENT || "prod",
|
|
14
|
+
openclawVersion: (0, packageVersion_1.resolveOpenClawVersion)(env.OPENCLAW_VERSION),
|
|
15
|
+
runtimeCertificationStatus: (0, runtimeCertification_1.normalizeRuntimeCertificationStatus)(env.CERTIFICATION_STATUS || "CERTIFIED_ENFORCED"),
|
|
16
|
+
jsonMode: argv.includes("--json"),
|
|
17
|
+
expectedStatus: env.EXPECTED_STATUS,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.postDecision = postDecision;
|
|
4
|
+
const sdeGuidance_1 = require("./sdeGuidance");
|
|
5
|
+
async function postDecision(pdpUrl, payload) {
|
|
6
|
+
try {
|
|
7
|
+
const res = await fetch(pdpUrl, {
|
|
8
|
+
method: "POST",
|
|
9
|
+
headers: { "Content-Type": "application/json" },
|
|
10
|
+
body: JSON.stringify(payload),
|
|
11
|
+
});
|
|
12
|
+
if (!res.ok) {
|
|
13
|
+
throw new Error(`PDP unreachable (${res.status})`);
|
|
14
|
+
}
|
|
15
|
+
return (await res.json());
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
const detail = err?.name === "AbortError" ? "PDP timeout" : err?.message || String(err);
|
|
19
|
+
throw new Error((0, sdeGuidance_1.maybeAppendSdeRuntimeGuidance)(detail, pdpUrl));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.parseConfigureCliArgs = parseConfigureCliArgs;
|
|
5
|
+
exports.runConfigureCli = runConfigureCli;
|
|
6
|
+
const node_process_1 = require("node:process");
|
|
7
|
+
const node_os_1 = require("node:os");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const openclawConfig_1 = require("./openclawConfig");
|
|
10
|
+
const runtimeCertification_1 = require("./runtimeCertification");
|
|
11
|
+
function printHelp() {
|
|
12
|
+
console.log(`Configure ${openclawConfig_1.PLUGIN_ID} governed mode in OpenClaw.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
openclaw-trusted-mode-configure --tenantId <tenant> --gatewayId <gateway> --environment <environment> --pdpUrl <url> [options]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--policyVariant <variant> Policy variant to store in plugin config.
|
|
19
|
+
Default: guard-pro.v2026.02
|
|
20
|
+
--configPath <path> OpenClaw config path.
|
|
21
|
+
Default: ${openclawConfig_1.DEFAULT_OPENCLAW_CONFIG_PATH}
|
|
22
|
+
--certificationStatus <status> CERTIFIED_ENFORCED | LOCKDOWN_ONLY | UNSUPPORTED
|
|
23
|
+
Default: LOCKDOWN_ONLY
|
|
24
|
+
--pdpTimeoutMs <ms> PDP timeout to store in plugin config.
|
|
25
|
+
Default: 5000
|
|
26
|
+
--failOpen Write failClosed=false instead of true.
|
|
27
|
+
--json Print a JSON summary instead of prose.
|
|
28
|
+
--help Show this help text.
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
function expandHomePath(value) {
|
|
32
|
+
if (value === "~")
|
|
33
|
+
return (0, node_os_1.homedir)();
|
|
34
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
35
|
+
return (0, node_path_1.join)((0, node_os_1.homedir)(), value.slice(2));
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function readFlagValue(argv, flag) {
|
|
40
|
+
const index = argv.indexOf(flag);
|
|
41
|
+
if (index === -1)
|
|
42
|
+
return undefined;
|
|
43
|
+
const value = argv[index + 1];
|
|
44
|
+
if (!value || value.startsWith("--")) {
|
|
45
|
+
throw new Error(`Missing value for ${flag}`);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function parseConfigureCliArgs(argv) {
|
|
50
|
+
if (argv.includes("--help")) {
|
|
51
|
+
printHelp();
|
|
52
|
+
(0, node_process_1.exit)(0);
|
|
53
|
+
}
|
|
54
|
+
const tenantId = readFlagValue(argv, "--tenantId");
|
|
55
|
+
const gatewayId = readFlagValue(argv, "--gatewayId");
|
|
56
|
+
const environment = readFlagValue(argv, "--environment");
|
|
57
|
+
const pdpUrl = readFlagValue(argv, "--pdpUrl");
|
|
58
|
+
const policyVariant = readFlagValue(argv, "--policyVariant") || "guard-pro.v2026.02";
|
|
59
|
+
const configPath = expandHomePath(readFlagValue(argv, "--configPath") || (0, node_path_1.join)((0, node_os_1.homedir)(), ".openclaw", "openclaw.json"));
|
|
60
|
+
const certificationStatus = (0, runtimeCertification_1.normalizeRuntimeCertificationStatus)(readFlagValue(argv, "--certificationStatus") || "LOCKDOWN_ONLY");
|
|
61
|
+
const timeoutRaw = readFlagValue(argv, "--pdpTimeoutMs");
|
|
62
|
+
const pdpTimeoutMs = timeoutRaw ? Number(timeoutRaw) : 5000;
|
|
63
|
+
const failClosed = !argv.includes("--failOpen");
|
|
64
|
+
const jsonMode = argv.includes("--json");
|
|
65
|
+
const missing = [
|
|
66
|
+
["--tenantId", tenantId],
|
|
67
|
+
["--gatewayId", gatewayId],
|
|
68
|
+
["--environment", environment],
|
|
69
|
+
["--pdpUrl", pdpUrl],
|
|
70
|
+
].filter(([, value]) => !value);
|
|
71
|
+
if (missing.length > 0) {
|
|
72
|
+
throw new Error(`Missing required flags: ${missing.map(([flag]) => flag).join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
if (!Number.isFinite(pdpTimeoutMs) || pdpTimeoutMs <= 0) {
|
|
75
|
+
throw new Error("--pdpTimeoutMs must be a positive number");
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
tenantId: tenantId,
|
|
79
|
+
gatewayId: gatewayId,
|
|
80
|
+
environment: environment,
|
|
81
|
+
pdpUrl: pdpUrl,
|
|
82
|
+
policyVariant,
|
|
83
|
+
configPath,
|
|
84
|
+
certificationStatus,
|
|
85
|
+
failClosed,
|
|
86
|
+
pdpTimeoutMs,
|
|
87
|
+
jsonMode,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function runConfigureCli(argv = process.argv.slice(2)) {
|
|
91
|
+
try {
|
|
92
|
+
const options = parseConfigureCliArgs(argv);
|
|
93
|
+
const result = (0, openclawConfig_1.writeGovernedConfig)(options);
|
|
94
|
+
const output = {
|
|
95
|
+
pluginId: openclawConfig_1.PLUGIN_ID,
|
|
96
|
+
configPath: result.configPath,
|
|
97
|
+
created: result.created,
|
|
98
|
+
pluginAllowAdded: result.pluginAllowAdded,
|
|
99
|
+
mode: "governed",
|
|
100
|
+
values: {
|
|
101
|
+
tenantId: options.tenantId,
|
|
102
|
+
gatewayId: options.gatewayId,
|
|
103
|
+
environment: options.environment,
|
|
104
|
+
pdpUrl: options.pdpUrl,
|
|
105
|
+
policyVariant: options.policyVariant,
|
|
106
|
+
certificationStatus: options.certificationStatus,
|
|
107
|
+
failClosed: options.failClosed,
|
|
108
|
+
pdpTimeoutMs: options.pdpTimeoutMs,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
if (options.jsonMode) {
|
|
112
|
+
console.log(JSON.stringify(output, null, 2));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
console.log(`Configured ${openclawConfig_1.PLUGIN_ID} governed mode at ${result.configPath}`);
|
|
116
|
+
console.log(`- plugins.allow includes ${openclawConfig_1.PLUGIN_ID}`);
|
|
117
|
+
console.log(`- tenantId=${options.tenantId}`);
|
|
118
|
+
console.log(`- gatewayId=${options.gatewayId}`);
|
|
119
|
+
console.log(`- environment=${options.environment}`);
|
|
120
|
+
console.log(`- pdpUrl=${options.pdpUrl}`);
|
|
121
|
+
console.log(`- toolPolicyMode=PDP`);
|
|
122
|
+
console.log(`- failClosed=${options.failClosed}`);
|
|
123
|
+
console.log(`- certificationStatus=${options.certificationStatus}`);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.error(error.message);
|
|
127
|
+
console.error(`Run "openclaw-trusted-mode-configure --help" for usage.`);
|
|
128
|
+
(0, node_process_1.exit)(1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (require.main === module) {
|
|
132
|
+
runConfigureCli();
|
|
133
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_OPENCLAW_CONFIG_PATH = exports.PLUGIN_ID = void 0;
|
|
4
|
+
exports.normalizeConfigText = normalizeConfigText;
|
|
5
|
+
exports.parseOpenClawConfig = parseOpenClawConfig;
|
|
6
|
+
exports.configureGovernedPlugin = configureGovernedPlugin;
|
|
7
|
+
exports.writeGovernedConfig = writeGovernedConfig;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
exports.PLUGIN_ID = "openclaw-trusted-mode";
|
|
11
|
+
exports.DEFAULT_OPENCLAW_CONFIG_PATH = "~/.openclaw/openclaw.json";
|
|
12
|
+
function ensureObject(value) {
|
|
13
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
function normalizeConfigText(text) {
|
|
19
|
+
return `${text.trimEnd()}\n`;
|
|
20
|
+
}
|
|
21
|
+
function parseOpenClawConfig(raw) {
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
return ensureObject(parsed);
|
|
24
|
+
}
|
|
25
|
+
function configureGovernedPlugin(baseConfig, input) {
|
|
26
|
+
const nextConfig = ensureObject(baseConfig);
|
|
27
|
+
const plugins = ensureObject(nextConfig.plugins);
|
|
28
|
+
const existingAllow = Array.isArray(plugins.allow) ? [...plugins.allow] : [];
|
|
29
|
+
const allow = existingAllow.filter((item) => typeof item === "string");
|
|
30
|
+
const pluginAllowAdded = !allow.includes(exports.PLUGIN_ID);
|
|
31
|
+
if (pluginAllowAdded) {
|
|
32
|
+
allow.push(exports.PLUGIN_ID);
|
|
33
|
+
}
|
|
34
|
+
const entries = ensureObject(plugins.entries);
|
|
35
|
+
const existingEntry = ensureObject(entries[exports.PLUGIN_ID]);
|
|
36
|
+
const existingPluginConfig = ensureObject(existingEntry.config);
|
|
37
|
+
const nextPluginConfig = {
|
|
38
|
+
...existingPluginConfig,
|
|
39
|
+
pdpUrl: input.pdpUrl,
|
|
40
|
+
policyVariant: input.policyVariant,
|
|
41
|
+
pdpTimeoutMs: input.pdpTimeoutMs,
|
|
42
|
+
failClosed: input.failClosed,
|
|
43
|
+
tenantId: input.tenantId,
|
|
44
|
+
gatewayId: input.gatewayId,
|
|
45
|
+
environment: input.environment,
|
|
46
|
+
toolPolicyMode: "PDP",
|
|
47
|
+
requireTenantId: true,
|
|
48
|
+
allowedTenantIds: [input.tenantId],
|
|
49
|
+
certificationStatus: input.certificationStatus,
|
|
50
|
+
};
|
|
51
|
+
delete nextPluginConfig.allowedTools;
|
|
52
|
+
entries[exports.PLUGIN_ID] = {
|
|
53
|
+
...existingEntry,
|
|
54
|
+
enabled: true,
|
|
55
|
+
config: nextPluginConfig,
|
|
56
|
+
};
|
|
57
|
+
plugins.allow = allow;
|
|
58
|
+
plugins.entries = entries;
|
|
59
|
+
nextConfig.plugins = plugins;
|
|
60
|
+
return {
|
|
61
|
+
config: nextConfig,
|
|
62
|
+
pluginAllowAdded,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function writeGovernedConfig(input) {
|
|
66
|
+
let parsed = {};
|
|
67
|
+
let created = false;
|
|
68
|
+
try {
|
|
69
|
+
parsed = parseOpenClawConfig((0, node_fs_1.readFileSync)(input.configPath, "utf8"));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const code = error?.code;
|
|
73
|
+
if (code === "ENOENT") {
|
|
74
|
+
created = true;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const { config, pluginAllowAdded } = configureGovernedPlugin(parsed, input);
|
|
81
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(input.configPath), { recursive: true });
|
|
82
|
+
(0, node_fs_1.writeFileSync)(input.configPath, normalizeConfigText(JSON.stringify(config, null, 2)), "utf8");
|
|
83
|
+
return {
|
|
84
|
+
configPath: input.configPath,
|
|
85
|
+
created,
|
|
86
|
+
pluginAllowAdded,
|
|
87
|
+
};
|
|
88
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "openclaw-trusted-mode",
|
|
2
|
+
"id": "openclaw-trusted-mode",
|
|
3
3
|
"name": "Trusted Mode Governance",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.5",
|
|
5
5
|
"description": "Provable enforcement using SDE-PDP + signed policy packs",
|
|
6
6
|
"author": "Darkelogix",
|
|
7
7
|
"hooks": ["before_tool_call"],
|
|
8
8
|
"configSchema": {
|
|
9
9
|
"type": "object",
|
|
10
|
-
"properties": {
|
|
11
|
-
"pdpUrl": { "type": "string", "default": "http://localhost:8001/v1/authorize" },
|
|
12
|
-
"policyVariant": { "type": "string", "default": "guard-pro.v2026.02" },
|
|
13
|
-
"pdpTimeoutMs": { "type": "number", "default": 5000 },
|
|
14
|
-
"failClosed": { "type": "boolean", "default": true },
|
|
15
|
-
"tenantId": { "type": "string" },
|
|
16
|
-
"gatewayId": { "type": "string" },
|
|
17
|
-
"environment": { "type": "string" },
|
|
18
|
-
"certificationStatus": {
|
|
19
|
-
"type": "string",
|
|
20
|
-
"enum": ["CERTIFIED_ENFORCED", "LOCKDOWN_ONLY", "UNSUPPORTED"],
|
|
21
|
-
"default": "LOCKDOWN_ONLY"
|
|
22
|
-
},
|
|
23
|
-
"openclawVersion": { "type": "string" },
|
|
24
|
-
"certifiedOpenClawVersions": {
|
|
25
|
-
"type": "array",
|
|
26
|
-
"items": { "type": "string" },
|
|
27
|
-
"default": []
|
|
28
|
-
},
|
|
29
|
-
"highRiskTools": {
|
|
30
|
-
"type": "array",
|
|
31
|
-
"items": { "type": "string" },
|
|
32
|
-
"default": ["exec", "execute_shell", "run_shell_command", "shell", "delete_file", "remove_file", "write_file", "edit_file"]
|
|
33
|
-
},
|
|
34
|
-
"toolPolicyMode": {
|
|
35
|
-
"type": "string",
|
|
36
|
-
"enum": ["PDP", "ALLOWLIST_ONLY"],
|
|
37
|
-
"default": "ALLOWLIST_ONLY"
|
|
38
|
-
},
|
|
39
|
-
"allowedTools": {
|
|
40
|
-
"type": "array",
|
|
41
|
-
"items": { "type": "string" },
|
|
42
|
-
"default": ["read_file", "list_files", "search_files"]
|
|
43
|
-
},
|
|
44
|
-
"requireTenantId": {
|
|
45
|
-
"type": "boolean",
|
|
46
|
-
"default": false
|
|
47
|
-
},
|
|
48
|
-
"allowedTenantIds": {
|
|
49
|
-
"type": "array",
|
|
50
|
-
"items": { "type": "string" },
|
|
51
|
-
"default": []
|
|
52
|
-
},
|
|
53
|
-
"contextCurator": {
|
|
54
|
-
"type": "object",
|
|
55
|
-
"properties": {
|
|
56
|
-
"enabled": { "type": "boolean", "default": false },
|
|
57
|
-
"redactKeys": { "type": "array", "items": { "type": "string" } },
|
|
58
|
-
"dropPaths": { "type": "array", "items": { "type": "string" } },
|
|
59
|
-
"maxStringLength": { "type": "number", "default": 0 }
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
10
|
+
"properties": {
|
|
11
|
+
"pdpUrl": { "type": "string", "default": "http://localhost:8001/v1/authorize" },
|
|
12
|
+
"policyVariant": { "type": "string", "default": "guard-pro.v2026.02" },
|
|
13
|
+
"pdpTimeoutMs": { "type": "number", "default": 5000 },
|
|
14
|
+
"failClosed": { "type": "boolean", "default": true },
|
|
15
|
+
"tenantId": { "type": "string" },
|
|
16
|
+
"gatewayId": { "type": "string" },
|
|
17
|
+
"environment": { "type": "string" },
|
|
18
|
+
"certificationStatus": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": ["CERTIFIED_ENFORCED", "LOCKDOWN_ONLY", "UNSUPPORTED"],
|
|
21
|
+
"default": "LOCKDOWN_ONLY"
|
|
22
|
+
},
|
|
23
|
+
"openclawVersion": { "type": "string" },
|
|
24
|
+
"certifiedOpenClawVersions": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": { "type": "string" },
|
|
27
|
+
"default": []
|
|
28
|
+
},
|
|
29
|
+
"highRiskTools": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"items": { "type": "string" },
|
|
32
|
+
"default": ["exec", "execute_shell", "run_shell_command", "shell", "delete_file", "remove_file", "write_file", "edit_file"]
|
|
33
|
+
},
|
|
34
|
+
"toolPolicyMode": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": ["PDP", "ALLOWLIST_ONLY"],
|
|
37
|
+
"default": "ALLOWLIST_ONLY"
|
|
38
|
+
},
|
|
39
|
+
"allowedTools": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": { "type": "string" },
|
|
42
|
+
"default": ["read_file", "list_files", "search_files"]
|
|
43
|
+
},
|
|
44
|
+
"requireTenantId": {
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": false
|
|
47
|
+
},
|
|
48
|
+
"allowedTenantIds": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": { "type": "string" },
|
|
51
|
+
"default": []
|
|
52
|
+
},
|
|
53
|
+
"contextCurator": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"enabled": { "type": "boolean", "default": false },
|
|
57
|
+
"redactKeys": { "type": "array", "items": { "type": "string" } },
|
|
58
|
+
"dropPaths": { "type": "array", "items": { "type": "string" } },
|
|
59
|
+
"maxStringLength": { "type": "number", "default": 0 }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darkelogix/openclaw-trusted-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "MIT-licensed OpenClaw Trusted Mode plugin with standalone hardening and optional SDE-backed governance",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
".": "./dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
|
-
"openclaw-trusted-mode-check": "./dist/cli.js"
|
|
11
|
+
"openclaw-trusted-mode-check": "./dist/cli.js",
|
|
12
|
+
"openclaw-trusted-mode-configure": "./dist/configureCli.js"
|
|
12
13
|
},
|
|
13
14
|
"homepage": "https://www.darkelogix.ai/openclaw/",
|
|
14
15
|
"repository": {
|
|
@@ -37,10 +38,14 @@
|
|
|
37
38
|
"files": [
|
|
38
39
|
"dist/attestation.js",
|
|
39
40
|
"dist/cli.js",
|
|
41
|
+
"dist/cliConfig.js",
|
|
42
|
+
"dist/cliPdpClient.js",
|
|
43
|
+
"dist/configureCli.js",
|
|
40
44
|
"dist/constraints.js",
|
|
41
45
|
"dist/contextCurator.js",
|
|
42
46
|
"dist/hardening.js",
|
|
43
47
|
"dist/index.js",
|
|
48
|
+
"dist/openclawConfig.js",
|
|
44
49
|
"dist/packageVersion.js",
|
|
45
50
|
"dist/runtimeCertification.js",
|
|
46
51
|
"dist/sdeGuidance.js",
|
|
@@ -76,6 +81,7 @@
|
|
|
76
81
|
"verify-compatibility-matrix": "node scripts/update_compatibility_matrix.js --check",
|
|
77
82
|
"enforce-cert-quota": "node scripts/enforce_cert_quota.js",
|
|
78
83
|
"trusted-mode-check": "node dist/cli.js",
|
|
84
|
+
"trusted-mode-configure": "node dist/configureCli.js",
|
|
79
85
|
"trusted-mode-check:mock": "node scripts/mock_pdp.js",
|
|
80
86
|
"lint-docs-terminology": "python scripts/lint_docs_terminology.py"
|
|
81
87
|
},
|