All files / lib/internal/policy sri.js

100% Statements 73/73
92.3% Branches 12/13
100% Functions 1/1
100% Lines 73/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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