Loading CHANGES.md +16 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,11 @@ Version 1.7.2 To be released. - Fixed HTTP signature verification to handle malformed RFC 9421 signatures gracefully instead of returning `500 Internal Server Error` responses. Malformed signatures now properly fail verification and return appropriate error responses. Version 1.7.1 ------------- Loading Loading @@ -49,6 +54,17 @@ Released on June 25, 2025. [#252]: https://github.com/fedify-dev/fedify/pull/252 Version 1.6.4 ------------- Released on July 2, 2025. - Fixed HTTP signature verification to handle malformed RFC 9421 signatures gracefully instead of returning `500 Internal Server Error` responses. Malformed signatures now properly fail verification and return appropriate error responses. Version 1.6.3 ------------- Loading fedify/sig/http.test.ts +64 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { assertExists, assertFalse, assertStringIncludes, assertThrows, } from "@std/assert"; import { encodeBase64 } from "byte-encodings/base64"; import fetchMock from "fetch-mock"; Loading Loading @@ -1813,3 +1814,66 @@ test("timingSafeEqual()", async (t) => { }, ); }); test("signRequest() [rfc9421] error handling for invalid signature base creation", async () => { // Test that createRfc9421SignatureBase errors are properly caught and wrapped // We'll test this by directly calling createRfc9421SignatureBase with invalid input const request = new Request("https://example.com/test", { method: "POST", body: "test body", }); // First verify that createRfc9421SignatureBase throws for unsupported components await assertThrows( () => { createRfc9421SignatureBase( request, ["@unsupported"], // This will trigger the "Unsupported derived component" error 'alg="rsa-pss-sha256";keyid="https://example.com/key2";created=1234567890', ); }, Error, "Unsupported derived component: @unsupported", ); // The actual error handling in signRequest is tested indirectly by ensuring // that normal signing operations work without throwing the wrapped error const signedRequest = await signRequest( request, rsaPrivateKey2, new URL("https://example.com/key2"), { spec: "rfc9421" }, ); // Verify that the request was signed successfully assertExists(signedRequest.headers.get("Signature-Input")); assertExists(signedRequest.headers.get("Signature")); }); test("verifyRequest() [rfc9421] error handling for invalid signature base creation", async () => { // Create a request with a malformed signature input that will cause createRfc9421SignatureBase to fail const request = new Request("https://example.com/test", { method: "GET", headers: { "Accept": "application/json", // Add a malformed signature input that references an unsupported component "Signature-Input": 'sig1=("@unsupported");alg="rsa-pss-sha256";keyid="https://example.com/key2";created=1234567890', "Signature": "sig1=:invalid_signature_data:", }, }); // Attempt verification with the malformed signature input // This should fail gracefully and return null instead of throwing const result = await verifyRequest(request, { spec: "rfc9421", documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader, }); assertEquals( result, null, "Verification should fail gracefully for malformed signature inputs", ); }); fedify/sig/http.ts +30 −13 Original line number Diff line number Diff line Loading @@ -434,7 +434,9 @@ async function signRequestRfc9421( keyId, created, }); const signatureBase = createRfc9421SignatureBase( let signatureBase: string; try { signatureBase = createRfc9421SignatureBase( new Request(request.url, { method: request.method, headers, Loading @@ -442,6 +444,12 @@ async function signRequestRfc9421( components, signatureParams, ); } catch (error) { throw new TypeError( `Failed to create signature base: ${String(error)}; it is probably ` + `a bug in the implementation. Please report it at Fedify's issue tracker.`, ); } // Sign the signature base const signatureBytes = await crypto.subtle.sign( Loading Loading @@ -1094,11 +1102,20 @@ async function verifyRequestRfc9421( } // Rebuild the signature base for verification const signatureBase = createRfc9421SignatureBase( let signatureBase: string; try { signatureBase = createRfc9421SignatureBase( request, sigInput.components, sigInput.parameters, ); } catch (error) { logger.debug( "Failed to create signature base for verification: {error}", { error, signatureInput: sigInput }, ); continue; } const signatureBaseBytes = new TextEncoder().encode(signatureBase); // Verify the signature Loading Loading
CHANGES.md +16 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,11 @@ Version 1.7.2 To be released. - Fixed HTTP signature verification to handle malformed RFC 9421 signatures gracefully instead of returning `500 Internal Server Error` responses. Malformed signatures now properly fail verification and return appropriate error responses. Version 1.7.1 ------------- Loading Loading @@ -49,6 +54,17 @@ Released on June 25, 2025. [#252]: https://github.com/fedify-dev/fedify/pull/252 Version 1.6.4 ------------- Released on July 2, 2025. - Fixed HTTP signature verification to handle malformed RFC 9421 signatures gracefully instead of returning `500 Internal Server Error` responses. Malformed signatures now properly fail verification and return appropriate error responses. Version 1.6.3 ------------- Loading
fedify/sig/http.test.ts +64 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import { assertExists, assertFalse, assertStringIncludes, assertThrows, } from "@std/assert"; import { encodeBase64 } from "byte-encodings/base64"; import fetchMock from "fetch-mock"; Loading Loading @@ -1813,3 +1814,66 @@ test("timingSafeEqual()", async (t) => { }, ); }); test("signRequest() [rfc9421] error handling for invalid signature base creation", async () => { // Test that createRfc9421SignatureBase errors are properly caught and wrapped // We'll test this by directly calling createRfc9421SignatureBase with invalid input const request = new Request("https://example.com/test", { method: "POST", body: "test body", }); // First verify that createRfc9421SignatureBase throws for unsupported components await assertThrows( () => { createRfc9421SignatureBase( request, ["@unsupported"], // This will trigger the "Unsupported derived component" error 'alg="rsa-pss-sha256";keyid="https://example.com/key2";created=1234567890', ); }, Error, "Unsupported derived component: @unsupported", ); // The actual error handling in signRequest is tested indirectly by ensuring // that normal signing operations work without throwing the wrapped error const signedRequest = await signRequest( request, rsaPrivateKey2, new URL("https://example.com/key2"), { spec: "rfc9421" }, ); // Verify that the request was signed successfully assertExists(signedRequest.headers.get("Signature-Input")); assertExists(signedRequest.headers.get("Signature")); }); test("verifyRequest() [rfc9421] error handling for invalid signature base creation", async () => { // Create a request with a malformed signature input that will cause createRfc9421SignatureBase to fail const request = new Request("https://example.com/test", { method: "GET", headers: { "Accept": "application/json", // Add a malformed signature input that references an unsupported component "Signature-Input": 'sig1=("@unsupported");alg="rsa-pss-sha256";keyid="https://example.com/key2";created=1234567890', "Signature": "sig1=:invalid_signature_data:", }, }); // Attempt verification with the malformed signature input // This should fail gracefully and return null instead of throwing const result = await verifyRequest(request, { spec: "rfc9421", documentLoader: mockDocumentLoader, contextLoader: mockDocumentLoader, }); assertEquals( result, null, "Verification should fail gracefully for malformed signature inputs", ); });
fedify/sig/http.ts +30 −13 Original line number Diff line number Diff line Loading @@ -434,7 +434,9 @@ async function signRequestRfc9421( keyId, created, }); const signatureBase = createRfc9421SignatureBase( let signatureBase: string; try { signatureBase = createRfc9421SignatureBase( new Request(request.url, { method: request.method, headers, Loading @@ -442,6 +444,12 @@ async function signRequestRfc9421( components, signatureParams, ); } catch (error) { throw new TypeError( `Failed to create signature base: ${String(error)}; it is probably ` + `a bug in the implementation. Please report it at Fedify's issue tracker.`, ); } // Sign the signature base const signatureBytes = await crypto.subtle.sign( Loading Loading @@ -1094,11 +1102,20 @@ async function verifyRequestRfc9421( } // Rebuild the signature base for verification const signatureBase = createRfc9421SignatureBase( let signatureBase: string; try { signatureBase = createRfc9421SignatureBase( request, sigInput.components, sigInput.parameters, ); } catch (error) { logger.debug( "Failed to create signature base for verification: {error}", { error, signatureInput: sigInput }, ); continue; } const signatureBaseBytes = new TextEncoder().encode(signatureBase); // Verify the signature Loading