mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
64 lines
2.6 KiB
JavaScript
64 lines
2.6 KiB
JavaScript
#!/usr/bin/env node
|
|
// SRI assertion (mirrors the legacy intent of integrity-hashes.cjs).
|
|
//
|
|
// After `vite build`, re-hash every emitted JS/CSS asset and assert it matches
|
|
// the integrity recorded in `dist/sri-manifest.json` AND the `<script
|
|
// type="importmap">` embedded in `dist/index.html`. Also assert no JS chunk is
|
|
// left uncovered. Exits non-zero (failing the build) on any mismatch or gap.
|
|
|
|
import { createHash } from 'node:crypto'
|
|
import { readFileSync, existsSync, readdirSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
const distDir = path.resolve(process.argv[2] ?? 'dist')
|
|
const manifestPath = path.join(distDir, 'sri-manifest.json')
|
|
const indexPath = path.join(distDir, 'index.html')
|
|
|
|
const fail = (message) => {
|
|
console.error(`[SRI] ${message}`)
|
|
process.exit(1)
|
|
}
|
|
|
|
if (!existsSync(manifestPath)) fail(`Missing ${manifestPath}. Did the import-map-integrity plugin run?`)
|
|
if (!existsSync(indexPath)) fail(`Missing ${indexPath}.`)
|
|
|
|
const sri = (buf) => `sha384-${createHash('sha384').update(buf).digest('base64')}`
|
|
|
|
const { integrity } = JSON.parse(readFileSync(manifestPath, 'utf-8'))
|
|
const entries = Object.entries(integrity)
|
|
if (entries.length === 0) fail('sri-manifest.json has no integrity entries.')
|
|
|
|
// 1. Re-hash every file referenced by the manifest and assert it matches.
|
|
let checked = 0
|
|
for (const [url, expected] of entries) {
|
|
const filePath = path.join(distDir, url.replace(/^\//, ''))
|
|
if (!existsSync(filePath)) fail(`Manifest references missing file: ${url}`)
|
|
const actual = sri(readFileSync(filePath))
|
|
if (actual !== expected) fail(`Integrity mismatch for ${url}\n expected ${expected}\n actual ${actual}`)
|
|
checked++
|
|
}
|
|
|
|
// 2. Assert no JS chunk in dist/assets is left uncovered (no silent gaps).
|
|
const assetsDir = path.join(distDir, 'assets')
|
|
if (existsSync(assetsDir)) {
|
|
const uncovered = readdirSync(assetsDir)
|
|
.filter((f) => f.endsWith('.js'))
|
|
.map((f) => `/assets/${f}`)
|
|
.filter((url) => !(url in integrity))
|
|
if (uncovered.length > 0) fail(`JS chunks missing from SRI manifest:\n ${uncovered.join('\n ')}`)
|
|
}
|
|
|
|
// 3. Assert the embedded import map agrees with the manifest (JS entries).
|
|
const html = readFileSync(indexPath, 'utf-8')
|
|
const importMapMatch = html.match(/<script type="importmap">(.*?)<\/script>/s)
|
|
if (!importMapMatch) fail('No <script type="importmap"> found in index.html.')
|
|
const importMap = JSON.parse(importMapMatch[1])
|
|
for (const [url, expected] of entries) {
|
|
if (!url.endsWith('.js')) continue
|
|
if (importMap.integrity?.[url] !== expected) {
|
|
fail(`Import map integrity for ${url} disagrees with sri-manifest.json.`)
|
|
}
|
|
}
|
|
|
|
console.log(`[SRI] OK — ${checked} assets verified, import map consistent.`)
|