fix(slack): bound external menu cache clocks

This commit is contained in:
Peter Steinberger
2026-05-30 11:17:13 -04:00
parent 06b2bf1c0a
commit 7e3ebb8e10
2 changed files with 64 additions and 6 deletions

View File

@@ -0,0 +1,44 @@
import { describe, expect, it } from "vitest";
import {
createSlackExternalArgMenuStore,
SLACK_EXTERNAL_ARG_MENU_PREFIX,
} from "./external-arg-menu-store.js";
describe("createSlackExternalArgMenuStore", () => {
const choices = [{ label: "Daily", value: "day" }];
it("returns entries before their expiry", () => {
const store = createSlackExternalArgMenuStore();
const token = store.create({ choices, userId: "U1" }, 1_700_000_000_000);
expect(store.get(token, 1_700_000_001_000)).toEqual({
choices,
userId: "U1",
expiresAt: 1_700_000_600_000,
});
});
it("drops entries when the current clock is not a valid date timestamp", () => {
const store = createSlackExternalArgMenuStore();
const token = store.create({ choices, userId: "U1" }, 1_700_000_000_000);
expect(store.get(token, Number.NaN)).toBeUndefined();
expect(store.get(token, 1_700_000_001_000)).toBeUndefined();
});
it("does not retain entries when expiry would exceed the valid date range", () => {
const store = createSlackExternalArgMenuStore();
const token = store.create({ choices, userId: "U1" }, 8_640_000_000_000_000);
expect(store.get(token, 1_700_000_001_000)).toBeUndefined();
});
it("reads only prefixed valid menu tokens", () => {
const store = createSlackExternalArgMenuStore();
const token = store.create({ choices, userId: "U1" }, 1_700_000_000_000);
expect(store.readToken(`${SLACK_EXTERNAL_ARG_MENU_PREFIX}${token}`)).toBe(token);
expect(store.readToken(token)).toBeUndefined();
expect(store.readToken(`${SLACK_EXTERNAL_ARG_MENU_PREFIX}not a token`)).toBeUndefined();
});
});

View File

@@ -1,3 +1,7 @@
import {
asDateTimestampMs,
resolveExpiresAtMsFromDurationMs,
} from "openclaw/plugin-sdk/number-runtime";
import { generateSecureToken } from "openclaw/plugin-sdk/secure-random-runtime";
const SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES = 18;
@@ -20,10 +24,15 @@ type SlackExternalArgMenuEntry = {
function pruneSlackExternalArgMenuStore(
store: Map<string, SlackExternalArgMenuEntry>,
now: number,
rawNow: number,
): void {
const now = asDateTimestampMs(rawNow);
if (now === undefined) {
store.clear();
return;
}
for (const [token, entry] of store.entries()) {
if (entry.expiresAt <= now) {
if (asDateTimestampMs(entry.expiresAt) === undefined || entry.expiresAt <= now) {
store.delete(token);
}
}
@@ -47,11 +56,16 @@ export function createSlackExternalArgMenuStore() {
): string {
pruneSlackExternalArgMenuStore(store, now);
const token = createSlackExternalArgMenuToken(store);
store.set(token, {
choices: params.choices,
userId: params.userId,
expiresAt: now + SLACK_EXTERNAL_ARG_MENU_TTL_MS,
const expiresAt = resolveExpiresAtMsFromDurationMs(SLACK_EXTERNAL_ARG_MENU_TTL_MS, {
nowMs: now,
});
if (expiresAt !== undefined) {
store.set(token, {
choices: params.choices,
userId: params.userId,
expiresAt,
});
}
return token;
},
readToken(raw: unknown): string | undefined {