Skip to content

Commit

Permalink
fix: add support for integrity option to Fetch (nodejs#1596)
Browse files Browse the repository at this point in the history
* Test case for fetch() integrity option.

* Implement matchRequestIntegrity

* Add test to cover encoded body

* Speed up integrity test completion

* Fix trailing spaces
  • Loading branch information
jelmervdl authored and crysmags committed Feb 27, 2024
1 parent 85a64c9 commit d48369f
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 26 deletions.
4 changes: 3 additions & 1 deletion lib/fetch/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
25 changes: 0 additions & 25 deletions test/fetch/client-fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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))
// })
74 changes: 74 additions & 0 deletions test/fetch/integrity.js
Original file line number Diff line number Diff line change
@@ -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()
})
})

0 comments on commit d48369f

Please sign in to comment.