mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 04:30:42 +00:00
fix(feishu): harden webhook signature compare
This commit is contained in:
@@ -32,6 +32,15 @@ function isFeishuWebhookPayload(value: unknown): value is Record<string, unknown
|
||||
return !!value && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function timingSafeEqualString(left: string, right: string): boolean {
|
||||
const leftBuffer = Buffer.from(left, "utf8");
|
||||
const rightBuffer = Buffer.from(right, "utf8");
|
||||
if (leftBuffer.length !== rightBuffer.length) {
|
||||
return false;
|
||||
}
|
||||
return crypto.timingSafeEqual(leftBuffer, rightBuffer);
|
||||
}
|
||||
|
||||
function buildFeishuWebhookEnvelope(
|
||||
req: http.IncomingMessage,
|
||||
payload: Record<string, unknown>,
|
||||
@@ -63,7 +72,7 @@ function isFeishuWebhookSignatureValid(params: {
|
||||
.createHash("sha256")
|
||||
.update(timestamp + nonce + encryptKey + JSON.stringify(params.payload))
|
||||
.digest("hex");
|
||||
return computedSignature === signature;
|
||||
return timingSafeEqualString(computedSignature, signature);
|
||||
}
|
||||
|
||||
function respondText(res: http.ServerResponse, statusCode: number, body: string): void {
|
||||
|
||||
@@ -114,6 +114,34 @@ describe("Feishu webhook signed-request e2e", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects malformed short signatures with 401", async () => {
|
||||
probeFeishuMock.mockResolvedValue({ ok: true, botOpenId: "bot_open_id" });
|
||||
|
||||
await withRunningWebhookMonitor(
|
||||
{
|
||||
accountId: "short-signature",
|
||||
path: "/hook-e2e-short-signature",
|
||||
verificationToken: "verify_token",
|
||||
encryptKey: "encrypt_key",
|
||||
},
|
||||
monitorFeishuProvider,
|
||||
async (url) => {
|
||||
const payload = { type: "url_verification", challenge: "challenge-token" };
|
||||
const headers = signFeishuPayload({ encryptKey: "encrypt_key", payload });
|
||||
headers["x-lark-signature"] = headers["x-lark-signature"].slice(0, 12);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
expect(await response.text()).toBe("Invalid signature");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("returns 400 for invalid json before invoking the sdk", async () => {
|
||||
probeFeishuMock.mockResolvedValue({ ok: true, botOpenId: "bot_open_id" });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user