From 4888b7de4609acb4cc263017fbe255b9363faf14 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Fri, 12 Aug 2022 15:34:23 +0100 Subject: [PATCH] fix: add support for `integrity` option to Fetch (#1596) * Test case for fetch() integrity option. * Implement matchRequestIntegrity * Add test to cover encoded body * Speed up integrity test completion * Fix trailing spaces --- lib/fetch/util.js | 4 ++- test/fetch/client-fetch.js | 25 ------------- test/fetch/integrity.js | 74 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 26 deletions(-) create mode 100644 test/fetch/integrity.js diff --git a/lib/fetch/util.js b/lib/fetch/util.js index 27adea72085..ae20a05d815 100644 --- a/lib/fetch/util.js +++ b/lib/fetch/util.js @@ -5,6 +5,7 @@ const { performance } = require('perf_hooks') const { isBlobLike, toUSVString, ReadableStreamFrom } = require('../core/util') const assert = require('assert') const { isUint8Array } = require('util/types') +const { createHash } = require('crypto') let File @@ -341,7 +342,8 @@ function determineRequestsReferrer (request) { } function matchRequestIntegrity (request, bytes) { - return false + const [algo, expectedHashValue] = request.integrity.split('-', 2) + return createHash(algo).update(bytes).digest('hex') === expectedHashValue } // https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-request diff --git a/test/fetch/client-fetch.js b/test/fetch/client-fetch.js index 935abe9f0d0..4ec4545d544 100644 --- a/test/fetch/client-fetch.js +++ b/test/fetch/client-fetch.js @@ -536,28 +536,3 @@ test('Receiving non-Latin1 headers', async (t) => { t.same(lengths, [30, 34, 94, 104, 90]) t.end() }) - -// https://github.com/nodejs/undici/issues/1594 -// TODO(@KhafraDev): this test fails because of an integrity-mismatch check -// that hasn't been implemented when this comment was written. Enable this -// check once a resource's integrity is checked. -// test('with RequestInit.integrity set', async (t) => { -// const body = 'Hello world' -// const hash = require('crypto').createHash('sha256').update(body).digest('hex') -// -// const server = createServer((req, res) => { -// res.write(body) -// res.end() -// }).listen(0) -// -// t.teardown(server.close.bind(server)) -// await once(server, 'listening') -// -// const response = await fetch(`http://localhost:${server.address().port}`, { -// integrity: `sha256-${hash}` -// }) -// -// const ab = await response.arrayBuffer() -// -// t.same(new Uint8Array(ab), new TextEncoder().encode(body)) -// }) diff --git a/test/fetch/integrity.js b/test/fetch/integrity.js new file mode 100644 index 00000000000..bd464218d55 --- /dev/null +++ b/test/fetch/integrity.js @@ -0,0 +1,74 @@ +'use strict' + +const { test } = require('tap') +const { createServer } = require('http') +const { createHash } = require('crypto') +const { gzipSync } = require('zlib') +const { fetch, setGlobalDispatcher, Agent } = require('../..') + +setGlobalDispatcher(new Agent({ + keepAliveTimeout: 1, + keepAliveMaxTimeout: 1 +})) + +test('request with correct integrity checksum', (t) => { + const body = 'Hello world!' + const hash = createHash('sha256').update(body).digest('hex') + + const server = createServer((req, res) => { + res.end(body) + }) + + t.teardown(server.close.bind(server)) + + server.listen(0, async () => { + const response = await fetch(`http://localhost:${server.address().port}`, { + integrity: `sha256-${hash}` + }) + t.strictSame(body, await response.text()) + t.end() + }) +}) + +test('request with wrong integrity checksum', (t) => { + const body = 'Hello world!' + const hash = 'c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51b' + + const server = createServer((req, res) => { + res.end(body) + }) + + t.teardown(server.close.bind(server)) + + server.listen(0, () => { + fetch(`http://localhost:${server.address().port}`, { + integrity: `sha256-${hash}` + }).then(response => { + t.fail('fetch did not fail') + }).catch((err) => { + t.equal(err.cause.message, 'integrity mismatch') + }).finally(() => { + t.end() + }) + }) +}) + +test('request with integrity checksum on encoded body', (t) => { + const body = 'Hello world!' + const hash = createHash('sha256').update(body).digest('hex') + + const server = createServer((req, res) => { + res.setHeader('content-encoding', 'gzip') + res.end(gzipSync(body)) + }) + + t.teardown(server.close.bind(server)) + + server.listen(0, async () => { + const response = await fetch(`http://localhost:${server.address().port}`, { + integrity: `sha256-${hash}` + }) + t.strictSame(body, await response.text()) + t.end() + }) +})