@@ -11,20 +11,6 @@ const assert = require('node:assert')
1111const { isUint8Array } = require ( 'node:util/types' )
1212const { webidl } = require ( '../webidl' )
1313
14- let supportedHashes = [ ]
15-
16- // https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable
17- /** @type {import('crypto') } */
18- let crypto
19- try {
20- crypto = require ( 'node:crypto' )
21- const possibleRelevantHashes = [ 'sha256' , 'sha384' , 'sha512' ]
22- supportedHashes = crypto . getHashes ( ) . filter ( ( hash ) => possibleRelevantHashes . includes ( hash ) )
23- /* c8 ignore next 3 */
24- } catch {
25-
26- }
27-
2814function responseURL ( response ) {
2915 // https://fetch.spec.whatwg.org/#responses
3016 // A response has an associated URL. It is a pointer to the last URL
@@ -698,206 +684,6 @@ function isURLPotentiallyTrustworthy (url) {
698684 return isOriginPotentiallyTrustworthy ( url . origin )
699685}
700686
701- /**
702- * @see https://w3c.github.io/webappsec-subresource-integrity/#does-response-match-metadatalist
703- * @param {Uint8Array } bytes
704- * @param {string } metadataList
705- */
706- function bytesMatch ( bytes , metadataList ) {
707- // If node is not built with OpenSSL support, we cannot check
708- // a request's integrity, so allow it by default (the spec will
709- // allow requests if an invalid hash is given, as precedence).
710- /* istanbul ignore if: only if node is built with --without-ssl */
711- if ( crypto === undefined ) {
712- return true
713- }
714-
715- // 1. Let parsedMetadata be the result of parsing metadataList.
716- const parsedMetadata = parseMetadata ( metadataList )
717-
718- // 2. If parsedMetadata is no metadata, return true.
719- if ( parsedMetadata === 'no metadata' ) {
720- return true
721- }
722-
723- // 3. If response is not eligible for integrity validation, return false.
724- // TODO
725-
726- // 4. If parsedMetadata is the empty set, return true.
727- if ( parsedMetadata . length === 0 ) {
728- return true
729- }
730-
731- // 5. Let metadata be the result of getting the strongest
732- // metadata from parsedMetadata.
733- const strongest = getStrongestMetadata ( parsedMetadata )
734- const metadata = filterMetadataListByAlgorithm ( parsedMetadata , strongest )
735-
736- // 6. For each item in metadata:
737- for ( const item of metadata ) {
738- // 1. Let algorithm be the alg component of item.
739- const algorithm = item . algo
740-
741- // 2. Let expectedValue be the val component of item.
742- const expectedValue = item . hash
743-
744- // See https://github.com/web-platform-tests/wpt/commit/e4c5cc7a5e48093220528dfdd1c4012dc3837a0e
745- // "be liberal with padding". This is annoying, and it's not even in the spec.
746-
747- // 3. Let actualValue be the result of applying algorithm to bytes.
748- let actualValue = crypto . createHash ( algorithm ) . update ( bytes ) . digest ( 'base64' )
749-
750- if ( actualValue [ actualValue . length - 1 ] === '=' ) {
751- if ( actualValue [ actualValue . length - 2 ] === '=' ) {
752- actualValue = actualValue . slice ( 0 , - 2 )
753- } else {
754- actualValue = actualValue . slice ( 0 , - 1 )
755- }
756- }
757-
758- // 4. If actualValue is a case-sensitive match for expectedValue,
759- // return true.
760- if ( compareBase64Mixed ( actualValue , expectedValue ) ) {
761- return true
762- }
763- }
764-
765- // 7. Return false.
766- return false
767- }
768-
769- // https://w3c.github.io/webappsec-subresource-integrity/#grammardef-hash-with-options
770- // https://www.w3.org/TR/CSP2/#source-list-syntax
771- // https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1
772- const parseHashWithOptions = / (?< algo > s h a 2 5 6 | s h a 3 8 4 | s h a 5 1 2 ) - ( (?< hash > [ A - Z a - z 0 - 9 + / ] + | [ A - Z a - z 0 - 9 _ - ] + ) = { 0 , 2 } (?: \s | $ ) ( + [ ! - ~ ] * ) ? ) ? / i
773-
774- /**
775- * @see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
776- * @param {string } metadata
777- */
778- function parseMetadata ( metadata ) {
779- // 1. Let result be the empty set.
780- /** @type {{ algo: string, hash: string }[] } */
781- const result = [ ]
782-
783- // 2. Let empty be equal to true.
784- let empty = true
785-
786- // 3. For each token returned by splitting metadata on spaces:
787- for ( const token of metadata . split ( ' ' ) ) {
788- // 1. Set empty to false.
789- empty = false
790-
791- // 2. Parse token as a hash-with-options.
792- const parsedToken = parseHashWithOptions . exec ( token )
793-
794- // 3. If token does not parse, continue to the next token.
795- if (
796- parsedToken === null ||
797- parsedToken . groups === undefined ||
798- parsedToken . groups . algo === undefined
799- ) {
800- // Note: Chromium blocks the request at this point, but Firefox
801- // gives a warning that an invalid integrity was given. The
802- // correct behavior is to ignore these, and subsequently not
803- // check the integrity of the resource.
804- continue
805- }
806-
807- // 4. Let algorithm be the hash-algo component of token.
808- const algorithm = parsedToken . groups . algo . toLowerCase ( )
809-
810- // 5. If algorithm is a hash function recognized by the user
811- // agent, add the parsed token to result.
812- if ( supportedHashes . includes ( algorithm ) ) {
813- result . push ( parsedToken . groups )
814- }
815- }
816-
817- // 4. Return no metadata if empty is true, otherwise return result.
818- if ( empty === true ) {
819- return 'no metadata'
820- }
821-
822- return result
823- }
824-
825- /**
826- * @param {{ algo: 'sha256' | 'sha384' | 'sha512' }[] } metadataList
827- */
828- function getStrongestMetadata ( metadataList ) {
829- // Let algorithm be the algo component of the first item in metadataList.
830- // Can be sha256
831- let algorithm = metadataList [ 0 ] . algo
832- // If the algorithm is sha512, then it is the strongest
833- // and we can return immediately
834- if ( algorithm [ 3 ] === '5' ) {
835- return algorithm
836- }
837-
838- for ( let i = 1 ; i < metadataList . length ; ++ i ) {
839- const metadata = metadataList [ i ]
840- // If the algorithm is sha512, then it is the strongest
841- // and we can break the loop immediately
842- if ( metadata . algo [ 3 ] === '5' ) {
843- algorithm = 'sha512'
844- break
845- // If the algorithm is sha384, then a potential sha256 or sha384 is ignored
846- } else if ( algorithm [ 3 ] === '3' ) {
847- continue
848- // algorithm is sha256, check if algorithm is sha384 and if so, set it as
849- // the strongest
850- } else if ( metadata . algo [ 3 ] === '3' ) {
851- algorithm = 'sha384'
852- }
853- }
854- return algorithm
855- }
856-
857- function filterMetadataListByAlgorithm ( metadataList , algorithm ) {
858- if ( metadataList . length === 1 ) {
859- return metadataList
860- }
861-
862- let pos = 0
863- for ( let i = 0 ; i < metadataList . length ; ++ i ) {
864- if ( metadataList [ i ] . algo === algorithm ) {
865- metadataList [ pos ++ ] = metadataList [ i ]
866- }
867- }
868-
869- metadataList . length = pos
870-
871- return metadataList
872- }
873-
874- /**
875- * Compares two base64 strings, allowing for base64url
876- * in the second string.
877- *
878- * @param {string } actualValue always base64
879- * @param {string } expectedValue base64 or base64url
880- * @returns {boolean }
881- */
882- function compareBase64Mixed ( actualValue , expectedValue ) {
883- if ( actualValue . length !== expectedValue . length ) {
884- return false
885- }
886- for ( let i = 0 ; i < actualValue . length ; ++ i ) {
887- if ( actualValue [ i ] !== expectedValue [ i ] ) {
888- if (
889- ( actualValue [ i ] === '+' && expectedValue [ i ] === '-' ) ||
890- ( actualValue [ i ] === '/' && expectedValue [ i ] === '_' )
891- ) {
892- continue
893- }
894- return false
895- }
896- }
897-
898- return true
899- }
900-
901687// https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-request
902688function tryUpgradeRequestToAPotentiallyTrustworthyURL ( request ) {
903689 // TODO
@@ -1761,7 +1547,6 @@ module.exports = {
17611547 isValidHeaderValue,
17621548 isErrorLike,
17631549 fullyReadBody,
1764- bytesMatch,
17651550 readableStreamClose,
17661551 isomorphicEncode,
17671552 urlIsLocal,
@@ -1770,7 +1555,6 @@ module.exports = {
17701555 readAllBytes,
17711556 simpleRangeHeaderValue,
17721557 buildContentRange,
1773- parseMetadata,
17741558 createInflate,
17751559 extractMimeType,
17761560 getDecodeSplit,
0 commit comments