Projekt

Obecné

Profil

Stáhnout (18.5 KB) Statistiky
| Větev: | Revize:
1
# 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
```
(3-3/5)