Compare commits

...

3 Commits

Author SHA1 Message Date
Vignesh Natarajan
4814c6797c Signal: harden E.164 validation 2026-02-12 15:21:42 -08:00
Vignesh Natarajan
22bc5e5ca9 Changelog: credit Signal account validation 2026-02-12 15:15:40 -08:00
Vignesh Natarajan
0969eaf038 Signal: validate account input 2026-02-12 15:14:24 -08:00
3 changed files with 75 additions and 8 deletions

View File

@@ -39,6 +39,7 @@ Docs: https://docs.openclaw.ai
- BlueBubbles: fix webhook auth bypass via loopback proxy trust. (#13787) Thanks @coygeek.
- Slack: change default replyToMode from "off" to "all". (#14364) Thanks @nm-de.
- Slack: detect control commands when channel messages start with bot mention prefixes (for example, `@Bot /new`). (#14142) Thanks @beefiker.
- Signal: enforce E.164 validation for the Signal bot account prompt so mistyped numbers are caught early. (#15063) Thanks @Duartemartins.
- Signal: render mention placeholders as `@uuid`/`@phone` so mention gating and Clawdbot targeting work. (#2013) Thanks @alexgleason.
- Onboarding/Providers: add Z.AI endpoint-specific auth choices (`zai-coding-global`, `zai-coding-cn`, `zai-global`, `zai-cn`) and expand default Z.AI model wiring. (#13456) Thanks @tomsun28.
- Onboarding/Providers: update MiniMax API default/recommended models from M2.1 to M2.5, add M2.5/M2.5-Lightning model entries, and include `minimax-m2.5` in modern model filtering. (#14865) Thanks @adao-max.

View File

@@ -0,0 +1,31 @@
import { describe, expect, it } from "vitest";
import { normalizeSignalAccountInput } from "./signal.js";
describe("normalizeSignalAccountInput", () => {
it("accepts already normalized numbers", () => {
expect(normalizeSignalAccountInput("+15555550123")).toBe("+15555550123");
});
it("normalizes formatted input", () => {
expect(normalizeSignalAccountInput(" +1 (555) 000-1234 ")).toBe("+15550001234");
});
it("rejects empty input", () => {
expect(normalizeSignalAccountInput(" ")).toBeNull();
});
it("rejects non-numeric input", () => {
expect(normalizeSignalAccountInput("ok")).toBeNull();
expect(normalizeSignalAccountInput("++--")).toBeNull();
});
it("rejects inputs with stray + characters", () => {
expect(normalizeSignalAccountInput("++12345")).toBeNull();
expect(normalizeSignalAccountInput("+1+2345")).toBeNull();
});
it("rejects numbers that are too short or too long", () => {
expect(normalizeSignalAccountInput("+1234")).toBeNull();
expect(normalizeSignalAccountInput("+1234567890123456")).toBeNull();
});
});

View File

@@ -16,6 +16,27 @@ import { normalizeE164 } from "../../../utils.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "signal" as const;
const MIN_E164_DIGITS = 5;
const MAX_E164_DIGITS = 15;
const DIGITS_ONLY = /^\d+$/;
const INVALID_SIGNAL_ACCOUNT_ERROR =
"Invalid E.164 phone number (must start with + and country code, e.g. +15555550123)";
export function normalizeSignalAccountInput(value: string | null | undefined): string | null {
const trimmed = value?.trim();
if (!trimmed) {
return null;
}
const normalized = normalizeE164(trimmed);
const digits = normalized.slice(1);
if (!DIGITS_ONLY.test(digits)) {
return null;
}
if (digits.length < MIN_E164_DIGITS || digits.length > MAX_E164_DIGITS) {
return null;
}
return `+${digits}`;
}
function setSignalDmPolicy(cfg: OpenClawConfig, dmPolicy: DmPolicy) {
const allowFrom =
@@ -243,22 +264,36 @@ export const signalOnboardingAdapter: ChannelOnboardingAdapter = {
let account = accountConfig.account ?? "";
if (account) {
const keep = await prompter.confirm({
message: `Signal account set (${account}). Keep it?`,
initialValue: true,
});
if (!keep) {
const normalizedExisting = normalizeSignalAccountInput(account);
if (!normalizedExisting) {
await prompter.note(
"Existing Signal account isn't a valid E.164 number. Please enter it again.",
"Signal",
);
account = "";
} else {
account = normalizedExisting;
const keep = await prompter.confirm({
message: `Signal account set (${account}). Keep it?`,
initialValue: true,
});
if (!keep) {
account = "";
}
}
}
if (!account) {
account = String(
const rawAccount = String(
await prompter.text({
message: "Signal bot number (E.164)",
validate: (value) => (value?.trim() ? undefined : "Required"),
validate: (value) =>
normalizeSignalAccountInput(String(value ?? ""))
? undefined
: INVALID_SIGNAL_ACCOUNT_ERROR,
}),
).trim();
);
account = normalizeSignalAccountInput(rawAccount) ?? "";
}
if (account) {