From 7f971b1ebc65bd29d5016bc72e0cd077daf87091 Mon Sep 17 00:00:00 2001 From: martin veillette Date: Tue, 11 Aug 2026 08:13:12 -0400 Subject: [PATCH] chore: retrofit template hardening from SceneryStackTemplate Ports portable pieces of OpenPhysics/SceneryStackTemplate@ab309a1: Dependabot override ignores, tilde override pins, security headers, PWA/share metadata, and CLAUDE override docs. --- .github/dependabot.yml | 5 ++++ CLAUDE.md | 17 +++++++++++ README.md | 2 ++ index.html | 22 ++++++++++++++ package-lock.json | 6 ++-- package.json | 6 ++-- public/favicon.ico | Bin 0 -> 15086 bytes public/icons/apple-touch-icon.png | Bin 0 -> 3906 bytes public/icons/icon-192.png | Bin 0 -> 4051 bytes public/icons/icon-512.png | Bin 0 -> 14392 bytes public/screenshots/narrow.png | Bin 0 -> 28200 bytes public/screenshots/wide.png | Bin 0 -> 25580 bytes scripts/generate-icons.ts | 33 +++++++++++++++++++-- vite.config.ts | 46 ++++++++++++++++++++++++++++-- 14 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 public/favicon.ico create mode 100644 public/icons/apple-touch-icon.png create mode 100644 public/icons/icon-192.png create mode 100644 public/icons/icon-512.png create mode 100644 public/screenshots/narrow.png create mode 100644 public/screenshots/wide.png diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c354abb..7391a2d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,6 +18,11 @@ updates: ignore: - dependency-name: "@types/node" update-types: ["version-update:semver-major"] + # Forced in package.json overrides (see CLAUDE.md → package.json overrides). + # Dependabot PRs would fight the pin / reopen known-accepted risk. + - dependency-name: "lodash" + - dependency-name: "three" + - dependency-name: "brace-expansion" groups: development-dependencies: dependency-type: "development" diff --git a/CLAUDE.md b/CLAUDE.md index ad9c534..78c6dfc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,8 +74,25 @@ Fleet-standard Vitest layout: npm run lint && npm run check && npm run build && npm test ``` +`npm run release` intentionally skips `npm test` in some sims — append `&& npm test` before the version bump so a release cannot ship a failing suite. + ## Development notes - **`VariableStarPhotometryConstants.ts`** uses nested frozen `as const` groups (`FIELD`, `APERTURE`, `TIME`, `LAYOUT`, `PDM`) instead of a flat namespace — intentional for five distinct concerns; still no magic numbers in model/view code. - **`npm run decompile`** extracts NAAP Flash ActionScript via JPEXS FFDec from `../Baseline/Astronomy/flash-animations` into gitignored `NAAP/decompiled/`. - After `npm run build`, the sim is installable offline via Workbox (`dist/manifest.webmanifest`). + +## Compliance carve-outs + +### `package.json` overrides + +JSON cannot carry comments, so the rationale for forced transitive pins lives here. Prefer +**tilde (`~`) or exact** versions — caret (`^`) lets minors drift under what is meant to be a +hard pin. Dependabot ignores these three names (see `.github/dependabot.yml`) so it does not +open PRs that fight the overrides. Revisit when SceneryStack drops or re-pins them upstream. + +| Override | Pin | Why | +|---|---|---| +| `lodash` | `~4.18.1` | SceneryStack declares `~4.17.12`. Bump clears Dependabot/npm advisories patched in 4.18.x (e.g. GHSA-r5fr-rjxr-66jc, GHSA-f23m-r3pf-42rh). | +| `three` | `~0.125.2` | SceneryStack declares `^0.104.0`. Floor is 0.125.0 for GHSA-fq6p-x6j3-cmmq (ReDoS). Staying on the 0.125 line avoids a larger API jump; **0.125.x still has open CVEs** (e.g. XSS GHSA-7vvq-7r29-5vg3, fixed only in ≥0.137.0). Remove this override if/when SceneryStack stops depending on `three` or pins a patched line itself. LightPropagation keeps a higher `three` pin — do not force 0.125 there. | +| `brace-expansion` | `~5.0.9` | Transitive via `vite-plugin-pwa` / Workbox. Clears npm audit (originally GHSA-mh99-v99m-4gvg; keep ≥5.0.9 for GHSA-rgw5-rvv9-x895). | diff --git a/README.md b/README.md index f9c2391..79db2c5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Variable Star Photometry +[![CI](https://github.com/OpenPhysics/VariableStarPhotometry/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenPhysics/VariableStarPhotometry/actions/workflows/ci.yml) + An interactive multi-screen simulation for studying variable stars through CCD photometry, built with [SceneryStack](https://scenerystack.org/), Vite 8, TypeScript 7, and Biome 2. A SceneryStack port of the [NAAP Variable Star Photometry Lab](https://astro.unl.edu/naap/vsp/vsp.html). diff --git a/index.html b/index.html index a21727a..6e6974e 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,28 @@ + + + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json index 2022440..7c4a644 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4701,9 +4701,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 9704b7f..e5c22a5 100644 --- a/package.json +++ b/package.json @@ -64,8 +64,8 @@ "node": ">=24" }, "overrides": { - "lodash": "^4.18.0", - "three": "^0.125.0", - "brace-expansion": "^5.0.8" + "lodash": "~4.18.1", + "three": "~0.125.2", + "brace-expansion": "~5.0.9" } } diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8a2487fa9d0d95565d41f0e1406f0cee4306ec2a GIT binary patch literal 15086 zcmeHNOK&4Z5FP}ljpO(GF*A1j%z6`N**IAeOcIl9cFAsb5pRTub{7GMa6#h0@(=ci z#1U}=@dNl5Nc;}Oe<=EE)6R_D(X1G@Czjj5_P7<0{XEdyfjp*T7A^l}<+@R|Y%}ki;$so0hF{;Ib zqz41kNQL;k%F)&S{0e~;UN1$iaANL)?p8KTTY7}^+sK6paL0^txUKg}$OA67GTL_4 zSWix#$pr{mewD>=fXq<9mE1LExAl!J1}y@gX?3o|g0yw|gj%PM*_sU7TgET9Vz&{- z2^$X557G|yTif}-u>RAGxAJnT7o*XUHj?S!#0&Vss}$#K8kMio}%nCn|;>eaq<(4aM-plRFer?dp#YZC%ZY{&WD{!mQJ_Q)G9=1 zzZ&O&v+3b>wyzh?&iD87{2aCV+uOHNeHwwsIU(wze6toG!ihfE9(Bma+y%at2-4Bb zBtLsm;%q(N$@Tm1wbFEx(OdNd-N|N}%_qa@aK2*<&S%geCJ_8ZDddZ`wS3)((%s6H z?l{AaH&S%Cp5(GYi?a~C@88b!V+_O_XLqt3Z$0PVswYUq972v6i45r^`eY^Sgm}c- z2OVTFjFm>!XWJ)~{d)F8T#T3){|)%LpLT(dH?t8k5-AQX7JuVE>6Z?evzgPWbNK^x zzInmxo9sT|mwenkN|idD|MCZiQn}9WIDW~;T#Zh8}N+!x2)8OPaomhtnDKTf|{`fI7-SaTA7 z^-PAvj`lplB;)2qvq;(Vnu8a7obm#G;gP3cDN^l%fyz_3-?zN*4aHNfhw=gC1Iy+E zwRD;ghb%i(lS%2lXjBY^_p3+ktNhxs*&h1rHFT+t&>Mif96+YKc+Zpd`Gtf1k+b}~ zYjAy$KY4aqBIB9{EqFw@Kt&A=66l zRJZyM=JDB)At zmgQJSz4H(9SyUT7qo*>od2&u?KmAS*e*Bd-jvsN{vg?nS4Y7VPZ&3TA=XCMs-*oZU zKh*y48OLF6hRg5!{8|rg|3s3SdtEyE{v{o~c**N}Jjw4le(B*>4)z+nLmQbKRq9O+ zMw->xJM>=Fez$Qe!|k|9-x~0pjP)B#)Sd3lZgp`pZqm0*@WvZ3?jQi%Mcv5zzm3s@ z{(TD}vmNe7v)u6(tv}{8_`oxJAJ nZMQx11Fv=WlK;cQ!)vjAUfMwMz8?SKMXVG1JV^Z(Xn}tL!(rTV literal 0 HcmV?d00001 diff --git a/public/icons/apple-touch-icon.png b/public/icons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dd0604a3563afa0542378791cf2e62eba2f8575 GIT binary patch literal 3906 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 literal 0 HcmV?d00001 diff --git a/public/icons/icon-512.png b/public/icons/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..02357989797afb5549d810244e32a80326f999c0 GIT binary patch literal 14392 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 literal 0 HcmV?d00001 diff --git a/public/screenshots/narrow.png b/public/screenshots/narrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0abc9b10fffd929a92bddc7fd90aa1425eee1ca7 GIT binary patch literal 28200 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 x9f8^=t!QEe-c(xAz8wFHe*FJcG`_r=Lu { + const iconSize = Math.round(Math.min(width, height) * 0.4); + const icon = await sharp(svg, { density }).resize(iconSize, iconSize).png().toBuffer(); + await sharp({ + create: { + width, + height, + channels: 4, + background: THEME_BG, + }, + }) + .composite([{ input: icon, gravity: "center" }]) + .png() + .toFile(resolve(publicDir, file)); +} + +mkdirSync(resolve(publicDir, "screenshots"), { recursive: true }); +await writeScreenshot(1280, 720, "screenshots/wide.png"); +await writeScreenshot(720, 1280, "screenshots/narrow.png"); diff --git a/vite.config.ts b/vite.config.ts index 128d7dc..80425ba 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,6 +6,7 @@ import { VitePWA } from "vite-plugin-pwa"; * Security headers required for: * - COOP/COEP: SharedArrayBuffer support * - CSP: restrict resource loading to same-origin + known blob/data exceptions + * - Referrer / Permissions: tighten default browser leakage * - X-Content-Type-Options: prevent MIME sniffing * - X-Frame-Options: prevent clickjacking (belt-and-suspenders alongside frame-ancestors) */ @@ -14,9 +15,17 @@ const securityHeaders: Record = { "Cross-Origin-Embedder-Policy": "require-corp", "Content-Security-Policy": [ "default-src 'self'", + // TODO(scenerystack): drop 'unsafe-eval' when SceneryStack no longer needs + + // Function/eval for query-parameter parsing — reopen a CSP audit then. + // 'unsafe-eval' is required for SceneryStack query parameter parsing "script-src 'self' 'unsafe-eval'", "worker-src blob: 'self'", + // TODO(scenerystack): drop 'unsafe-inline' when SceneryStack stops setting + + // element.style / cssText for theming (same CSP revisit as unsafe-eval). + // Inline styles are set via element.style / cssText throughout the UI layer "style-src 'self' 'unsafe-inline'", // data: for icons @@ -29,10 +38,18 @@ const securityHeaders: Record = { "base-uri 'self'", "frame-ancestors 'none'", ].join("; "), + "Referrer-Policy": "strict-origin-when-cross-origin", + "Permissions-Policy": "camera=(), microphone=(), geolocation=()", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", }; +/** Single-file mode: inline every imported asset as base64 (effectively unlimited). */ +const INLINE_LIMIT_BYTES = 100 * 1024 * 1024; + +/** Workbox precache ceiling — SceneryStack bundles exceed the default 2 MB limit. */ +const WORKBOX_MAX_FILE_BYTES = 12 * 1024 * 1024; + /** Escape a string for literal use inside a `RegExp`. */ function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); @@ -116,7 +133,7 @@ export default defineConfig(({ mode }) => { // SceneryStack bundles exceed Vite's default 500 kB chunk warning. chunkSizeWarningLimit: 5000, ...(single && { - assetsInlineLimit: 100_000_000, + assetsInlineLimit: INLINE_LIMIT_BYTES, cssCodeSplit: false, copyPublicDir: false, rollupOptions: { @@ -137,16 +154,20 @@ export default defineConfig(({ mode }) => { registerType: "autoUpdate", includeAssets: ["favicon.ico", "icons/apple-touch-icon.png"], manifest: { + id: "variable-star-photometry", name: "Variable Star Photometry", // biome-ignore lint/style/useNamingConvention: Web App Manifest spec requires snake_case keys short_name: "VarStarPhoto", description: "A SceneryStack port of the NAAP Variable Star Photometry Lab", + categories: ["education", "science"], // biome-ignore lint/style/useNamingConvention: Web App Manifest spec requires snake_case keys theme_color: "#0a0a1a", // biome-ignore lint/style/useNamingConvention: Web App Manifest spec requires snake_case keys background_color: "#000000", display: "standalone", - orientation: "landscape", + // biome-ignore lint/style/useNamingConvention: Web App Manifest spec requires snake_case keys + display_override: ["window-controls-overlay", "standalone"], + // No `orientation` — leave free so portrait-friendly sims are not forced landscape. icons: [ { src: "icons/icon-192.png", @@ -165,9 +186,28 @@ export default defineConfig(({ mode }) => { purpose: "maskable", }, ], + // Placeholder shots from `npm run icons`; replace with real sim screenshots before shipping. + screenshots: [ + { + src: "screenshots/wide.png", + sizes: "1280x720", + type: "image/png", + // biome-ignore lint/style/useNamingConvention: Web App Manifest spec requires snake_case keys + form_factor: "wide", + label: "Variable Star Photometry", + }, + { + src: "screenshots/narrow.png", + sizes: "720x1280", + type: "image/png", + // biome-ignore lint/style/useNamingConvention: Web App Manifest spec requires snake_case keys + form_factor: "narrow", + label: "Variable Star Photometry", + }, + ], }, workbox: { - maximumFileSizeToCacheInBytes: 12 * 1024 * 1024, + maximumFileSizeToCacheInBytes: WORKBOX_MAX_FILE_BYTES, globPatterns: ["**/*.{js,css,html,svg,png,woff2}"], }, }),