fix(signal): reject sibling account collisions

This commit is contained in:
Jesse Merhi
2026-08-09 14:46:00 +10:00
committed by jesse-merhi
parent 904716e4b4
commit d68e939412
2 changed files with 43 additions and 0 deletions
@@ -447,6 +447,39 @@ describe("signalSetupWizard QR linking", () => {
expect(result.cfg.channels?.signal?.accounts?.work?.account).toBe("+15555550444");
});
it("does not assign a QR-linked identity already owned by a sibling account", async () => {
const transport = {
kind: "managed-native" as const,
cliPath: "/opt/signal-cli",
configPath: "~/.local/share/signal-cli",
};
const cfg = {
channels: {
signal: {
defaultAccount: "default",
accounts: {
default: { account: "+15555550123", transport },
work: { transport },
},
},
},
};
const note = vi.fn<WizardPrompter["note"]>(async () => undefined);
const result = await configure({
cfg,
accountId: "work",
prompter: { ...createQrPrompter(), note },
});
expect(result.cfg.channels?.signal?.accounts?.default?.account).toBe("+15555550123");
expect(result.cfg.channels?.signal?.accounts?.work?.account).toBeUndefined();
expect(note).toHaveBeenCalledWith(
"+15555550123 is already assigned to another OpenClaw Signal account. The selected account was not changed.",
"Signal account linking",
);
});
it("finishes setup when signal-cli completes before the QR is acknowledged", async () => {
const qrCode = vi.fn(async (params: Parameters<NonNullable<WizardPrompter["qrCode"]>>[0]) => {
await params.dismissed;
+10
View File
@@ -211,6 +211,16 @@ export const signalSetupWizard: ChannelSetupWizard = {
// Preserve that authoritative result even if cancellation raced its final output.
preparedCredentialValues[SIGNAL_LINK_COMPLETED_INPUT_KEY] = "true";
if (linkResult.associatedAccount) {
if (siblingAccounts.has(linkResult.associatedAccount)) {
// The device link succeeded, but this setup target must not claim a sibling's identity.
// Mark it handled so the later number prompt cannot recreate the same collision.
preparedCredentialValues[SIGNAL_LINKED_ACCOUNT_INPUT_KEY] = "true";
await prompter.note(
`${linkResult.associatedAccount} is already assigned to another OpenClaw Signal account. The selected account was not changed.`,
"Signal account linking",
);
return { credentialValues: preparedCredentialValues };
}
if (configuredAccount && linkResult.associatedAccount !== configuredAccount) {
const replaceConfiguredAccount = await prompter.confirm({
message: `Use ${linkResult.associatedAccount} instead of configured Signal account ${configuredAccount}?`,