mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 21:41:24 +00:00
pairing: isolate account-scoped allowlist and pending requests
This commit is contained in:
@@ -257,7 +257,7 @@ describe("pairing store", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("reads sync allowFrom with scoped + legacy dedupe and wildcard filtering", async () => {
|
||||
it("reads sync allowFrom with account-scoped isolation and wildcard filtering", async () => {
|
||||
await withTempStateDir(async (stateDir) => {
|
||||
await writeAllowFromFixture({
|
||||
stateDir,
|
||||
@@ -273,11 +273,37 @@ describe("pairing store", () => {
|
||||
|
||||
const scoped = readChannelAllowFromStoreSync("telegram", process.env, "yy");
|
||||
const channelScoped = readChannelAllowFromStoreSync("telegram");
|
||||
expect(scoped).toEqual(["1002", "1001"]);
|
||||
expect(scoped).toEqual(["1002", "1001", "1002"]);
|
||||
expect(channelScoped).toEqual(["1001", "1001"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not reuse pairing requests across accounts for the same sender id", async () => {
|
||||
await withTempStateDir(async () => {
|
||||
const first = await upsertChannelPairingRequest({
|
||||
channel: "telegram",
|
||||
accountId: "alpha",
|
||||
id: "12345",
|
||||
});
|
||||
const second = await upsertChannelPairingRequest({
|
||||
channel: "telegram",
|
||||
accountId: "beta",
|
||||
id: "12345",
|
||||
});
|
||||
|
||||
expect(first.created).toBe(true);
|
||||
expect(second.created).toBe(true);
|
||||
expect(second.code).not.toBe(first.code);
|
||||
|
||||
const alpha = await listChannelPairingRequests("telegram", process.env, "alpha");
|
||||
const beta = await listChannelPairingRequests("telegram", process.env, "beta");
|
||||
expect(alpha).toHaveLength(1);
|
||||
expect(beta).toHaveLength(1);
|
||||
expect(alpha[0]?.code).toBe(first.code);
|
||||
expect(beta[0]?.code).toBe(second.code);
|
||||
});
|
||||
});
|
||||
|
||||
it("reads legacy channel-scoped allowFrom for default account", async () => {
|
||||
await withTempStateDir(async (stateDir) => {
|
||||
await writeAllowFromFixture({ stateDir, channel: "telegram", allowFrom: ["1001"] });
|
||||
|
||||
@@ -218,6 +218,12 @@ function requestMatchesAccountId(entry: PairingRequest, normalizedAccountId: str
|
||||
);
|
||||
}
|
||||
|
||||
function shouldIncludeLegacyAllowFromEntries(normalizedAccountId: string): boolean {
|
||||
// Keep backward compatibility for legacy channel-scoped allowFrom only on default account.
|
||||
// Non-default accounts should remain isolated to avoid cross-account implicit approvals.
|
||||
return !normalizedAccountId || normalizedAccountId === "default";
|
||||
}
|
||||
|
||||
function normalizeId(value: string | number): string {
|
||||
return String(value).trim();
|
||||
}
|
||||
@@ -344,8 +350,11 @@ export async function readChannelAllowFromStore(
|
||||
|
||||
const scopedPath = resolveAllowFromPath(channel, env, accountId);
|
||||
const scopedEntries = await readAllowFromStateForPath(channel, scopedPath);
|
||||
if (!shouldIncludeLegacyAllowFromEntries(normalizedAccountId)) {
|
||||
return scopedEntries;
|
||||
}
|
||||
// Backward compatibility: legacy channel-level allowFrom store was unscoped.
|
||||
// Keep honoring it alongside account-scoped files to prevent re-pair prompts after upgrades.
|
||||
// Keep honoring it for default account to prevent re-pair prompts after upgrades.
|
||||
const legacyPath = resolveAllowFromPath(channel, env);
|
||||
const legacyEntries = await readAllowFromStateForPath(channel, legacyPath);
|
||||
return dedupePreserveOrder([...scopedEntries, ...legacyEntries]);
|
||||
@@ -364,6 +373,9 @@ export function readChannelAllowFromStoreSync(
|
||||
|
||||
const scopedPath = resolveAllowFromPath(channel, env, accountId);
|
||||
const scopedEntries = readAllowFromStateForPathSync(channel, scopedPath);
|
||||
if (!shouldIncludeLegacyAllowFromEntries(normalizedAccountId)) {
|
||||
return scopedEntries;
|
||||
}
|
||||
const legacyPath = resolveAllowFromPath(channel, env);
|
||||
const legacyEntries = readAllowFromStateForPathSync(channel, legacyPath);
|
||||
return dedupePreserveOrder([...scopedEntries, ...legacyEntries]);
|
||||
@@ -503,7 +515,12 @@ export async function upsertChannelPairingRequest(params: {
|
||||
nowMs,
|
||||
);
|
||||
reqs = prunedExpired;
|
||||
const existingIdx = reqs.findIndex((r) => r.id === id);
|
||||
const existingIdx = reqs.findIndex((r) => {
|
||||
if (r.id !== id) {
|
||||
return false;
|
||||
}
|
||||
return requestMatchesAccountId(r, normalizePairingAccountId(normalizedAccountId));
|
||||
});
|
||||
const existingCodes = new Set(
|
||||
reqs.map((req) =>
|
||||
String(req.code ?? "")
|
||||
|
||||
Reference in New Issue
Block a user