Skip to content

Commit

Permalink
fix: parseHashWithOptions regex (nodejs#2561)
Browse files Browse the repository at this point in the history
  • Loading branch information
flapenna authored and crysmags committed Feb 27, 2024
1 parent 9bfddf6 commit a909a0f
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 2 deletions.
5 changes: 3 additions & 2 deletions lib/fetch/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ function bytesMatch (bytes, metadataList) {
// https://w3c.github.io/webappsec-subresource-integrity/#grammardef-hash-with-options
// https://www.w3.org/TR/CSP2/#source-list-syntax
// https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1
const parseHashWithOptions = /((?<algo>sha256|sha384|sha512)-(?<hash>[A-z0-9+/]{1}.*={0,2}))( +[\x21-\x7e]?)?/i
const parseHashWithOptions = /(?<algo>sha256|sha384|sha512)-(?<hash>[A-Za-z0-9+/]+={0,2}(?=\s|$))( +[!-~]*)?/i

/**
* @see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
Expand Down Expand Up @@ -1213,5 +1213,6 @@ module.exports = {
readAllBytes,
normalizeMethodRecord,
simpleRangeHeaderValue,
buildContentRange
buildContentRange,
parseMetadata
}
76 changes: 76 additions & 0 deletions test/fetch/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { test } = t

const util = require('../../lib/fetch/util')
const { HeadersList } = require('../../lib/fetch/headers')
const { createHash } = require('crypto')

test('responseURL', (t) => {
t.plan(2)
Expand Down Expand Up @@ -279,3 +280,78 @@ test('setRequestReferrerPolicyOnRedirect', nested => {
t.equal(request.referrerPolicy, initial)
})
})

test('parseMetadata', t => {
t.test('should parse valid metadata with option', t => {
const body = 'Hello world!'
const hash256 = createHash('sha256').update(body).digest('base64')
const hash384 = createHash('sha384').update(body).digest('base64')
const hash512 = createHash('sha512').update(body).digest('base64')

const validMetadata = `sha256-${hash256} !@ sha384-${hash384} !@ sha512-${hash512} !@`
const result = util.parseMetadata(validMetadata)

t.same(result, [
{ algo: 'sha256', hash: hash256 },
{ algo: 'sha384', hash: hash384 },
{ algo: 'sha512', hash: hash512 }
])

t.end()
})

t.test('should parse valid metadata with non ASCII chars option', t => {
const body = 'Hello world!'
const hash256 = createHash('sha256').update(body).digest('base64')
const hash384 = createHash('sha384').update(body).digest('base64')
const hash512 = createHash('sha512').update(body).digest('base64')

const validMetadata = `sha256-${hash256} !© sha384-${hash384} !€ sha512-${hash512} !µ`
const result = util.parseMetadata(validMetadata)

t.same(result, [
{ algo: 'sha256', hash: hash256 },
{ algo: 'sha384', hash: hash384 },
{ algo: 'sha512', hash: hash512 }
])

t.end()
})

t.test('should parse valid metadata without option', t => {
const body = 'Hello world!'
const hash256 = createHash('sha256').update(body).digest('base64')
const hash384 = createHash('sha384').update(body).digest('base64')
const hash512 = createHash('sha512').update(body).digest('base64')

const validMetadata = `sha256-${hash256} sha384-${hash384} sha512-${hash512}`
const result = util.parseMetadata(validMetadata)

t.same(result, [
{ algo: 'sha256', hash: hash256 },
{ algo: 'sha384', hash: hash384 },
{ algo: 'sha512', hash: hash512 }
])

t.end()
})

t.test('should ignore invalid metadata with invalid base64 chars', t => {
const body = 'Hello world!'
const hash256 = createHash('sha256').update(body).digest('base64')
const invalidHash384 = 'zifp5hE1Xl5LQQqQz[]Bq/iaq9Wb6jVb//T7EfTmbXD2aEP5c2ZdJr9YTDfcTE1ZH+'
const hash512 = createHash('sha512').update(body).digest('base64')

const validMetadata = `sha256-${hash256} sha384-${invalidHash384} sha512-${hash512}`
const result = util.parseMetadata(validMetadata)

t.same(result, [
{ algo: 'sha256', hash: hash256 },
{ algo: 'sha512', hash: hash512 }
])

t.end()
})

t.end()
})

0 comments on commit a909a0f

Please sign in to comment.