UNPKG

webpack-subresource-integrity

Version:
141 lines 5.75 kB
/** * Copyright (c) 2015-present, Waysact Pty Ltd * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { addIfNotExist, map, flatMap, intersect, intersectSets, unionSet, allChunksInChunkIterable, allChunksInPrimaryChunkIterable, sriHashVariableReference, } from "./util"; import { createDAGfromGraph } from "./scc"; import { RuntimeModule, Template } from "webpack"; // This implementation assumes a directed acyclic graph (such as one produced by createDAGfromGraph), // and does not attempt to detect cycles function topologicalSort({ vertices, edges }) { const sortedItems = []; const seenNodes = new Set(); function visit(node) { if (addIfNotExist(seenNodes, node)) { return; } (edges.get(node) ?? []).forEach(visit); sortedItems.push(node); } vertices.forEach(visit); return sortedItems; } function buildTopologicallySortedChunkGraph(chunks) { const vertices = new Set(); const edges = new Map(); // Chunks should have *all* chunks, not simply entry chunks for (const vertex of chunks) { if (addIfNotExist(vertices, vertex)) { continue; } edges.set(vertex, new Set()); for (const childChunk of allChunksInChunkIterable(vertex)) { edges.get(vertex)?.add(childChunk); } } const dag = createDAGfromGraph({ vertices, edges }); const sortedVertices = topologicalSort(dag); const chunkToSccMap = new Map(flatMap(dag.vertices, (scc) => map(scc.nodes, (chunk) => [chunk, scc]))); return [sortedVertices, dag, chunkToSccMap]; } class ChunkToManifestMapBuilder { sortedVertices; chunkToSccMap; manifest; // This map tracks which hashes a chunk group has in its manifest and the intersection // of all its parents (and intersection of all their parents, etc.) // This is meant as a guarantee that the hash for a given chunk is handled by a chunk group // or its parents regardless of the tree traversal used from the roots hashesByChunkGroupAndParents = new Map(); constructor(chunks) { const [sortedVertices, , chunkToSccMap] = buildTopologicallySortedChunkGraph(chunks); this.sortedVertices = sortedVertices; this.chunkToSccMap = chunkToSccMap; this.manifest = this.createManifest(); } build() { return [this.sortedVertices, this.manifest]; } createManifest() { // A map of what child chunks a given chunk should contain hashes for // We want to walk from the root nodes down to the leaves return this.sortedVertices.reduceRight((manifest, vertex) => { for (const chunk of vertex.nodes) { manifest.set(chunk, this.createChunkManifest(chunk)); } return manifest; }, new Map()); } createChunkManifest(chunk) { const manifest = this.getChildChunksToAddToChunkManifest(chunk); for (const manifestEntry of this.findIntersectionOfParentSets(chunk)) { manifest.delete(manifestEntry); } const combinedParentManifest = this.findIntersectionOfParentSets(chunk); for (const chunk of manifest) { if (combinedParentManifest.has(chunk)) { manifest.delete(chunk); } else { combinedParentManifest.add(chunk); } } this.addGroupCombinedManifest(chunk, manifest); return manifest; } addGroupCombinedManifest(chunk, manifest) { for (const group of chunk.groupsIterable) { this.hashesByChunkGroupAndParents.set(group, unionSet( // Intersection of all parent manifests intersect(map(group.parentsIterable, (parent) => this.hashesByChunkGroupAndParents.get(parent) ?? new Set())), // Add this chunk's manifest manifest, // Add any existing manifests part of the group this.hashesByChunkGroupAndParents.get(group) ?? new Set())); } } findIntersectionOfParentSets(chunk) { const setsToIntersect = []; for (const group of chunk.groupsIterable) { for (const parent of group.parentsIterable) { setsToIntersect.push(this.hashesByChunkGroupAndParents.get(parent) ?? new Set()); } } return intersectSets(setsToIntersect); } getChildChunksToAddToChunkManifest(chunk) { const childChunks = new Set(); const chunkSCC = this.chunkToSccMap.get(chunk); for (const childChunk of allChunksInPrimaryChunkIterable(chunk)) { const childChunkSCC = this.chunkToSccMap.get(childChunk); if (childChunkSCC === chunkSCC) { // Don't include your own SCC. // Your parent will have the hashes for your SCC siblings continue; } for (const childChunkSccNode of childChunkSCC?.nodes ?? []) { childChunks.add(childChunkSccNode); } } return childChunks; } } export function getChunkToManifestMap(chunks) { return new ChunkToManifestMapBuilder(chunks).build(); } export class AddLazySriRuntimeModule extends RuntimeModule { sriHashes; constructor(sriHashes, chunkName) { super(`webpack-subresource-integrity lazy hashes for direct children of chunk ${chunkName}`); this.sriHashes = sriHashes; } generate() { return Template.asString([ `Object.assign(${sriHashVariableReference}, ${JSON.stringify(this.sriHashes)});`, ]); } } //# sourceMappingURL=manifest.js.map