fix(voice-call): pin plivo callback origins (#58238)

This commit is contained in:
Vincent Koc 2026-03-31 19:50:35 +09:00 committed by GitHub
parent cf3ae2612b
commit efe9183f9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 34 additions and 0 deletions

View File

@ -78,6 +78,7 @@ Docs: https://docs.openclaw.ai
- Memory/QMD: keep `memory_search` session-hit paths roundtrip-safe when exported session markdown lives under the workspace `qmd/` directory, so `memory_get` can read the exact returned path instead of failing on the generic `qmd/sessions/...` alias. (#43519) Thanks @holgergruenhagen and @vincentkoc.
- Memory/QMD: treat null-byte collection corruption the same when QMD surfaces it as `ENOENT`, so managed-collection repair still rebuilds and retries instead of leaving QMD stuck on a broken path. Thanks @vincentkoc.
- Memory/QMD: stop rewriting Han/CJK BM25 queries before `qmd search`, so OpenClaw search semantics match direct QMD results for mixed and spaced Chinese queries. Thanks @vincentkoc.
- Voice call/Plivo: pin stored callback bases to the configured public webhook URL so later call-control redirects stay on the intended origin even if webhook transport metadata differs. Thanks @zsxsoft and @vincentkoc.
- Agents/memory flush: keep daily memory flush files append-only during embedded attempts so compaction writes do not overwrite earlier notes. (#53725) Thanks @HPluseven.
- Web UI/markdown: stop bare auto-links from swallowing adjacent CJK text while preserving valid mixed-script path and query characters in rendered links. (#48410) Thanks @jnuyao.
- BlueBubbles/iMessage: coalesce URL-only inbound messages with their link-preview balloon again so sharing a bare link no longer drops the URL from agent context. Thanks @vincentkoc.

View File

@ -64,4 +64,30 @@ describe("PlivoProvider", () => {
"plivo:v3:verified",
);
});
it("pins stored callback bases to publicUrl instead of request Host", () => {
const provider = new PlivoProvider(
{
authId: "MA000000000000000000",
authToken: "test-token",
},
{
publicUrl: "https://voice.openclaw.ai/voice/webhook?provider=plivo",
},
);
provider.parseWebhookEvent({
headers: { host: "attacker.example" },
rawBody:
"CallUUID=call-uuid&CallStatus=in-progress&Direction=outbound&From=%2B15550000000&To=%2B15550000001&Event=StartApp",
url: "https://attacker.example/voice/webhook?provider=plivo&flow=answer&callId=internal-call-id",
method: "POST",
query: { provider: "plivo", flow: "answer", callId: "internal-call-id" },
});
const callbackMap = (provider as unknown as { callUuidToWebhookUrl: Map<string, string> })
.callUuidToWebhookUrl;
expect(callbackMap.get("call-uuid")).toBe("https://voice.openclaw.ai/voice/webhook");
});
});

View File

@ -544,6 +544,13 @@ export class PlivoProvider implements VoiceCallProvider {
private baseWebhookUrlFromCtx(ctx: WebhookContext): string | null {
try {
if (this.options.publicUrl) {
const base = new URL(this.options.publicUrl);
const requestUrl = new URL(ctx.url);
base.pathname = requestUrl.pathname;
return `${base.origin}${base.pathname}`;
}
const u = new URL(
reconstructWebhookUrl(ctx, {
allowedHosts: this.options.webhookSecurity?.allowedHosts,