perf(security): bound regex input in filters and redaction

This commit is contained in:
Peter Steinberger
2026-03-02 16:37:23 +00:00
parent 31c7637e0f
commit b1592457fa
6 changed files with 103 additions and 5 deletions

View File

@@ -194,6 +194,34 @@ describe("exec approval forwarder", () => {
expect(deliver).not.toHaveBeenCalled();
});
it("matches long session keys with tail-bounded regex checks", async () => {
const cfg = {
approvals: {
exec: {
enabled: true,
mode: "session",
sessionFilter: ["discord:tail$"],
},
},
} as OpenClawConfig;
const { deliver, forwarder } = createForwarder({
cfg,
resolveSessionTarget: () => ({ channel: "slack", to: "U1" }),
});
const request = {
...baseRequest,
request: {
...baseRequest.request,
sessionKey: `${"x".repeat(5000)}discord:tail`,
},
};
await expect(forwarder.handleRequested(request)).resolves.toBe(true);
expect(deliver).toHaveBeenCalledTimes(1);
});
it("returns false when all targets are skipped", async () => {
await expectDiscordSessionTargetRequest({
cfg: makeSessionCfg({ discordExecApprovalsEnabled: true }),

View File

@@ -22,6 +22,7 @@ import { deliverOutboundPayloads } from "./outbound/deliver.js";
import { resolveSessionDeliveryTarget } from "./outbound/targets.js";
const log = createSubsystemLogger("gateway/exec-approvals");
const SESSION_FILTER_REGEX_MAX_INPUT = 2048;
export type { ExecApprovalRequest, ExecApprovalResolved };
@@ -56,12 +57,28 @@ function normalizeMode(mode?: ExecApprovalForwardingConfig["mode"]) {
}
function matchSessionFilter(sessionKey: string, patterns: string[]): boolean {
const head = sessionKey.slice(0, SESSION_FILTER_REGEX_MAX_INPUT);
const tail =
sessionKey.length > SESSION_FILTER_REGEX_MAX_INPUT
? sessionKey.slice(-SESSION_FILTER_REGEX_MAX_INPUT)
: "";
return patterns.some((pattern) => {
if (sessionKey.includes(pattern)) {
return true;
}
const regex = compileSafeRegex(pattern);
return regex ? regex.test(sessionKey) : false;
if (!regex) {
return false;
}
regex.lastIndex = 0;
if (regex.test(head)) {
return true;
}
if (tail) {
regex.lastIndex = 0;
return regex.test(tail);
}
return false;
});
}