• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2// Utility to parse the value of
3// https://w3c.github.io/webappsec-subresource-integrity/#the-integrity-attribute
4
5const {
6  ArrayPrototype,
7  ObjectDefineProperty,
8  ObjectFreeze,
9  ObjectSeal,
10  ObjectSetPrototypeOf,
11  RegExp,
12  RegExpPrototypeExec,
13  StringPrototypeSlice,
14} = primordials;
15
16const {
17  ERR_SRI_PARSE,
18} = require('internal/errors').codes;
19const kWSP = '[\\x20\\x09]';
20const kVCHAR = '[\\x21-\\x7E]';
21const kHASH_ALGO = 'sha(?:256|384|512)';
22// Base64
23const kHASH_VALUE = '[A-Za-z0-9+/]+[=]{0,2}';
24const kHASH_EXPRESSION = `(${kHASH_ALGO})-(${kHASH_VALUE})`;
25// Ungrouped since unused
26const kOPTION_EXPRESSION = `(?:${kVCHAR}*)`;
27const kHASH_WITH_OPTIONS = `${kHASH_EXPRESSION}(?:[?](${kOPTION_EXPRESSION}))?`;
28const kSRIPattern = RegExp(`(${kWSP}*)(?:${kHASH_WITH_OPTIONS})`, 'g');
29ObjectSeal(kSRIPattern);
30const kAllWSP = RegExp(`^${kWSP}*$`);
31ObjectSeal(kAllWSP);
32
33const BufferFrom = require('buffer').Buffer.from;
34
35// Returns {algorithm, value (in base64 string), options,}[]
36const parse = (str) => {
37  let prevIndex = 0;
38  let match;
39  const entries = [];
40  while ((match = RegExpPrototypeExec(kSRIPattern, str)) !== null) {
41    if (match.index !== prevIndex) {
42      throw new ERR_SRI_PARSE(str, str[prevIndex], prevIndex);
43    }
44    if (entries.length > 0 && match[1] === '') {
45      throw new ERR_SRI_PARSE(str, str[prevIndex], prevIndex);
46    }
47
48    // Avoid setters being fired
49    ObjectDefineProperty(entries, entries.length, {
50      __proto__: null,
51      enumerable: true,
52      configurable: true,
53      value: ObjectFreeze({
54        __proto__: null,
55        algorithm: match[2],
56        value: BufferFrom(match[3], 'base64'),
57        options: match[4] === undefined ? null : match[4],
58      }),
59    });
60    prevIndex += match[0].length;
61  }
62
63  if (prevIndex !== str.length) {
64    if (RegExpPrototypeExec(kAllWSP, StringPrototypeSlice(str, prevIndex)) === null) {
65      throw new ERR_SRI_PARSE(str, str[prevIndex], prevIndex);
66    }
67  }
68  return ObjectSetPrototypeOf(entries, ArrayPrototype);
69};
70
71module.exports = {
72  parse,
73};
74