fix(security): re-evaluate forced policy warnings

This commit is contained in:
Jesse Merhi
2026-08-10 12:12:54 +10:00
committed by jesse-merhi
parent 7be358d70f
commit e057d9570e
6 changed files with 33 additions and 15 deletions
+1 -1
View File
@@ -198,7 +198,7 @@ non-npm sources are not rewritten.
`--pin` applies to npm installs only and records the resolved exact `<name>@<version>`. It is not supported with `git:` installs (pin the ref in the spec instead, e.g. `git:github.com/acme/plugin@v1.2.3`) or with `--marketplace` (marketplace installs persist marketplace source metadata instead of an npm spec).
</Accordion>
<Accordion title="--dangerously-force-unsafe-install">
When `security.installPolicy` returns `warn` in an interactive terminal, OpenClaw prints the reason and findings, then uses the same acknowledgement copy as a suspicious ClawHub release: `type: '<plugin>' to install anyway`. A matching answer re-evaluates the staged source before continuing. A declined or non-interactive install stops before commit; after review, it can be rerun with `--dangerously-force-unsafe-install`. Neither form overrides `block` or a policy failure.
When `security.installPolicy` returns `warn` in an interactive terminal, OpenClaw prints the reason and findings, then uses the same acknowledgement copy as a suspicious ClawHub release: `type: '<plugin>' to install anyway`. A matching answer re-evaluates the staged source before continuing. A declined or non-interactive install stops before commit; after review, `--dangerously-force-unsafe-install` is the explicit noninteractive approval. Every approved warning is re-evaluated before continuing. Neither form overrides `block` or a policy failure.
If a plugin you published on ClawHub is hidden or blocked by a registry scan, use the publisher steps in [ClawHub publishing](/clawhub/publishing). `--dangerously-force-unsafe-install` does not ask ClawHub to rescan the plugin or make a blocked release public.
+3 -2
View File
@@ -104,8 +104,9 @@ When `security.installPolicy` returns `warn` in an interactive terminal,
OpenClaw prints the reason and findings, then asks `type: '<skill>' to install
anyway` (or `update anyway`). A matching answer evaluates the staged skill
again before continuing. Declined and non-interactive commands stop before
commit; after review, rerun with `--dangerously-force-unsafe-install`. `block`
and policy failures remain terminal.
commit; after review, `--dangerously-force-unsafe-install` is the explicit
noninteractive approval, and every approved warning is re-evaluated before
continuing. `block` and policy failures remain terminal.
Notes:
+1 -1
View File
@@ -340,7 +340,7 @@ Plugins run in-process with the Gateway - treat them as trusted code.
- npm and git plugin installs run package-manager dependency convergence only during the explicit install/update flow. Local paths and archives are treated as self-contained packages; OpenClaw copies/references them without running `npm install`.
- Prefer pinned exact versions (`@scope/pkg@1.2.3`) and inspect the unpacked code before enabling.
- `security.installPolicy` lets operators run a trusted local command to return `allow`, `warn`, or `block` for skill and plugin installs. It runs after source material is staged but before install continues and applies to ClawHub skills too.
- A `warn` result stops before commit. Interactive CLI commands ask the operator to type the plugin or skill name using the same wording as suspicious ClawHub releases, then re-evaluate policy before continuing. Declined and non-interactive commands can be rerun with `--dangerously-force-unsafe-install` after review. `block` and policy failures remain terminal.
- A `warn` result stops before commit. Interactive CLI commands ask the operator to type the plugin or skill name using the same wording as suspicious ClawHub releases, then re-evaluate policy before continuing. Declined and non-interactive commands can use `--dangerously-force-unsafe-install` as explicit approval after review; every approved warning is re-evaluated before continuing. `block` and policy failures remain terminal.
Details: [Plugins](/tools/plugin)
+2 -1
View File
@@ -181,7 +181,8 @@ write one JSON object on stdout with an `allow`, `warn`, or `block` decision.
plugin and skill commands ask the operator to type the target name using the
same `install anyway` or `update anyway` copy as suspicious ClawHub releases,
then run policy again before continuing. Declined and non-interactive commands
may be rerun with `--dangerously-force-unsafe-install` after review.
may use `--dangerously-force-unsafe-install` as explicit approval after review;
every approved warning is re-evaluated before continuing.
Gateway-backed and automatic installs remain
blocked on warnings because they have no operator-confirmation flow. A `block`, non-zero
exit, timeout, malformed JSON, missing field, or unsupported protocol version
@@ -393,6 +393,24 @@ describe("legacy file install scan compatibility", () => {
}),
});
expect(acknowledgedAttempt).toBeUndefined();
expect(runInstallPolicyMock).toHaveBeenCalledTimes(3);
});
it("keeps a block from forced policy re-evaluation terminal", async () => {
runInstallPolicyMock
.mockResolvedValueOnce({ warning: { reason: "review this plugin" } })
.mockResolvedValueOnce({
blocked: { code: "security_scan_blocked", reason: "now blocked" },
});
const result = await scanFileInstallSourceRuntime({
dangerouslyForceUnsafeInstall: true,
filePath: "/tmp/payload.js",
logger: {},
pluginId: "payload",
});
expect(result?.blocked).toEqual({ code: "security_scan_blocked", reason: "now blocked" });
expect(runInstallPolicyMock).toHaveBeenCalledTimes(2);
});
+8 -10
View File
@@ -739,11 +739,7 @@ async function runOperatorInstallPolicy(params: {
logPolicyResult(result);
return undefined;
}
if (params.dangerouslyForceUnsafeInstall) {
logPolicyResult(result);
return undefined;
}
if (!params.onInstallPolicyWarning) {
if (!params.dangerouslyForceUnsafeInstall && !params.onInstallPolicyWarning) {
return {
blocked: {
code: "security_scan_blocked",
@@ -763,11 +759,13 @@ async function runOperatorInstallPolicy(params: {
};
}
logPolicyResult(result);
const acknowledged = await params.onInstallPolicyWarning({
targetName: params.targetName,
targetType: params.targetType,
requestMode: params.requestMode,
});
const acknowledged =
params.dangerouslyForceUnsafeInstall ||
(await params.onInstallPolicyWarning?.({
targetName: params.targetName,
targetType: params.targetType,
requestMode: params.requestMode,
}));
if (acknowledged) {
const reevaluated = await evaluatePolicy();
if (reevaluated?.blocked) {