mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-22 06:32:00 +00:00
fix(voice-call): stabilize plivo v2 replay keys
This commit is contained in:
@@ -256,6 +256,52 @@ describe("verifyPlivoWebhook", () => {
|
||||
expectReplayResultPair(first, second);
|
||||
});
|
||||
|
||||
it("treats query-only V2 variants as the same verified request", () => {
|
||||
const authToken = "test-auth-token";
|
||||
const nonce = "nonce-replay-v2";
|
||||
const verificationUrl = "https://example.com/voice/webhook";
|
||||
const signature = plivoV2Signature({
|
||||
authToken,
|
||||
urlNoQuery: verificationUrl,
|
||||
nonce,
|
||||
});
|
||||
|
||||
const baseHeaders = {
|
||||
host: "example.com",
|
||||
"x-forwarded-proto": "https",
|
||||
"x-plivo-signature-v2": signature,
|
||||
"x-plivo-signature-v2-nonce": nonce,
|
||||
};
|
||||
const rawBody = "CallUUID=uuid&CallStatus=in-progress";
|
||||
|
||||
const first = verifyPlivoWebhook(
|
||||
{
|
||||
headers: baseHeaders,
|
||||
rawBody,
|
||||
url: `${verificationUrl}?flow=answer&callId=abc`,
|
||||
method: "POST",
|
||||
query: { flow: "answer", callId: "abc" },
|
||||
},
|
||||
authToken,
|
||||
);
|
||||
const second = verifyPlivoWebhook(
|
||||
{
|
||||
headers: baseHeaders,
|
||||
rawBody,
|
||||
url: `${verificationUrl}?flow=getinput&callId=abc`,
|
||||
method: "POST",
|
||||
query: { flow: "getinput", callId: "abc" },
|
||||
},
|
||||
authToken,
|
||||
);
|
||||
|
||||
expect(first.ok).toBe(true);
|
||||
expect(first.verifiedRequestKey).toBeDefined();
|
||||
expect(second.ok).toBe(true);
|
||||
expect(second.verifiedRequestKey).toBe(first.verifiedRequestKey);
|
||||
expect(second.isReplay).toBe(true);
|
||||
});
|
||||
|
||||
it("returns a stable request key when verification is skipped", () => {
|
||||
const ctx = {
|
||||
headers: {},
|
||||
|
||||
@@ -724,6 +724,10 @@ function getBaseUrlNoQuery(url: string): string {
|
||||
return `${u.protocol}//${u.host}${u.pathname}`;
|
||||
}
|
||||
|
||||
function createPlivoV2ReplayKey(url: string, nonce: string): string {
|
||||
return `plivo:v2:${sha256Hex(`${getBaseUrlNoQuery(url)}\n${nonce}`)}`;
|
||||
}
|
||||
|
||||
function timingSafeEqualString(a: string, b: string): boolean {
|
||||
if (a.length !== b.length) {
|
||||
const dummy = Buffer.from(a);
|
||||
@@ -967,7 +971,7 @@ export function verifyPlivoWebhook(
|
||||
reason: "Invalid Plivo V2 signature",
|
||||
};
|
||||
}
|
||||
const replayKey = `plivo:v2:${sha256Hex(`${verificationUrl}\n${nonceV2}`)}`;
|
||||
const replayKey = createPlivoV2ReplayKey(verificationUrl, nonceV2);
|
||||
const isReplay = markReplay(plivoReplayCache, replayKey);
|
||||
return { ok: true, version: "v2", verificationUrl, isReplay, verifiedRequestKey: replayKey };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user