'use strict'; // Utility to parse the value of // https://w3c.github.io/webappsec-subresource-integrity/#the-integrity-attribute const { ObjectDefineProperty, ObjectFreeze, ObjectGetPrototypeOf, ObjectSeal, ObjectSetPrototypeOf, RegExp, RegExpPrototypeExec, RegExpPrototypeTest, 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; const RealArrayPrototype = ObjectGetPrototypeOf([]); // Returns {algorithm, value (in base64 string), options,}[] const parse = (str) => { let prevIndex = 0; let match; const entries = []; while (match = RegExpPrototypeExec(kSRIPattern, str)) { if (match.index !== prevIndex) { throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex); } if (entries.length > 0 && match[1] === '') { throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex); } // Avoid setters being fired ObjectDefineProperty(entries, entries.length, { 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 (!RegExpPrototypeTest(kAllWSP, StringPrototypeSlice(str, prevIndex))) { throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex); } } return ObjectSetPrototypeOf(entries, RealArrayPrototype); }; module.exports = { parse, };