fix(security): harden regex compilation for filters and redaction

This commit is contained in:
Peter Steinberger
2026-02-23 23:54:46 +00:00
parent e6484cb65f
commit a2dfe9879f
8 changed files with 238 additions and 16 deletions

View File

@@ -160,6 +160,34 @@ describe("exec approval forwarder", () => {
expect(deliver).not.toHaveBeenCalled();
});
it("rejects unsafe nested-repetition regex in sessionFilter", async () => {
const cfg = {
approvals: {
exec: {
enabled: true,
mode: "session",
sessionFilter: ["(a+)+$"],
},
},
} as OpenClawConfig;
const { deliver, forwarder } = createForwarder({
cfg,
resolveSessionTarget: () => ({ channel: "slack", to: "U1" }),
});
const request = {
...baseRequest,
request: {
...baseRequest.request,
sessionKey: `${"a".repeat(28)}!`,
},
};
await expect(forwarder.handleRequested(request)).resolves.toBe(false);
expect(deliver).not.toHaveBeenCalled();
});
it("returns false when all targets are skipped", async () => {
await expectDiscordSessionTargetRequest({
cfg: makeSessionCfg({ discordExecApprovalsEnabled: true }),

View File

@@ -7,6 +7,7 @@ import type {
} from "../config/types.approvals.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { normalizeAccountId, parseAgentSessionKey } from "../routing/session-key.js";
import { compileSafeRegex } from "../security/safe-regex.js";
import { isDeliverableMessageChannel, normalizeMessageChannel } from "../utils/message-channel.js";
import type {
ExecApprovalDecision,
@@ -52,11 +53,11 @@ function normalizeMode(mode?: ExecApprovalForwardingConfig["mode"]) {
function matchSessionFilter(sessionKey: string, patterns: string[]): boolean {
return patterns.some((pattern) => {
try {
return sessionKey.includes(pattern) || new RegExp(pattern).test(sessionKey);
} catch {
return sessionKey.includes(pattern);
if (sessionKey.includes(pattern)) {
return true;
}
const regex = compileSafeRegex(pattern);
return regex ? regex.test(sessionKey) : false;
});
}