4
4
ArrayIsArray,
5
5
Map,
6
6
MapPrototypeSet,
7
+ ObjectCreate,
7
8
ObjectEntries,
8
9
ObjectFreeze,
9
10
ObjectSetPrototypeOf,
@@ -31,7 +32,6 @@ const { URL } = require('internal/url');
31
32
const { createHash, timingSafeEqual } = crypto ;
32
33
const HashUpdate = uncurryThis ( crypto . Hash . prototype . update ) ;
33
34
const HashDigest = uncurryThis ( crypto . Hash . prototype . digest ) ;
34
- const BufferEquals = uncurryThis ( Buffer . prototype . equals ) ;
35
35
const BufferToString = uncurryThis ( Buffer . prototype . toString ) ;
36
36
const kRelativeURLStringPattern = / ^ \. { 0 , 2 } \/ / ;
37
37
const { getOptionValue } = require ( 'internal/options' ) ;
@@ -56,9 +56,47 @@ function REACTION_LOG(error) {
56
56
}
57
57
58
58
class Manifest {
59
+ /**
60
+ * @type {Map<string, true | string | SRI[]> }
61
+ *
62
+ * Used to compare a resource to the content body at the resource.
63
+ * `true` is used to signify that all integrities are allowed, otherwise,
64
+ * SRI strings are parsed to compare with the body.
65
+ *
66
+ * This stores strings instead of eagerly parsing SRI strings
67
+ * and only converts them to SRI data structures when needed.
68
+ * This avoids needing to parse all SRI strings at startup even
69
+ * if some never end up being used.
70
+ */
59
71
#integrities = new SafeMap ( ) ;
72
+ /**
73
+ * @type {Map<string, (specifier: string) => true | URL> }
74
+ *
75
+ * Used to find where a dependency is located.
76
+ *
77
+ * This stores functions to lazily calculate locations as needed.
78
+ * `true` is used to signify that the location is not specified
79
+ * by the manifest and default resolution should be allowed.
80
+ */
60
81
#dependencies = new SafeMap ( ) ;
82
+ /**
83
+ * @type {(err: Error) => void }
84
+ *
85
+ * Performs default action for what happens when a manifest encounters
86
+ * a violation such as abort()ing or exiting the process, throwing the error,
87
+ * or logging the error.
88
+ */
61
89
#reaction = null ;
90
+ /**
91
+ * `obj` should match the policy file format described in the docs
92
+ * it is expected to not have prototype pollution issues either by reassigning
93
+ * the prototype to `null` for values or by running prior to any user code.
94
+ *
95
+ * `manifestURL` is a URL to resolve relative locations against.
96
+ *
97
+ * @param {object } obj
98
+ * @param {string } manifestURL
99
+ */
62
100
constructor ( obj , manifestURL ) {
63
101
const integrities = this . #integrities;
64
102
const dependencies = this . #dependencies;
@@ -98,35 +136,14 @@ class Manifest {
98
136
let integrity = manifestEntries [ i ] [ 1 ] . integrity ;
99
137
if ( ! integrity ) integrity = null ;
100
138
if ( integrity != null ) {
101
- debug ( ` Manifest contains integrity for url ${ originalHREF } ` ) ;
139
+ debug ( ' Manifest contains integrity for url %s' , originalHREF ) ;
102
140
if ( typeof integrity === 'string' ) {
103
- const sri = ObjectFreeze ( SRI . parse ( integrity ) ) ;
104
141
if ( integrities . has ( resourceHREF ) ) {
105
- const old = integrities . get ( resourceHREF ) ;
106
- let mismatch = false ;
107
-
108
- if ( old . length !== sri . length ) {
109
- mismatch = true ;
110
- } else {
111
- compare:
112
- for ( let sriI = 0 ; sriI < sri . length ; sriI ++ ) {
113
- for ( let oldI = 0 ; oldI < old . length ; oldI ++ ) {
114
- if ( sri [ sriI ] . algorithm === old [ oldI ] . algorithm &&
115
- BufferEquals ( sri [ sriI ] . value , old [ oldI ] . value ) &&
116
- sri [ sriI ] . options === old [ oldI ] . options ) {
117
- continue compare;
118
- }
119
- }
120
- mismatch = true ;
121
- break compare;
122
- }
123
- }
124
-
125
- if ( mismatch ) {
142
+ if ( integrities . get ( resourceHREF ) !== integrity ) {
126
143
throw new ERR_MANIFEST_INTEGRITY_MISMATCH ( resourceURL ) ;
127
144
}
128
145
}
129
- integrities . set ( resourceHREF , sri ) ;
146
+ integrities . set ( resourceHREF , integrity ) ;
130
147
} else if ( integrity === true ) {
131
148
integrities . set ( resourceHREF , true ) ;
132
149
} else {
@@ -138,7 +155,7 @@ class Manifest {
138
155
139
156
let dependencyMap = manifestEntries [ i ] [ 1 ] . dependencies ;
140
157
if ( dependencyMap === null || dependencyMap === undefined ) {
141
- dependencyMap = { } ;
158
+ dependencyMap = ObjectCreate ( null ) ;
142
159
}
143
160
if ( typeof dependencyMap === 'object' && ! ArrayIsArray ( dependencyMap ) ) {
144
161
/**
@@ -200,13 +217,18 @@ class Manifest {
200
217
201
218
assertIntegrity ( url , content ) {
202
219
const href = `${ url } ` ;
203
- debug ( ` Checking integrity of ${ href } ` ) ;
220
+ debug ( ' Checking integrity of %s' , href ) ;
204
221
const integrities = this . #integrities;
205
222
const realIntegrities = new Map ( ) ;
206
223
207
224
if ( integrities . has ( href ) ) {
208
- const integrityEntries = integrities . get ( href ) ;
225
+ let integrityEntries = integrities . get ( href ) ;
209
226
if ( integrityEntries === true ) return true ;
227
+ if ( typeof integrityEntries === 'string' ) {
228
+ const sri = ObjectFreeze ( SRI . parse ( integrityEntries ) ) ;
229
+ integrities . set ( href , sri ) ;
230
+ integrityEntries = sri ;
231
+ }
210
232
// Avoid clobbered Symbol.iterator
211
233
for ( let i = 0 ; i < integrityEntries . length ; i ++ ) {
212
234
const {
0 commit comments