fix: remove stale allowlist matcher cache

This commit is contained in:
Peter Steinberger
2026-03-11 00:00:04 +00:00
parent 825a435709
commit f604cbedf3
3 changed files with 88 additions and 26 deletions

View File

@@ -82,6 +82,7 @@ Docs: https://docs.openclaw.ai
- Sessions/reset model recompute: clear stale runtime model, context-token, and system-prompt metadata before session resets recompute the replacement session, so resets pick up current defaults and explicit overrides instead of reusing old runtime model state. (#41173) thanks @PonyX-lab.
- Browser/Browserbase 429 handling: surface stable no-retry rate-limit guidance without buffering discarded HTTP 429 response bodies from remote browser services. (#40491) thanks @mvanhorn.
- Gateway/auth: allow one trusted device-token retry on shared-token mismatch with recovery hints to prevent reconnect churn during token drift. (#42507) Thanks @joshavant.
- Channels/allowlists: remove stale matcher caching so same-array allowlist edits and wildcard replacements take effect immediately, with regression coverage for in-place mutation cases.
## 2026.3.8

View File

@@ -0,0 +1,85 @@
import { describe, expect, it } from "vitest";
import {
resolveAllowlistMatchByCandidates,
resolveAllowlistMatchSimple,
} from "./allowlist-match.js";
describe("channels/allowlist-match", () => {
it("reflects in-place allowFrom edits even when array length stays the same", () => {
const allowFrom = ["alice", "bob"];
expect(resolveAllowlistMatchSimple({ allowFrom, senderId: "bob" })).toEqual({
allowed: true,
matchKey: "bob",
matchSource: "id",
});
allowFrom[1] = "mallory";
expect(resolveAllowlistMatchSimple({ allowFrom, senderId: "bob" })).toEqual({
allowed: false,
});
expect(resolveAllowlistMatchSimple({ allowFrom, senderId: "mallory" })).toEqual({
allowed: true,
matchKey: "mallory",
matchSource: "id",
});
});
it("drops wildcard access after in-place wildcard replacement", () => {
const allowFrom = ["*"];
expect(resolveAllowlistMatchSimple({ allowFrom, senderId: "eve" })).toEqual({
allowed: true,
matchKey: "*",
matchSource: "wildcard",
});
allowFrom[0] = "alice";
expect(resolveAllowlistMatchSimple({ allowFrom, senderId: "eve" })).toEqual({
allowed: false,
});
expect(resolveAllowlistMatchSimple({ allowFrom, senderId: "alice" })).toEqual({
allowed: true,
matchKey: "alice",
matchSource: "id",
});
});
it("recomputes candidate allowlist sets after in-place replacement", () => {
const allowList = ["user:alice", "user:bob"];
expect(
resolveAllowlistMatchByCandidates({
allowList,
candidates: [{ value: "user:bob", source: "prefixed-user" }],
}),
).toEqual({
allowed: true,
matchKey: "user:bob",
matchSource: "prefixed-user",
});
allowList[1] = "user:mallory";
expect(
resolveAllowlistMatchByCandidates({
allowList,
candidates: [{ value: "user:bob", source: "prefixed-user" }],
}),
).toEqual({
allowed: false,
});
expect(
resolveAllowlistMatchByCandidates({
allowList,
candidates: [{ value: "user:mallory", source: "prefixed-user" }],
}),
).toEqual({
allowed: true,
matchKey: "user:mallory",
matchSource: "prefixed-user",
});
});
});

View File

@@ -16,17 +16,6 @@ export type AllowlistMatch<TSource extends string = AllowlistMatchSource> = {
matchSource?: TSource;
};
type CachedAllowListSet = {
size: number;
set: Set<string>;
};
const ALLOWLIST_SET_CACHE = new WeakMap<string[], CachedAllowListSet>();
const SIMPLE_ALLOWLIST_CACHE = new WeakMap<
Array<string | number>,
{ normalized: string[]; size: number; wildcard: boolean; set: Set<string> }
>();
export function formatAllowlistMatchMeta(
match?: { matchKey?: string; matchSource?: string } | null,
): string {
@@ -82,13 +71,7 @@ export function resolveAllowlistMatchSimple(params: {
}
function resolveAllowListSet(allowList: string[]): Set<string> {
const cached = ALLOWLIST_SET_CACHE.get(allowList);
if (cached && cached.size === allowList.length) {
return cached.set;
}
const set = new Set(allowList);
ALLOWLIST_SET_CACHE.set(allowList, { size: allowList.length, set });
return set;
return new Set(allowList);
}
function resolveSimpleAllowFrom(allowFrom: Array<string | number>): {
@@ -97,19 +80,12 @@ function resolveSimpleAllowFrom(allowFrom: Array<string | number>): {
wildcard: boolean;
set: Set<string>;
} {
const cached = SIMPLE_ALLOWLIST_CACHE.get(allowFrom);
if (cached && cached.size === allowFrom.length) {
return cached;
}
const normalized = allowFrom.map((entry) => String(entry).trim().toLowerCase()).filter(Boolean);
const set = new Set(normalized);
const built = {
return {
normalized,
size: allowFrom.length,
wildcard: set.has("*"),
set,
};
SIMPLE_ALLOWLIST_CACHE.set(allowFrom, built);
return built;
}