fix(gateway): preserve unapproved policy warnings

This commit is contained in:
jesse-merhi
2026-08-11 03:25:28 +10:00
parent a573c64f1f
commit 254db64295
7 changed files with 80 additions and 41 deletions
@@ -1,4 +1,5 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { InstallPolicyWarningAcknowledgementRequest } from "../plugins/install-security-scan.types.js";
import { resolveInstallPolicyWarningAcknowledgementCliOptions } from "./install-policy-warning-acknowledgement.ts";
const promptTextMock = vi.hoisted(() => vi.fn());
@@ -120,32 +121,30 @@ describe("resolveInstallPolicyWarningAcknowledgementCliOptions", () => {
const options = resolveInstallPolicyWarningAcknowledgementCliOptions({
acknowledgeInstallPolicyWarning: true,
});
await expect(
options.onInstallPolicyWarning?.({
const warningRequest: InstallPolicyWarningAcknowledgementRequest = {
targetName: "demo",
targetType: "plugin",
requestMode: "install",
scan: {
requestKind: "plugin-npm",
originType: "plugin-npm",
pluginContentType: "package",
},
warning: {
targetName: "demo",
targetType: "plugin",
requestMode: "install",
scan: {
requestKind: "plugin-npm",
originType: "plugin-npm",
pluginContentType: "package",
},
warning: {
targetName: "demo",
targetType: "plugin",
requestMode: "install",
reason: "Review required",
},
}),
).resolves.toEqual({ status: "approved" });
await expect(
options.onInstallPolicyWarning?.({
targetName: "demo-dependency",
targetType: "plugin",
requestMode: "install",
}),
).resolves.toEqual({ status: "unavailable", reason: "approval-exhausted" });
reason: "Review required",
},
};
await expect(options.onInstallPolicyWarning?.(warningRequest)).resolves.toEqual({
status: "approved",
});
await expect(options.onInstallPolicyWarning?.(warningRequest)).resolves.toEqual({
status: "unavailable",
reason: "approval-exhausted",
});
expect(promptTextMock).not.toHaveBeenCalled();
});
});
@@ -568,6 +568,19 @@ describe("legacy file install scan compatibility", () => {
expect(result?.blocked?.reason).toContain("The noninteractive approval was already used.");
expect(result?.blocked?.reason).toContain("Review this warning and rerun interactively.");
expect(result?.blocked?.reason).not.toContain("Install cancelled");
expect(result?.blocked?.installPolicyWarning).toMatchObject({
scan: {
requestKind: "plugin-file",
originType: "plugin-file",
pluginContentType: "file",
},
warning: {
targetName: "payload",
targetType: "plugin",
requestMode: "install",
reason: "review the dependency warning",
},
});
expect(runInstallPolicyMock).toHaveBeenCalledTimes(1);
});
+8 -4
View File
@@ -924,13 +924,17 @@ async function runOperatorInstallPolicy(params: {
return {
blocked: {
code: "security_scan_blocked",
installPolicyWarning: warningOccurrence,
reason: formatInstallPolicyNotice({
decision: "warn",
findings: result.findings,
guidance: [
"The noninteractive approval was already used.",
"Review this warning and rerun interactively.",
],
guidance:
acknowledgement.reason === "approval-exhausted"
? [
"The noninteractive approval was already used.",
"Review this warning and rerun interactively.",
]
: ["This warning has not been approved.", "Review it and try again."],
reason: result.warning.reason,
targetName: params.targetName,
targetType: params.targetType,
+1 -1
View File
@@ -34,7 +34,7 @@ type InstallPolicyWarningAcknowledgementResult =
| { status: "declined" }
| {
status: "unavailable";
reason: "approval-exhausted";
reason: "approval-exhausted" | "warning-not-approved";
};
/** Overrides that intentionally loosen install safety policy for trusted/operator paths. */
@@ -1,6 +1,10 @@
import { expectDefined } from "@openclaw/normalization-core";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { InstallPolicyWarningOccurrence } from "./install-security-scan.types.js";
import type {
InstallPolicyWarningAcknowledgementRequest,
InstallPolicyWarningAcknowledgementResult,
InstallPolicyWarningOccurrence,
} from "./install-security-scan.types.js";
import {
expectOneShotInstallPolicyWarningAcknowledgement,
officialDiffsWarningRequest,
@@ -239,17 +243,32 @@ describe("plugin management install-policy acknowledgements", () => {
const acknowledge = expectDefined(
(
call[0] as {
onInstallPolicyWarning?: (request: {
scan: typeof packageWarning.scan;
warning: typeof packageWarning.warning;
}) => Promise<boolean>;
onInstallPolicyWarning?: (
request: InstallPolicyWarningAcknowledgementRequest,
) => Promise<InstallPolicyWarningAcknowledgementResult>;
}
).onInstallPolicyWarning,
"install-policy acknowledgement callback",
);
expect(await acknowledge(dependencyWarning)).toBe(false);
expect(await acknowledge(packageWarning)).toBe(true);
expect(await acknowledge(packageWarning)).toBe(false);
expect(
await acknowledge({
targetName: dependencyWarning.warning.targetName,
targetType: dependencyWarning.warning.targetType,
requestMode: dependencyWarning.warning.requestMode,
...dependencyWarning,
}),
).toEqual({ status: "unavailable", reason: "warning-not-approved" });
const packageRequest: InstallPolicyWarningAcknowledgementRequest = {
targetName: packageWarning.warning.targetName,
targetType: packageWarning.warning.targetType,
requestMode: packageWarning.warning.requestMode,
...packageWarning,
};
expect(await acknowledge(packageRequest)).toEqual({ status: "approved" });
expect(await acknowledge(packageRequest)).toEqual({
status: "unavailable",
reason: "warning-not-approved",
});
});
it("pins reviewed npm warnings to the first resolved version and integrity", async () => {
+2 -2
View File
@@ -1540,13 +1540,13 @@ export async function installManagedPlugin(params: {
isDeepStrictEqual(warning, approved.warning),
);
if (warningIndex < 0) {
return false;
return { status: "unavailable", reason: "warning-not-approved" };
}
const [approved] = remainingInstallPolicyWarnings.splice(warningIndex, 1);
if (approved) {
acknowledgedInstallPolicyWarnings.push(approved);
}
return true;
return { status: "approved" };
},
},
}
@@ -2,6 +2,7 @@ import { expectDefined } from "@openclaw/normalization-core";
import { expect } from "vitest";
import type {
InstallPolicyWarningAcknowledgementRequest,
InstallPolicyWarningAcknowledgementResult,
InstallPolicyWarningOccurrence,
} from "../install-security-scan.types.js";
@@ -22,7 +23,7 @@ export const officialDiffsWarningOccurrence: InstallPolicyWarningOccurrence = {
type InstallPolicyWarningCall = {
onInstallPolicyWarning?: (
request: InstallPolicyWarningAcknowledgementRequest,
) => Promise<boolean>;
) => Promise<InstallPolicyWarningAcknowledgementResult>;
};
export const officialDiffsWarningRequest = {
@@ -54,6 +55,9 @@ export async function expectOneShotInstallPolicyWarningAcknowledgement(mock: {
requestMode: "install",
...officialDiffsWarningOccurrence,
};
expect(await acknowledge(request)).toBe(true);
expect(await acknowledge(request)).toBe(false);
expect(await acknowledge(request)).toEqual({ status: "approved" });
expect(await acknowledge(request)).toEqual({
status: "unavailable",
reason: "warning-not-approved",
});
}