From ba14a89b1a8f5e9886a4db61ac806c1fbf4945b7 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Mon, 10 Aug 2026 15:56:47 -0400 Subject: [PATCH] port: refuse dst inside src (realpath-normalized) + workflow clones omp repo outside checkout --- .gitea/workflows/port-to-omp.yml | 22 +++++++++++++--------- port-to-omp.mjs | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/port-to-omp.yml b/.gitea/workflows/port-to-omp.yml index 06da490..c8f0186 100644 --- a/.gitea/workflows/port-to-omp.yml +++ b/.gitea/workflows/port-to-omp.yml @@ -33,24 +33,28 @@ jobs: run: | set -euo pipefail URL="https://oauth2:${PORTING_TOKEN}@git.freno.me/Mike/${OMP_REPO}.git" + # The omp checkout lives in $RUNNER_TEMP, outside the pi checkout: + # the port script refuses to write into a subdirectory of its own + # source (cpSync would recurse into itself). + PORT_DIR="${RUNNER_TEMP:-/tmp}/omp-port" if git ls-remote "$URL" HEAD >/dev/null 2>&1; then - git clone --depth 1 "$URL" omp-port - git -C omp-port config user.name "omp-port" - git -C omp-port config user.email "omp-port@freno.me" + git clone --depth 1 "$URL" "$PORT_DIR" + git -C "$PORT_DIR" config user.name "omp-port" + git -C "$PORT_DIR" config user.email "omp-port@freno.me" else - git init -b main omp-port - git -C omp-port remote add origin "$URL" - git -C omp-port config user.name "omp-port" - git -C omp-port config user.email "omp-port@freno.me" + git init -b main "$PORT_DIR" + git -C "$PORT_DIR" remote add origin "$URL" + git -C "$PORT_DIR" config user.name "omp-port" + git -C "$PORT_DIR" config user.email "omp-port@freno.me" fi # Regenerate the port directly into the omp checkout. The script # preserves .git, asserts every patch rule, and runs `bun install` # (refreshing bun.lock + node_modules). - bun "$GITHUB_WORKSPACE/port-to-omp.mjs" --out "$PWD/omp-port" + bun "$GITHUB_WORKSPACE/port-to-omp.mjs" --out "$PORT_DIR" - cd omp-port + cd "$PORT_DIR" # The port must compile against the pinned @oh-my-pi SDK before it # ships to users. bun run typecheck diff --git a/port-to-omp.mjs b/port-to-omp.mjs index eda5282..3949063 100644 --- a/port-to-omp.mjs +++ b/port-to-omp.mjs @@ -21,10 +21,11 @@ import { mkdirSync, readdirSync, readFileSync, + realpathSync, rmSync, writeFileSync, } from "node:fs"; -import { join } from "node:path"; +import { join, relative, resolve, isAbsolute, dirname, basename } from "node:path"; import { execSync } from "node:child_process"; import { homedir } from "node:os"; @@ -113,7 +114,33 @@ function mirrorTree(srcDir, dstDir) { } } -function walk(dir) { +function real(p) { + try { + return realpathSync(p); + } catch { + // walk to the nearest existing ancestor and realpath it, then re-append + const tail = []; + let cur = resolve(p); + for (;;) { + try { + return join(realpathSync(cur), ...tail); + } catch {} + const parent = dirname(cur); + if (parent === cur) return resolve(p); + tail.unshift(basename(cur)); + cur = parent; + } + } +} + +function assertDstOutsideSrc(srcDir, dstDir) { + const rel = relative(real(srcDir), real(dstDir)); + if (rel === "" || (!rel.startsWith("..") && !isAbsolute(rel))) { + throw new Error( + `refusing to port into a subdirectory of the source: ${dstDir} is inside ${srcDir}` + ); + } +}function walk(dir) { const out = []; for (const entry of readdirSync(dir, { withFileTypes: true })) { if (SKIP.has(entry.name)) continue; @@ -288,6 +315,7 @@ function portExtension() { if (!dstDir) throw new Error("--out requires a directory argument"); if (!existsSync(srcDir)) throw new Error(`no base extension at ${srcDir}`); + assertDstOutsideSrc(srcDir, dstDir); console.log(`== ${srcDir} -> ${dstDir}`); mirrorTree(srcDir, dstDir);