diff --git a/.github/workflows/build-brand-assets.yml b/.github/workflows/build-brand-assets.yml index 1344731..6b9f570 100644 --- a/.github/workflows/build-brand-assets.yml +++ b/.github/workflows/build-brand-assets.yml @@ -20,10 +20,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12' @@ -32,7 +32,7 @@ jobs: sudo apt-get update sudo apt-get install -y libcairo2-dev libffi-dev fonts-dejavu-core python -m pip install --upgrade pip - python -m pip install pillow cairosvg + python -m pip install pillow==12.2.0 cairosvg==2.8.2 - name: Generate raster, favicon, app and social assets run: python scripts/build-assets.py diff --git a/brand.js b/brand.js index ab2b994..2c5c5b1 100644 --- a/brand.js +++ b/brand.js @@ -9,9 +9,11 @@ const rawPath=document.getElementById('raw-path'); let manifest={brands:[],canonicalRawBase:'https://raw.githubusercontent.com/puchadave/assets/main/'}; function esc(v=''){return String(v).replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));} +function safeColor(v=''){return /^#[0-9a-fA-F]{3,8}$/.test(v)?v:'';} +function safeUrl(v=''){return /^https:\/\//.test(v)?v:'#';} function initials(name=''){return name.split(/[\s._-]+/).filter(Boolean).slice(0,2).map(x=>x[0]).join('').toUpperCase()||'•';} function repoPath(path=''){return `https://github.com/puchadave/assets/tree/main/${path}`;} -function rawAsset(path=''){return `${manifest.canonicalRawBase||'https://raw.githubusercontent.com/puchadave/assets/main/'}${path}`;} +function rawAsset(path=''){return safeUrl(`${manifest.canonicalRawBase||'https://raw.githubusercontent.com/puchadave/assets/main/'}${path}`);} function fallbackMark(b){return `${esc(initials(b.name))}${esc(b.name)}`;} function renderBrands(){ @@ -21,7 +23,7 @@ function renderBrands(){ const mark=primary ? `` : fallbackMark(b); - const accent=b.accent?`style="--brand-accent:${esc(b.accent)}"`:''; + const accent=safeColor(b.accent)?`style="--brand-accent:${safeColor(b.accent)}"`:''; return `
${mark}

${esc(b.name)}

${esc(b.description)}

${esc(b.type)}
diff --git a/scripts/create-netbootxyz-alpine-lxc.sh b/scripts/create-netbootxyz-alpine-lxc.sh index f1a5efa..dbf4058 100644 --- a/scripts/create-netbootxyz-alpine-lxc.sh +++ b/scripts/create-netbootxyz-alpine-lxc.sh @@ -164,6 +164,24 @@ echo "[container] Preparing webroot..." mkdir -p "${WEBROOT}" /run/nginx cd "${WEBROOT}" +echo "[container] Fetching netboot.xyz checksums..." +curl -fL --retry 5 --connect-timeout 20 \ + -o netboot.xyz-sha256-checksums.txt \ + "${BASE_URL}/netboot.xyz-sha256-checksums.txt" + +verify_checksum() { + entry="$(grep -E "^[0-9a-f]{64} \*$1\$" netboot.xyz-sha256-checksums.txt || true)" + if [ -z "${entry}" ]; then + echo "WARN: no published checksum for $1, skipping verification" + return 0 + fi + if ! printf '%s\n' "${entry}" | sha256sum -c - >/dev/null 2>&1; then + rm -f "$1" + echo "ERROR: checksum mismatch for $1, file removed" >&2 + return 1 + fi +} + echo "[container] Fetching netboot.xyz menus..." curl -fL --retry 5 --connect-timeout 20 \ -o menus.tar.gz \ @@ -175,9 +193,13 @@ rm -f menus.tar.gz echo "[container] Fetching netboot.xyz boot files..." for f in ${BOOT_FILES}; do echo " -> ${f}" - curl -fL --retry 5 --connect-timeout 20 \ + if curl -fL --retry 5 --connect-timeout 20 \ -o "${f}" \ - "${BASE_URL}/${f}" || echo "WARN: could not fetch ${f}" + "${BASE_URL}/${f}"; then + verify_checksum "${f}" + else + echo "WARN: could not fetch ${f}" + fi done echo "[container] Configuring nginx..."