From 363e8cf44f683c2fe7172a2e3825848a3758af6a Mon Sep 17 00:00:00 2001 From: RedRackham Date: Tue, 7 May 2024 13:00:28 +0100 Subject: [PATCH] sri (#5) * sri * 2024.5.2 --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- index.html | 20 +- package-lock.json | 291 ++++++++++++++++++++++++++- package.json | 4 +- public/assets/hello-dev-wc-footer.js | 269 +++++++++++++++++++++++++ public/assets/quickstart-glob.js | 12 ++ sri.js | 71 +++++++ vite.config.js | 9 +- 7 files changed, 656 insertions(+), 20 deletions(-) create mode 100644 public/assets/hello-dev-wc-footer.js create mode 100644 public/assets/quickstart-glob.js create mode 100644 sri.js diff --git a/index.html b/index.html index 603b61b..7df17a8 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,8 @@ Hellō Quickstart - + + + + + + + + + + + ` + } + + get nosociallinks() { + return this.getAttribute("nosociallinks") + } + + static get observedAttributes() { + return ["nosociallinks"] + } + + connectedCallback() { + if (window.location.host === "www.hello.coop") { + const fromHelloRef = this.shadow.getElementById("from-hello") + fromHelloRef.style.display = "none" + if (window.innerWidth < 968) { + const footerRef = this.shadow.getElementById("wc-footer") + footerRef.style.height = "4rem" + } + } + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === "nosociallinks") { + const footerRef = this.shadow.getElementById("wc-footer") + footerRef.style.height = "3rem" + footerRef.style.padding = "0" + const footerContainerRef = this.shadow.getElementById( + "wc-footer-content-container" + ) + footerContainerRef.style.justifyContent = "center" + const socialLinksRef = this.shadow.getElementById("social-links") + socialLinksRef.style.display = "none" + const fromHello = this.shadow.getElementById("from-hello") + fromHello.style.display = "none" + const linksRef = this.shadow.getElementById("links") + linksRef.style.width = "100%" + linksRef.style.justifyContent = "center" + } + } + } + + customElements.define("wc-footer", Footer) \ No newline at end of file diff --git a/public/assets/quickstart-glob.js b/public/assets/quickstart-glob.js new file mode 100644 index 0000000..6415852 --- /dev/null +++ b/public/assets/quickstart-glob.js @@ -0,0 +1,12 @@ +/** + * If browser back button was used, flush cache + * This ensures that user will always see an accurate, up-to-date view based on their state + * https://stackoverflow.com/questions/8788802/prevent-safari-loading-from-cache-when-back-button-is-clicked + */ +(function () { + window.onpageshow = function (event) { + if (event.persisted) { + window.location.reload(); + } + }; +})(); \ No newline at end of file diff --git a/sri.js b/sri.js new file mode 100644 index 0000000..065bb32 --- /dev/null +++ b/sri.js @@ -0,0 +1,71 @@ +//Based on https://github.com/vitejs/vite/discussions/11043#discussioncomment-4233645 and rollup-sri-plugin +//These is discussion around making this fn native to vite: https://github.com/vitejs/vite/issues/2377 + +import * as cheerio from 'cheerio'; +import { createHash } from 'crypto'; +import fs from 'fs'; +import fetch from 'node-fetch'; + +const invalidHashAlgorithms = ['sha1', 'md5']; + +export default function subresourceIntegrity(options) { + const selectors = options?.selectors || ['script', 'link[rel=stylesheet]']; + const hashAlgorithms = options?.algorithms || ['sha384']; + const crossorigin = options?.crossorigin || 'anonymous'; + const publicPath = options?.publicPath ?? ''; + let active = options?.active ?? true; + + return { + name: 'subresource-integrity', + buildStart() { + hashAlgorithms + .filter((alg) => invalidHashAlgorithms.includes(alg.toLowerCase())) + .forEach((alg) => this.warn(`Insecure hashing algorithm "${alg}" will be rejected by browsers!`)); + }, + async closeBundle() { + if (!active) return; + + const buildDir = 'S3'; //TBD get this from vite config + for(const file of ['/index.html']) { //TBD get these from vite config + let content = fs.readFileSync(buildDir + file); + const $ = cheerio.load(content); + const elements = $(selectors.join()).get(); + + for (const el of elements) { + const integrityAlreadyHardCoded = $(el).attr('integrity'); + if (integrityAlreadyHardCoded) continue; + // const url = ($(el).attr('href') || $(el).attr('src'))?.replace(publicPath, ''); + const url = $(el).attr('src')?.replace(publicPath, ''); //only generate hash for script src + if (!url) continue; + + let buf; + if (fs.existsSync(buildDir + '/' + url)) { + //@ts-ignore + buf = Buffer.from(fs.readFileSync(buildDir + '/' + url)); + } else if (url.startsWith('http')) { + buf = await (await fetch(url)).buffer(); + } else { + this.warn(`could not resolve resource "${url}"!`); + continue; + } + + const hashes = hashAlgorithms.map((algorithm) => generateIdentity(buf, algorithm)); + + $(el).attr('integrity', hashes.join(' ')); + $(el).attr('crossorigin', crossorigin); + } + + fs.writeFileSync(buildDir + file, $.html()); + } + }, + }; +} + +function generateIdentity(source, alg) { + const hash = createHash(alg).update(source).digest().toString('base64'); + return `${alg.toLowerCase()}-${hash}`; +} + +function isHtmlAsset(obj) { + return obj.fileName.endsWith('.html') && obj.type === 'asset'; +} \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 91d6ca5..11c5469 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,9 +1,16 @@ import { defineConfig } from 'vite' import { svelte } from '@sveltejs/vite-plugin-svelte' +import sri from './sri.js'; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [svelte()], + plugins: [ + svelte(), + { + enforce: "post", + ...sri({ publicPath: "/" }) + } + ], build: { outDir: 'S3', emptyOutDir: true