everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
97 lines (95 loc) • 3.55 kB
JavaScript
import { fetchBosConfigFromFastKv } from "./fastkv.mjs";
import { createHash } from "node:crypto";
//#region src/integrity.ts
function computeSriHash(content) {
return `sha384-${createHash("sha384").update(content).digest("base64")}`;
}
async function computeSriHashForUrl(url) {
try {
const entryUrl = resolveEntryUrl(url);
const response = await fetch(entryUrl);
if (!response.ok) {
console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);
return null;
}
return computeSriHash(Buffer.from(await response.arrayBuffer()));
} catch (error) {
console.warn(`[SRI] Error computing integrity for ${url}:`, error instanceof Error ? error.message : error);
return null;
}
}
function resolveEntryUrl(url) {
if (url.endsWith("/remoteEntry.js")) return url;
if (url.endsWith("/mf-manifest.json")) return `${url.replace(/\/mf-manifest\.json$/, "")}/remoteEntry.js`;
return `${url.replace(/\/$/, "")}/remoteEntry.js`;
}
async function verifySriForUrl(url, expectedIntegrity) {
const entryUrl = resolveEntryUrl(url);
const response = await fetch(entryUrl);
if (!response.ok) {
console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);
return;
}
const computed = computeSriHash(Buffer.from(await response.arrayBuffer()));
if (computed !== expectedIntegrity) throw new Error(`[SRI] Integrity check failed for ${entryUrl}\n Expected: ${expectedIntegrity}\n Computed: ${computed}`);
}
var IntegrityRegistry = class {
hashes = /* @__PURE__ */ new Map();
register(url, integrity) {
this.hashes.set(url, integrity);
}
registerEntry(baseUrl, integrity) {
this.hashes.set(resolveEntryUrl(baseUrl), integrity);
}
get(url) {
return this.hashes.get(url);
}
has(url) {
return this.hashes.has(url);
}
entries() {
return this.hashes.entries();
}
};
function extractIntegrityHashes(config) {
const hashes = /* @__PURE__ */ new Map();
const app = config.app;
const plugins = config.plugins;
if (app) {
for (const [, entry] of Object.entries(app)) if (entry?.integrity && entry?.production) hashes.set(resolveEntryUrl(entry.production), entry.integrity);
}
if (plugins) {
for (const [, entry] of Object.entries(plugins)) if (entry?.integrity && entry?.production) hashes.set(resolveEntryUrl(entry.production), entry.integrity);
}
return hashes;
}
async function verifyConfigAgainstChain(localConfig, bosUrl) {
const mismatches = [];
let chainConfig;
try {
chainConfig = await fetchBosConfigFromFastKv(bosUrl);
} catch (error) {
console.warn(`[Attestation] Failed to fetch on-chain config: ${error instanceof Error ? error.message : String(error)}`);
return {
verified: false,
mismatches: ["chain-fetch-failed"]
};
}
const localHashes = extractIntegrityHashes(localConfig);
const chainHashes = extractIntegrityHashes(chainConfig);
for (const [url, chainHash] of chainHashes) {
const localHash = localHashes.get(url);
if (localHash && localHash !== chainHash) {
mismatches.push(url);
console.error(`[Attestation] Integrity mismatch for ${url}\n Local: ${localHash}\n Chain: ${chainHash}`);
}
}
if (mismatches.length === 0 && localHashes.size > 0) console.log(`[Attestation] Local config verified against on-chain anchor (${localHashes.size} entries checked)`);
return {
verified: mismatches.length === 0,
mismatches
};
}
//#endregion
export { IntegrityRegistry, computeSriHash, computeSriHashForUrl, resolveEntryUrl, verifyConfigAgainstChain, verifySriForUrl };
//# sourceMappingURL=integrity.mjs.map