0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 07:53:06 +01:00
nodejs/lib/internal/policy/manifest.js
Jesse Katsumata afdadbcff5 policy: reduce internal usage of public util for manifest.js
PR-URL: https://github.com/nodejs/node/pull/26833
Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Masashi Hirano <shisama07@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
2019-03-23 13:25:42 +08:00

151 lines
4.6 KiB
JavaScript

'use strict';
const {
ERR_MANIFEST_ASSERT_INTEGRITY,
ERR_MANIFEST_INTEGRITY_MISMATCH,
ERR_MANIFEST_UNKNOWN_ONERROR,
} = require('internal/errors').codes;
const debug = require('internal/util/debuglog').debuglog('policy');
const SRI = require('internal/policy/sri');
const {
SafeWeakMap,
FunctionPrototype,
Object,
RegExpPrototype
} = primordials;
const crypto = require('crypto');
const { Buffer } = require('buffer');
const { URL } = require('url');
const { createHash, timingSafeEqual } = crypto;
const HashUpdate = FunctionPrototype.call.bind(crypto.Hash.prototype.update);
const HashDigest = FunctionPrototype.call.bind(crypto.Hash.prototype.digest);
const BufferEquals = FunctionPrototype.call.bind(Buffer.prototype.equals);
const BufferToString = FunctionPrototype.call.bind(Buffer.prototype.toString);
const RegExpTest = FunctionPrototype.call.bind(RegExpPrototype.test);
const { entries } = Object;
const kIntegrities = new SafeWeakMap();
const kReactions = new SafeWeakMap();
const kRelativeURLStringPattern = /^\.{0,2}\//;
const { getOptionValue } = require('internal/options');
const shouldAbortOnUncaughtException =
getOptionValue('--abort-on-uncaught-exception');
const { abort, exit, _rawDebug } = process;
function REACTION_THROW(error) {
throw error;
}
function REACTION_EXIT(error) {
REACTION_LOG(error);
if (shouldAbortOnUncaughtException) {
abort();
}
exit(1);
}
function REACTION_LOG(error) {
_rawDebug(error.stack);
}
class Manifest {
constructor(obj, manifestURL) {
const integrities = {
__proto__: null,
};
const reactions = {
__proto__: null,
integrity: REACTION_THROW,
};
if (obj.onerror) {
const behavior = obj.onerror;
if (behavior === 'throw') {
} else if (behavior === 'exit') {
reactions.integrity = REACTION_EXIT;
} else if (behavior === 'log') {
reactions.integrity = REACTION_LOG;
} else {
throw new ERR_MANIFEST_UNKNOWN_ONERROR(behavior);
}
}
kReactions.set(this, Object.freeze(reactions));
const manifestEntries = entries(obj.resources);
for (var i = 0; i < manifestEntries.length; i++) {
let url = manifestEntries[i][0];
const integrity = manifestEntries[i][1].integrity;
if (integrity != null) {
debug(`Manifest contains integrity for url ${url}`);
if (RegExpTest(kRelativeURLStringPattern, url)) {
url = new URL(url, manifestURL).href;
}
const sri = Object.freeze(SRI.parse(integrity));
if (url in integrities) {
const old = integrities[url];
let mismatch = false;
if (old.length !== sri.length) {
mismatch = true;
} else {
compare:
for (var sriI = 0; sriI < sri.length; sriI++) {
for (var oldI = 0; oldI < old.length; oldI++) {
if (sri[sriI].algorithm === old[oldI].algorithm &&
BufferEquals(sri[sriI].value, old[oldI].value) &&
sri[sriI].options === old[oldI].options) {
continue compare;
}
}
mismatch = true;
break compare;
}
}
if (mismatch) {
throw new ERR_MANIFEST_INTEGRITY_MISMATCH(url);
}
}
integrities[url] = sri;
}
}
Object.freeze(integrities);
kIntegrities.set(this, integrities);
Object.freeze(this);
}
assertIntegrity(url, content) {
debug(`Checking integrity of ${url}`);
const integrities = kIntegrities.get(this);
const realIntegrities = new Map();
if (integrities && url in integrities) {
const integrityEntries = integrities[url];
// Avoid clobbered Symbol.iterator
for (var i = 0; i < integrityEntries.length; i++) {
const {
algorithm,
value: expected
} = integrityEntries[i];
const hash = createHash(algorithm);
HashUpdate(hash, content);
const digest = HashDigest(hash);
if (digest.length === expected.length &&
timingSafeEqual(digest, expected)) {
return true;
}
realIntegrities.set(algorithm, BufferToString(digest, 'base64'));
}
}
const error = new ERR_MANIFEST_ASSERT_INTEGRITY(url, realIntegrities);
kReactions.get(this).integrity(error);
}
}
// Lock everything down to avoid problems even if reference is leaked somehow
Object.setPrototypeOf(Manifest, null);
Object.setPrototypeOf(Manifest.prototype, null);
Object.freeze(Manifest);
Object.freeze(Manifest.prototype);
module.exports = Object.freeze({ Manifest });