Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/issue3329-macos-real-pixel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: macOS treated-media real-pixel gate

on:
pull_request:
paths:
- "scripts/issue3329-macos-real-pixel.mjs"
- ".github/workflows/issue3329-macos-real-pixel.yml"
- "packages/core/src/runtime/colorGrading.ts"
- "packages/engine/src/services/frameCapture.ts"
- "packages/engine/src/services/screenshotService.ts"

permissions:
contents: read

jobs:
macos-real-pixels:
runs-on: macos-14
timeout-minutes: 45
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
lfs: true
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: 22
- name: Install FFmpeg
run: brew install ffmpeg
- name: Install dependencies
run: bash scripts/ci/install-workspace-dependencies.sh
- name: Build local CLI render stack
run: |
bun run --filter '@hyperframes/{parsers,lint,studio-server}' build
bun run --cwd packages/core build
bun run --filter '@hyperframes/{engine,producer,studio}' build
bun run --filter @hyperframes/cli build
- name: Run real-pixel boundary matrix
env:
HF_ISSUE3329_ARTIFACT_DIR: ${{ github.workspace }}/.artifacts/issue3329-macos
run: node scripts/issue3329-macos-real-pixel.mjs
- name: Upload boundary evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: issue3329-macos-real-pixels-${{ runner.arch }}
path: .artifacts/issue3329-macos
if-no-files-found: error
include-hidden-files: true
55 changes: 55 additions & 0 deletions scripts/issue3329-artifact-safety.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { existsSync, realpathSync } from "node:fs";
import { homedir } from "node:os";
import { basename, dirname, isAbsolute, parse, relative, resolve, sep } from "node:path";

function rejectTraversal(raw) {
if (raw.split(/[\\/]+/).includes("..")) {
throw new Error("Artifact root must not contain traversal segments");
}
}

function isForbiddenRoot(target, repo, artifactSpace) {
return new Set([parse(target).root, resolve(homedir()), repo, artifactSpace]).has(target);
}

function isDedicatedDescendant(target, artifactSpace) {
const rel = relative(artifactSpace, target);
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
}

function canonicalizeTarget(target) {
const suffix = [];
let ancestor = target;
while (!existsSync(ancestor)) {
const parent = dirname(ancestor);
if (parent === ancestor) {
throw new Error("Artifact root has no canonical ancestor");
}
suffix.unshift(basename(ancestor));
ancestor = parent;
}
return resolve(realpathSync(ancestor), ...suffix);
}

function rejectUnsafeTarget(target, repo, artifactSpace) {
if (
isForbiddenRoot(target, repo, artifactSpace) ||
!isDedicatedDescendant(artifactSpace, repo) ||
!isDedicatedDescendant(target, artifactSpace)
) {
throw new Error(
"Artifact root must be a dedicated descendant of the repository artifact space",
);
}
}

export function validateArtifactRoot(requestedPath, repoRoot) {
const raw = String(requestedPath ?? "");
rejectTraversal(raw);
const repo = realpathSync(resolve(repoRoot));
const artifactSpace = canonicalizeTarget(resolve(repo, ".artifacts"));
const requestedTarget = resolve(repo, raw);
const target = canonicalizeTarget(requestedTarget);
rejectUnsafeTarget(target, repo, artifactSpace);
return requestedTarget;
}
Loading
Loading