mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix: remove stale allowlist matcher cache
This commit is contained in:
@@ -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.
|
- 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.
|
- 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.
|
- 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
|
## 2026.3.8
|
||||||
|
|
||||||
|
|||||||
85
src/channels/allowlist-match.test.ts
Normal file
85
src/channels/allowlist-match.test.ts
Normal 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",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -16,17 +16,6 @@ export type AllowlistMatch<TSource extends string = AllowlistMatchSource> = {
|
|||||||
matchSource?: TSource;
|
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(
|
export function formatAllowlistMatchMeta(
|
||||||
match?: { matchKey?: string; matchSource?: string } | null,
|
match?: { matchKey?: string; matchSource?: string } | null,
|
||||||
): string {
|
): string {
|
||||||
@@ -82,13 +71,7 @@ export function resolveAllowlistMatchSimple(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolveAllowListSet(allowList: string[]): Set<string> {
|
function resolveAllowListSet(allowList: string[]): Set<string> {
|
||||||
const cached = ALLOWLIST_SET_CACHE.get(allowList);
|
return new Set(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveSimpleAllowFrom(allowFrom: Array<string | number>): {
|
function resolveSimpleAllowFrom(allowFrom: Array<string | number>): {
|
||||||
@@ -97,19 +80,12 @@ function resolveSimpleAllowFrom(allowFrom: Array<string | number>): {
|
|||||||
wildcard: boolean;
|
wildcard: boolean;
|
||||||
set: Set<string>;
|
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 normalized = allowFrom.map((entry) => String(entry).trim().toLowerCase()).filter(Boolean);
|
||||||
const set = new Set(normalized);
|
const set = new Set(normalized);
|
||||||
const built = {
|
return {
|
||||||
normalized,
|
normalized,
|
||||||
size: allowFrom.length,
|
size: allowFrom.length,
|
||||||
wildcard: set.has("*"),
|
wildcard: set.has("*"),
|
||||||
set,
|
set,
|
||||||
};
|
};
|
||||||
SIMPLE_ALLOWLIST_CACHE.set(allowFrom, built);
|
|
||||||
return built;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user