Projekt

Obecné

Profil

Stáhnout (18.5 KB) Statistiky
| Větev: | Revize:
1 3a515b92 cagy
# ssri [![npm version](https://img.shields.io/npm/v/ssri.svg)](https://npm.im/ssri) [![license](https://img.shields.io/npm/l/ssri.svg)](https://npm.im/ssri) [![Travis](https://img.shields.io/travis/zkat/ssri.svg)](https://travis-ci.org/zkat/ssri) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/zkat/ssri?svg=true)](https://ci.appveyor.com/project/zkat/ssri) [![Coverage Status](https://coveralls.io/repos/github/zkat/ssri/badge.svg?branch=latest)](https://coveralls.io/github/zkat/ssri?branch=latest)
2
3
[`ssri`](https://github.com/zkat/ssri), short for Standard Subresource
4
Integrity, is a Node.js utility for parsing, manipulating, serializing,
5
generating, and verifying [Subresource
6
Integrity](https://w3c.github.io/webappsec/specs/subresourceintegrity/) hashes.
7
8
## Install
9
10
`$ npm install --save ssri`
11
12
## Table of Contents
13
14
* [Example](#example)
15
* [Features](#features)
16
* [Contributing](#contributing)
17
* [API](#api)
18
  * Parsing & Serializing
19
    * [`parse`](#parse)
20
    * [`stringify`](#stringify)
21
    * [`Integrity#concat`](#integrity-concat)
22
    * [`Integrity#toString`](#integrity-to-string)
23
    * [`Integrity#toJSON`](#integrity-to-json)
24
    * [`Integrity#match`](#integrity-match)
25
    * [`Integrity#pickAlgorithm`](#integrity-pick-algorithm)
26
    * [`Integrity#hexDigest`](#integrity-hex-digest)
27
  * Integrity Generation
28
    * [`fromHex`](#from-hex)
29
    * [`fromData`](#from-data)
30
    * [`fromStream`](#from-stream)
31
    * [`create`](#create)
32
  * Integrity Verification
33
    * [`checkData`](#check-data)
34
    * [`checkStream`](#check-stream)
35
    * [`integrityStream`](#integrity-stream)
36
37
### Example
38
39
```javascript
40
const ssri = require('ssri')
41
42
const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'
43
44
// Parsing and serializing
45
const parsed = ssri.parse(integrity)
46
ssri.stringify(parsed) // === integrity (works on non-Integrity objects)
47
parsed.toString() // === integrity
48
49
// Async stream functions
50
ssri.checkStream(fs.createReadStream('./my-file'), integrity).then(...)
51
ssri.fromStream(fs.createReadStream('./my-file')).then(sri => {
52
  sri.toString() === integrity
53
})
54
fs.createReadStream('./my-file').pipe(ssri.createCheckerStream(sri))
55
56
// Sync data functions
57
ssri.fromData(fs.readFileSync('./my-file')) // === parsed
58
ssri.checkData(fs.readFileSync('./my-file'), integrity) // => 'sha512'
59
```
60
61
### Features
62
63
* Parses and stringifies SRI strings.
64
* Generates SRI strings from raw data or Streams.
65
* Strict standard compliance.
66
* `?foo` metadata option support.
67
* Multiple entries for the same algorithm.
68
* Object-based integrity hash manipulation.
69
* Small footprint: no dependencies, concise implementation.
70
* Full test coverage.
71
* Customizable algorithm picker.
72
73
### Contributing
74
75
The ssri team enthusiastically welcomes contributions and project participation!
76
There's a bunch of things you can do if you want to contribute! The [Contributor
77
Guide](CONTRIBUTING.md) has all the information you need for everything from
78
reporting bugs to contributing entire new features. Please don't hesitate to
79
jump in if you'd like to, or even ask us questions if something isn't clear.
80
81
### API
82
83
#### <a name="parse"></a> `> ssri.parse(sri, [opts]) -> Integrity`
84
85
Parses `sri` into an `Integrity` data structure. `sri` can be an integrity
86
string, an `Hash`-like with `digest` and `algorithm` fields and an optional
87
`options` field, or an `Integrity`-like object. The resulting object will be an
88
`Integrity` instance that has this shape:
89
90
```javascript
91
{
92
  'sha1': [{algorithm: 'sha1', digest: 'deadbeef', options: []}],
93
  'sha512': [
94
    {algorithm: 'sha512', digest: 'c0ffee', options: []},
95
    {algorithm: 'sha512', digest: 'bad1dea', options: ['foo']}
96
  ],
97
}
98
```
99
100
If `opts.single` is truthy, a single `Hash` object will be returned. That is, a
101
single object that looks like `{algorithm, digest, options}`, as opposed to a
102
larger object with multiple of these.
103
104
If `opts.strict` is truthy, the resulting object will be filtered such that
105
it strictly follows the Subresource Integrity spec, throwing away any entries
106
with any invalid components. This also means a restricted set of algorithms
107
will be used -- the spec limits them to `sha256`, `sha384`, and `sha512`.
108
109
Strict mode is recommended if the integrity strings are intended for use in
110
browsers, or in other situations where strict adherence to the spec is needed.
111
112
##### Example
113
114
```javascript
115
ssri.parse('sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo') // -> Integrity object
116
```
117
118
#### <a name="stringify"></a> `> ssri.stringify(sri, [opts]) -> String`
119
120
This function is identical to [`Integrity#toString()`](#integrity-to-string),
121
except it can be used on _any_ object that [`parse`](#parse) can handle -- that
122
is, a string, an `Hash`-like, or an `Integrity`-like.
123
124
The `opts.sep` option defines the string to use when joining multiple entries
125
together. To be spec-compliant, this _must_ be whitespace. The default is a
126
single space (`' '`).
127
128
If `opts.strict` is true, the integrity string will be created using strict
129
parsing rules. See [`ssri.parse`](#parse).
130
131
##### Example
132
133
```javascript
134
// Useful for cleaning up input SRI strings:
135
ssri.stringify('\n\rsha512-foo\n\t\tsha384-bar')
136
// -> 'sha512-foo sha384-bar'
137
138
// Hash-like: only a single entry.
139
ssri.stringify({
140
  algorithm: 'sha512',
141
  digest:'9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==',
142
  options: ['foo']
143
})
144
// ->
145
// 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'
146
147
// Integrity-like: full multi-entry syntax. Similar to output of `ssri.parse`
148
ssri.stringify({
149
  'sha512': [
150
    {
151
      algorithm: 'sha512',
152
      digest:'9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==',
153
      options: ['foo']
154
    }
155
  ]
156
})
157
// ->
158
// 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'
159
```
160
161
#### <a name="integrity-concat"></a> `> Integrity#concat(otherIntegrity, [opts]) -> Integrity`
162
163
Concatenates an `Integrity` object with another IntegrityLike, or an integrity
164
string.
165
166
This is functionally equivalent to concatenating the string format of both
167
integrity arguments, and calling [`ssri.parse`](#ssri-parse) on the new string.
168
169
If `opts.strict` is true, the new `Integrity` will be created using strict
170
parsing rules. See [`ssri.parse`](#parse).
171
172
##### Example
173
174
```javascript
175
// This will combine the integrity checks for two different versions of
176
// your index.js file so you can use a single integrity string and serve
177
// either of these to clients, from a single `<script>` tag.
178
const desktopIntegrity = ssri.fromData(fs.readFileSync('./index.desktop.js'))
179
const mobileIntegrity = ssri.fromData(fs.readFileSync('./index.mobile.js'))
180
181
// Note that browsers (and ssri) will succeed as long as ONE of the entries
182
// for the *prioritized* algorithm succeeds. That is, in order for this fallback
183
// to work, both desktop and mobile *must* use the same `algorithm` values.
184
desktopIntegrity.concat(mobileIntegrity)
185
```
186
187
#### <a name="integrity-to-string"></a> `> Integrity#toString([opts]) -> String`
188
189
Returns the string representation of an `Integrity` object. All hash entries
190
will be concatenated in the string by `opts.sep`, which defaults to `' '`.
191
192
If you want to serialize an object that didn't come from an `ssri` function,
193
use [`ssri.stringify()`](#stringify).
194
195
If `opts.strict` is true, the integrity string will be created using strict
196
parsing rules. See [`ssri.parse`](#parse).
197
198
##### Example
199
200
```javascript
201
const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'
202
203
ssri.parse(integrity).toString() === integrity
204
```
205
206
#### <a name="integrity-to-json"></a> `> Integrity#toJSON() -> String`
207
208
Returns the string representation of an `Integrity` object. All hash entries
209
will be concatenated in the string by `' '`.
210
211
This is a convenience method so you can pass an `Integrity` object directly to `JSON.stringify`.
212
For more info check out [toJSON() behavior on mdn](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON%28%29_behavior).
213
214
##### Example
215
216
```javascript
217
const integrity = '"sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo"'
218
219
JSON.stringify(ssri.parse(integrity)) === integrity
220
```
221
222
#### <a name="integrity-match"></a> `> Integrity#match(sri, [opts]) -> Hash | false`
223
224
Returns the matching (truthy) hash if `Integrity` matches the argument passed as
225
`sri`, which can be anything that [`parse`](#parse) will accept. `opts` will be
226
passed through to `parse` and [`pickAlgorithm()`](#integrity-pick-algorithm).
227
228
##### Example
229
230
```javascript
231
const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A=='
232
233
ssri.parse(integrity).match(integrity)
234
// Hash {
235
//   digest: '9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A=='
236
//   algorithm: 'sha512'
237
// }
238
239
ssri.parse(integrity).match('sha1-deadbeef')
240
// false
241
```
242
243
#### <a name="integrity-pick-algorithm"></a> `> Integrity#pickAlgorithm([opts]) -> String`
244
245
Returns the "best" algorithm from those available in the integrity object.
246
247
If `opts.pickAlgorithm` is provided, it will be passed two algorithms as
248
arguments. ssri will prioritize whichever of the two algorithms is returned by
249
this function. Note that the function may be called multiple times, and it
250
**must** return one of the two algorithms provided. By default, ssri will make
251
a best-effort to pick the strongest/most reliable of the given algorithms. It
252
may intentionally deprioritize algorithms with known vulnerabilities.
253
254
##### Example
255
256
```javascript
257
ssri.parse('sha1-WEakDigEST sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1').pickAlgorithm() // sha512
258
```
259
260
#### <a name="integrity-hex-digest"></a> `> Integrity#hexDigest() -> String`
261
262
`Integrity` is assumed to be either a single-hash `Integrity` instance, or a
263
`Hash` instance. Returns its `digest`, converted to a hex representation of the
264
base64 data.
265
266
##### Example
267
268
```javascript
269
ssri.parse('sha1-deadbeef').hexDigest() // '75e69d6de79f'
270
```
271
272
#### <a name="from-hex"></a> `> ssri.fromHex(hexDigest, algorithm, [opts]) -> Integrity`
273
274
Creates an `Integrity` object with a single entry, based on a hex-formatted
275
hash. This is a utility function to help convert existing shasums to the
276
Integrity format, and is roughly equivalent to something like:
277
278
```javascript
279
algorithm + '-' + Buffer.from(hexDigest, 'hex').toString('base64')
280
```
281
282
`opts.options` may optionally be passed in: it must be an array of option
283
strings that will be added to all generated integrity hashes generated by
284
`fromData`. This is a loosely-specified feature of SRIs, and currently has no
285
specified semantics besides being `?`-separated. Use at your own risk, and
286
probably avoid if your integrity strings are meant to be used with browsers.
287
288
If `opts.strict` is true, the integrity object will be created using strict
289
parsing rules. See [`ssri.parse`](#parse).
290
291
If `opts.single` is true, a single `Hash` object will be returned.
292
293
##### Example
294
295
```javascript
296
ssri.fromHex('75e69d6de79f', 'sha1').toString() // 'sha1-deadbeef'
297
```
298
299
#### <a name="from-data"></a> `> ssri.fromData(data, [opts]) -> Integrity`
300
301
Creates an `Integrity` object from either string or `Buffer` data, calculating
302
all the requested hashes and adding any specified options to the object.
303
304
`opts.algorithms` determines which algorithms to generate hashes for. All
305
results will be included in a single `Integrity` object. The default value for
306
`opts.algorithms` is `['sha512']`. All algorithm strings must be hashes listed
307
in `crypto.getHashes()` for the host Node.js platform.
308
309
`opts.options` may optionally be passed in: it must be an array of option
310
strings that will be added to all generated integrity hashes generated by
311
`fromData`. This is a loosely-specified feature of SRIs, and currently has no
312
specified semantics besides being `?`-separated. Use at your own risk, and
313
probably avoid if your integrity strings are meant to be used with browsers.
314
315
If `opts.strict` is true, the integrity object will be created using strict
316
parsing rules. See [`ssri.parse`](#parse).
317
318
##### Example
319
320
```javascript
321
const integrityObj = ssri.fromData('foobarbaz', {
322
  algorithms: ['sha256', 'sha384', 'sha512']
323
})
324
integrity.toString('\n')
325
// ->
326
// sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0=
327
// sha384-irnCxQ0CfQhYGlVAUdwTPC9bF3+YWLxlaDGM4xbYminxpbXEq+D+2GCEBTxcjES9
328
// sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg==
329
```
330
331
#### <a name="from-stream"></a> `> ssri.fromStream(stream, [opts]) -> Promise<Integrity>`
332
333
Returns a Promise of an Integrity object calculated by reading data from
334
a given `stream`.
335
336
It accepts both `opts.algorithms` and `opts.options`, which are documented as
337
part of [`ssri.fromData`](#from-data).
338
339
Additionally, `opts.Promise` may be passed in to inject a Promise library of
340
choice. By default, ssri will use Node's built-in Promises.
341
342
If `opts.strict` is true, the integrity object will be created using strict
343
parsing rules. See [`ssri.parse`](#parse).
344
345
##### Example
346
347
```javascript
348
ssri.fromStream(fs.createReadStream('index.js'), {
349
  algorithms: ['sha1', 'sha512']
350
}).then(integrity => {
351
  return ssri.checkStream(fs.createReadStream('index.js'), integrity)
352
}) // succeeds
353
```
354
355
#### <a name="create"></a> `> ssri.create([opts]) -> <Hash>`
356
357
Returns a Hash object with `update(<Buffer or string>[,enc])` and `digest()` methods.
358
359
360
The Hash object provides the same methods as [crypto class Hash](https://nodejs.org/dist/latest-v6.x/docs/api/crypto.html#crypto_class_hash).
361
`digest()` accepts no arguments and returns an Integrity object calculated by reading data from
362
calls to update.
363
364
It accepts both `opts.algorithms` and `opts.options`, which are documented as
365
part of [`ssri.fromData`](#from-data).
366
367
If `opts.strict` is true, the integrity object will be created using strict
368
parsing rules. See [`ssri.parse`](#parse).
369
370
##### Example
371
372
```javascript
373
const integrity = ssri.create().update('foobarbaz').digest()
374
integrity.toString()
375
// ->
376
// sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg==
377
```
378
379
#### <a name="check-data"></a> `> ssri.checkData(data, sri, [opts]) -> Hash|false`
380
381
Verifies `data` integrity against an `sri` argument. `data` may be either a
382
`String` or a `Buffer`, and `sri` can be any subresource integrity
383
representation that [`ssri.parse`](#parse) can handle.
384
385
If verification succeeds, `checkData` will return the name of the algorithm that
386
was used for verification (a truthy value). Otherwise, it will return `false`.
387
388
If `opts.pickAlgorithm` is provided, it will be used by
389
[`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of
390
the available digests to match against.
391
392
If `opts.error` is true, and verification fails, `checkData` will throw either
393
an `EBADSIZE` or an `EINTEGRITY` error, instead of just returning false.
394
395
##### Example
396
397
```javascript
398
const data = fs.readFileSync('index.js')
399
ssri.checkData(data, ssri.fromData(data)) // -> 'sha512'
400
ssri.checkData(data, 'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0')
401
ssri.checkData(data, 'sha1-BaDDigEST') // -> false
402
ssri.checkData(data, 'sha1-BaDDigEST', {error: true}) // -> Error! EINTEGRITY
403
```
404
405
#### <a name="check-stream"></a> `> ssri.checkStream(stream, sri, [opts]) -> Promise<Hash>`
406
407
Verifies the contents of `stream` against an `sri` argument. `stream` will be
408
consumed in its entirety by this process. `sri` can be any subresource integrity
409
representation that [`ssri.parse`](#parse) can handle.
410
411
`checkStream` will return a Promise that either resolves to the
412
`Hash` that succeeded verification, or, if the verification fails
413
or an error happens with `stream`, the Promise will be rejected.
414
415
If the Promise is rejected because verification failed, the returned error will
416
have `err.code` as `EINTEGRITY`.
417
418
If `opts.size` is given, it will be matched against the stream size. An error
419
with `err.code` `EBADSIZE` will be returned by a rejection if the expected size
420
and actual size fail to match.
421
422
If `opts.pickAlgorithm` is provided, it will be used by
423
[`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of
424
the available digests to match against.
425
426
##### Example
427
428
```javascript
429
const integrity = ssri.fromData(fs.readFileSync('index.js'))
430
431
ssri.checkStream(
432
  fs.createReadStream('index.js'),
433
  integrity
434
)
435
// ->
436
// Promise<{
437
//   algorithm: 'sha512',
438
//   digest: 'sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1'
439
// }>
440
441
ssri.checkStream(
442
  fs.createReadStream('index.js'),
443
  'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0'
444
) // -> Promise<Hash>
445
446
ssri.checkStream(
447
  fs.createReadStream('index.js'),
448
  'sha1-BaDDigEST'
449
) // -> Promise<Error<{code: 'EINTEGRITY'}>>
450
```
451
452
#### <a name="integrity-stream"></a> `> integrityStream([opts]) -> IntegrityStream`
453
454
Returns a `Transform` stream that data can be piped through in order to generate
455
and optionally check data integrity for piped data. When the stream completes
456
successfully, it emits `size` and `integrity` events, containing the total
457
number of bytes processed and a calculated `Integrity` instance based on stream
458
data, respectively.
459
460
If `opts.algorithms` is passed in, the listed algorithms will be calculated when
461
generating the final `Integrity` instance. The default is `['sha512']`.
462
463
If `opts.single` is passed in, a single `Hash` instance will be returned.
464
465
If `opts.integrity` is passed in, it should be an `integrity` value understood
466
by [`parse`](#parse) that the stream will check the data against. If
467
verification succeeds, the integrity stream will emit a `verified` event whose
468
value is a single `Hash` object that is the one that succeeded verification. If
469
verification fails, the stream will error with an `EINTEGRITY` error code.
470
471
If `opts.size` is given, it will be matched against the stream size. An error
472
with `err.code` `EBADSIZE` will be emitted by the stream if the expected size
473
and actual size fail to match.
474
475
If `opts.pickAlgorithm` is provided, it will be passed two algorithms as
476
arguments. ssri will prioritize whichever of the two algorithms is returned by
477
this function. Note that the function may be called multiple times, and it
478
**must** return one of the two algorithms provided. By default, ssri will make
479
a best-effort to pick the strongest/most reliable of the given algorithms. It
480
may intentionally deprioritize algorithms with known vulnerabilities.
481
482
##### Example
483
484
```javascript
485
const integrity = ssri.fromData(fs.readFileSync('index.js'))
486
fs.createReadStream('index.js')
487
.pipe(ssri.integrityStream({integrity}))
488
```