Subresource integrity ftw!

This commit is contained in:
Starbeamrainbowlabs 2022-02-23 20:45:49 +00:00
parent b1936c31af
commit 5ee34ce082
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
3 changed files with 44 additions and 8 deletions

View file

@ -1,5 +1,3 @@
import __INDEX__ from "./index.html";
import make_router from './js/routes_client.mjs'; import make_router from './js/routes_client.mjs';
window.addEventListener("load", (_event) => { window.addEventListener("load", (_event) => {

View file

@ -1,21 +1,45 @@
#!/usr/bin/env node #!/usr/bin/env node
"use strict"; "use strict";
import fs from 'fs';
import path from 'path'; import path from 'path';
import crypto from 'crypto';
import esbuild from 'esbuild'; import esbuild from 'esbuild';
const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf("/")); const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf("/"));
/**
* Hashes the contents of a file.
* @ref https://stackoverflow.com/a/44643479/1460422
* @param {string} hashName The name of the hash algorithm to use.
* @param {string} path The path to the file to hash.
* @return {string} The resulting hash as a hexadecimal string.
*/
function hash_file(hashName, path) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash(hashName);
const stream = fs.createReadStream(path);
stream.once("error", reject);
stream.on("data", chunk => hash.update(chunk));
stream.once("end", () => {
stream.off("error", reject);
resolve(hash.digest('base64'));
});
});
}
(async () => { (async () => {
"use strict"; "use strict";
const outdir = path.resolve(__dirname, "../static-dist");
const result = await esbuild.build({ const result = await esbuild.build({
entryPoints: [ entryPoints: [
"./app.mjs", "./app.mjs",
"./app.css" "./app.css"
].map(filepath => path.resolve(__dirname, filepath)), ].map(filepath => path.resolve(__dirname, filepath)),
outdir: path.resolve(__dirname, "../static-dist"), outdir,
bundle: true, bundle: true,
minify: true, minify: true,
sourcemap: true, sourcemap: true,
@ -28,9 +52,23 @@ const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf("/"));
".ttf": "file" ".ttf": "file"
} }
}); });
console.log(result); if(result.errors.length > 0 || result.warnings.length > 0)
console.log(result);
// We *would* use SHA3 here, but we need to use SHA2 for subresource integrity
let algorithm = "sha256";
let css_hash = await hash_file(algorithm, path.join(outdir, "app.css"));
let js_hash = await hash_file(algorithm, path.join(outdir, "app.js"));
let html = (await fs.promises.readFile(path.join(__dirname, "index.html"), "utf-8"))
.replace(/\{JS_HASH\}/g, js_hash)
.replace(/\{CSS_HASH\}/g, css_hash)
.replace(/\{JS_HASH_SHORT\}/g, js_hash.substring(0, 7).replace(/[+/=]/, ""))
.replace(/\{CSS_HASH_SHORT\}/g, css_hash.substring(0, 7).replace(/[+/=]/, ""))
.replace(/\{ALGO\}/g, algorithm);
// TODO: Use fs.promises.copyFile() for index.html here, and also maybe find & replace for css/js filenames that we can then randomise? // TODO: Use fs.promises.copyFile() for index.html here, and also maybe find & replace for css/js filenames that we can then randomise?
await fs.promises.writeFile(path.join(outdir, "index.html"), html);
// console.log(await esbuild.analyzeMetafile(result.metafile)); // console.log(await esbuild.analyzeMetafile(result.metafile));
})(); })();

View file

@ -18,7 +18,7 @@
</nav> </nav>
<!----------------> <!---------------->
<link rel="stylesheet" href="/static/app.css" /> <link rel="stylesheet" href="/static/app.css?hash={CSS_HASH_SHORT}" integrity="{ALGO}-{CSS_HASH}" />
<script src="/static/app.js" charset="utf-8"></script> <script src="/static/app.js?hash={JS_HASH_SHORT}" integrity="{ALGO}-{JS_HASH}" charset="utf-8"></script>
</body> </body>
</html> </html>