fix(matrix): bound allowlist store cache expiry

This commit is contained in:
Peter Steinberger
2026-05-30 12:56:54 -04:00
parent 5568ecc7aa
commit 283238fd77
2 changed files with 66 additions and 5 deletions

View File

@@ -1,6 +1,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { MAX_DATE_TIMESTAMP_MS } from "openclaw/plugin-sdk/number-runtime";
import {
testing as sessionBindingTesting,
registerSessionBindingAdapter,
@@ -321,6 +322,58 @@ describe("matrix monitor handler pairing account scope", () => {
}
});
it("does not reuse account-scoped allowFrom cache while the process clock is invalid", async () => {
const readAllowFromStore = vi.fn(async () => [] as string[]);
const nowSpy = vi.spyOn(Date, "now");
const { handler } = createMatrixHandlerTestHarness({
readAllowFromStore,
dmPolicy: "pairing",
buildPairingReply: () => "pairing",
});
const makeEvent = (id: string): MatrixRawEvent =>
createMatrixTextMessageEvent({
eventId: id,
body: "@room hello",
mentions: { room: true },
});
try {
nowSpy.mockReturnValue(Number.NaN);
await handler("!room:example.org", makeEvent("$event1"));
await handler("!room:example.org", makeEvent("$event2"));
expect(readAllowFromStore).toHaveBeenCalledTimes(2);
} finally {
nowSpy.mockRestore();
}
});
it("does not cache account-scoped allowFrom reads when cache expiry overflows", async () => {
const readAllowFromStore = vi.fn(async () => [] as string[]);
const nowSpy = vi.spyOn(Date, "now");
const { handler } = createMatrixHandlerTestHarness({
readAllowFromStore,
dmPolicy: "pairing",
buildPairingReply: () => "pairing",
});
const makeEvent = (id: string): MatrixRawEvent =>
createMatrixTextMessageEvent({
eventId: id,
body: "@room hello",
mentions: { room: true },
});
try {
nowSpy.mockReturnValue(MAX_DATE_TIMESTAMP_MS);
await handler("!room:example.org", makeEvent("$event1"));
await handler("!room:example.org", makeEvent("$event2"));
expect(readAllowFromStore).toHaveBeenCalledTimes(2);
} finally {
nowSpy.mockRestore();
}
});
it("pins direct-message main route updates to the configured owner", async () => {
const { handler, recordInboundSession } = createMatrixHandlerTestHarness({
cfg: {

View File

@@ -27,6 +27,10 @@ import {
resolveChannelContextVisibilityMode,
} from "openclaw/plugin-sdk/context-visibility-runtime";
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
import {
isFutureDateTimestampMs,
resolveExpiresAtMsFromDurationMs,
} from "openclaw/plugin-sdk/number-runtime";
import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
import { buildInboundHistoryFromEntries } from "openclaw/plugin-sdk/reply-history";
import {
@@ -510,9 +514,13 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
const readStoreAllowFrom = async (): Promise<string[]> => {
const now = Date.now();
if (cachedStoreAllowFrom && now < cachedStoreAllowFrom.expiresAtMs) {
if (
cachedStoreAllowFrom &&
isFutureDateTimestampMs(cachedStoreAllowFrom.expiresAtMs, { nowMs: now })
) {
return cachedStoreAllowFrom.value;
}
cachedStoreAllowFrom = null;
const value = await core.channel.pairing
.readAllowFromStore({
channel: "matrix",
@@ -520,10 +528,10 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
accountId,
})
.catch(() => []);
cachedStoreAllowFrom = {
value,
expiresAtMs: now + ALLOW_FROM_STORE_CACHE_TTL_MS,
};
const expiresAtMs = resolveExpiresAtMsFromDurationMs(ALLOW_FROM_STORE_CACHE_TTL_MS, {
nowMs: now,
});
cachedStoreAllowFrom = expiresAtMs === undefined ? null : { value, expiresAtMs };
return value;
};