4
4
ArrayIsArray,
5
5
Map,
6
6
MapPrototypeSet,
7
+ ObjectCreate,
7
8
ObjectEntries,
8
9
ObjectFreeze,
9
10
ObjectSetPrototypeOf,
@@ -29,7 +30,6 @@ const { URL } = require('internal/url');
29
30
const { createHash, timingSafeEqual } = crypto ;
30
31
const HashUpdate = uncurryThis ( crypto . Hash . prototype . update ) ;
31
32
const HashDigest = uncurryThis ( crypto . Hash . prototype . digest ) ;
32
- const BufferEquals = uncurryThis ( Buffer . prototype . equals ) ;
33
33
const BufferToString = uncurryThis ( Buffer . prototype . toString ) ;
34
34
const kRelativeURLStringPattern = / ^ \. { 0 , 2 } \/ / ;
35
35
const { getOptionValue } = require ( 'internal/options' ) ;
@@ -54,9 +54,47 @@ function REACTION_LOG(error) {
54
54
}
55
55
56
56
class 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
+ */
57
69
#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
+ */
58
79
#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
+ */
59
87
#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
+ */
60
98
constructor ( obj , manifestURL ) {
61
99
const integrities = this . #integrities;
62
100
const dependencies = this . #dependencies;
@@ -96,35 +134,14 @@ class Manifest {
96
134
let integrity = manifestEntries [ i ] [ 1 ] . integrity ;
97
135
if ( ! integrity ) integrity = null ;
98
136
if ( integrity != null ) {
99
- debug ( ` Manifest contains integrity for url ${ originalHREF } ` ) ;
137
+ debug ( ' Manifest contains integrity for url %s' , originalHREF ) ;
100
138
if ( typeof integrity === 'string' ) {
101
- const sri = ObjectFreeze ( SRI . parse ( integrity ) ) ;
102
139
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 ) {
124
141
throw new ERR_MANIFEST_INTEGRITY_MISMATCH ( resourceURL ) ;
125
142
}
126
143
}
127
- integrities . set ( resourceHREF , sri ) ;
144
+ integrities . set ( resourceHREF , integrity ) ;
128
145
} else if ( integrity === true ) {
129
146
integrities . set ( resourceHREF , true ) ;
130
147
} else {
@@ -136,7 +153,7 @@ class Manifest {
136
153
137
154
let dependencyMap = manifestEntries [ i ] [ 1 ] . dependencies ;
138
155
if ( dependencyMap === null || dependencyMap === undefined ) {
139
- dependencyMap = { } ;
156
+ dependencyMap = ObjectCreate ( null ) ;
140
157
}
141
158
if ( typeof dependencyMap === 'object' && ! ArrayIsArray ( dependencyMap ) ) {
142
159
/**
@@ -198,13 +215,18 @@ class Manifest {
198
215
199
216
assertIntegrity ( url , content ) {
200
217
const href = `${ url } ` ;
201
- debug ( ` Checking integrity of ${ href } ` ) ;
218
+ debug ( ' Checking integrity of %s' , href ) ;
202
219
const integrities = this . #integrities;
203
220
const realIntegrities = new Map ( ) ;
204
221
205
222
if ( integrities . has ( href ) ) {
206
- const integrityEntries = integrities . get ( href ) ;
223
+ let integrityEntries = integrities . get ( href ) ;
207
224
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
+ }
208
230
// Avoid clobbered Symbol.iterator
209
231
for ( let i = 0 ; i < integrityEntries . length ; i ++ ) {
210
232
const {
0 commit comments