44 ArrayIsArray,
55 Map,
66 MapPrototypeSet,
7+ ObjectCreate,
78 ObjectEntries,
89 ObjectFreeze,
910 ObjectSetPrototypeOf,
@@ -29,7 +30,6 @@ const { URL } = require('internal/url');
2930const { createHash, timingSafeEqual } = crypto ;
3031const HashUpdate = uncurryThis ( crypto . Hash . prototype . update ) ;
3132const HashDigest = uncurryThis ( crypto . Hash . prototype . digest ) ;
32- const BufferEquals = uncurryThis ( Buffer . prototype . equals ) ;
3333const BufferToString = uncurryThis ( Buffer . prototype . toString ) ;
3434const kRelativeURLStringPattern = / ^ \. { 0 , 2 } \/ / ;
3535const { getOptionValue } = require ( 'internal/options' ) ;
@@ -54,9 +54,47 @@ function REACTION_LOG(error) {
5454}
5555
5656class Manifest {
57+ /**
58+ * @type {Map<string, true | string | SRI[]> }
59+ *
60+ * Used to compare a resource to the content body at the resource.
61+ * `true` is used to signify that all integrities are allowed, otherwise,
62+ * SRI strings are parsed to compare with the body.
63+ *
64+ * This stores strings instead of eagerly parsing SRI strings
65+ * and only converts them to SRI data structures when needed.
66+ * This avoids needing to parse all SRI strings at startup even
67+ * if some never end up being used.
68+ */
5769 #integrities = new SafeMap ( ) ;
70+ /**
71+ * @type {Map<string, (specifier: string) => true | URL> }
72+ *
73+ * Used to find where a dependency is located.
74+ *
75+ * This stores functions to lazily calculate locations as needed.
76+ * `true` is used to signify that the location is not specified
77+ * by the manifest and default resolution should be allowed.
78+ */
5879 #dependencies = new SafeMap ( ) ;
80+ /**
81+ * @type {(err: Error) => void }
82+ *
83+ * Performs default action for what happens when a manifest encounters
84+ * a violation such as abort()ing or exiting the process, throwing the error,
85+ * or logging the error.
86+ */
5987 #reaction = null ;
88+ /**
89+ * `obj` should match the policy file format described in the docs
90+ * it is expected to not have prototype pollution issues either by reassigning
91+ * the prototype to `null` for values or by running prior to any user code.
92+ *
93+ * `manifestURL` is a URL to resolve relative locations against.
94+ *
95+ * @param {object } obj
96+ * @param {string } manifestURL
97+ */
6098 constructor ( obj , manifestURL ) {
6199 const integrities = this . #integrities;
62100 const dependencies = this . #dependencies;
@@ -96,35 +134,14 @@ class Manifest {
96134 let integrity = manifestEntries [ i ] [ 1 ] . integrity ;
97135 if ( ! integrity ) integrity = null ;
98136 if ( integrity != null ) {
99- debug ( ` Manifest contains integrity for url ${ originalHREF } ` ) ;
137+ debug ( ' Manifest contains integrity for url %s' , originalHREF ) ;
100138 if ( typeof integrity === 'string' ) {
101- const sri = ObjectFreeze ( SRI . parse ( integrity ) ) ;
102139 if ( integrities . has ( resourceHREF ) ) {
103- const old = integrities . get ( resourceHREF ) ;
104- let mismatch = false ;
105-
106- if ( old . length !== sri . length ) {
107- mismatch = true ;
108- } else {
109- compare:
110- for ( let sriI = 0 ; sriI < sri . length ; sriI ++ ) {
111- for ( let oldI = 0 ; oldI < old . length ; oldI ++ ) {
112- if ( sri [ sriI ] . algorithm === old [ oldI ] . algorithm &&
113- BufferEquals ( sri [ sriI ] . value , old [ oldI ] . value ) &&
114- sri [ sriI ] . options === old [ oldI ] . options ) {
115- continue compare;
116- }
117- }
118- mismatch = true ;
119- break compare;
120- }
121- }
122-
123- if ( mismatch ) {
140+ if ( integrities . get ( resourceHREF ) !== integrity ) {
124141 throw new ERR_MANIFEST_INTEGRITY_MISMATCH ( resourceURL ) ;
125142 }
126143 }
127- integrities . set ( resourceHREF , sri ) ;
144+ integrities . set ( resourceHREF , integrity ) ;
128145 } else if ( integrity === true ) {
129146 integrities . set ( resourceHREF , true ) ;
130147 } else {
@@ -136,7 +153,7 @@ class Manifest {
136153
137154 let dependencyMap = manifestEntries [ i ] [ 1 ] . dependencies ;
138155 if ( dependencyMap === null || dependencyMap === undefined ) {
139- dependencyMap = { } ;
156+ dependencyMap = ObjectCreate ( null ) ;
140157 }
141158 if ( typeof dependencyMap === 'object' && ! ArrayIsArray ( dependencyMap ) ) {
142159 /**
@@ -198,13 +215,18 @@ class Manifest {
198215
199216 assertIntegrity ( url , content ) {
200217 const href = `${ url } ` ;
201- debug ( ` Checking integrity of ${ href } ` ) ;
218+ debug ( ' Checking integrity of %s' , href ) ;
202219 const integrities = this . #integrities;
203220 const realIntegrities = new Map ( ) ;
204221
205222 if ( integrities . has ( href ) ) {
206- const integrityEntries = integrities . get ( href ) ;
223+ let integrityEntries = integrities . get ( href ) ;
207224 if ( integrityEntries === true ) return true ;
225+ if ( typeof integrityEntries === 'string' ) {
226+ const sri = ObjectFreeze ( SRI . parse ( integrityEntries ) ) ;
227+ integrities . set ( href , sri ) ;
228+ integrityEntries = sri ;
229+ }
208230 // Avoid clobbered Symbol.iterator
209231 for ( let i = 0 ; i < integrityEntries . length ; i ++ ) {
210232 const {
0 commit comments