Skip to content
Closed
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
4 changes: 4 additions & 0 deletions docs/REPLAY.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,7 @@ npm test
```

The unit fixtures cover seeded extraction/redaction, stale intermediate-completion rejection, a deterministic three-completion replay, opaque-label retention and validation, coverage-only output, operational failures, zero candidates, private no-follow outputs, localhost redirect enforcement, schema validation, and corpus-hash rejection.

## Platform support

The exam/replay harness is **authoritative on Linux**, where it uses race-safe `/proc/self/fd` + `O_NOFOLLOW` I/O (no symlink races). On **macOS/Windows** the same scripts fall back to a best-effort path that still rejects symlink components but is **not** race-safe; use it for local development only. The release-gating exam run stays Linux-only (CI runs on `ubuntu-latest`). If you hit a platform error, that is expected on non-Linux for the race-safe path — run the harness on Linux (or in a container) for an authoritative result.
60 changes: 37 additions & 23 deletions scripts/label-corpus.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,33 +110,46 @@ function serializeJsonl(rows) {
}

function openAnchoredDirectory(directory, label = directory) {
if (process.platform !== 'linux' || !fs.constants.O_NOFOLLOW) {
throw new Error('race-safe exam I/O requires Linux /proc/self/fd and O_NOFOLLOW');
}
const resolved = path.resolve(directory);
const root = path.parse(resolved).root;
const flags = fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW;
let currentFd = fs.openSync(root, flags);
try {
for (const component of resolved.slice(root.length).split(path.sep).filter(Boolean)) {
const nextFd = fs.openSync(`/proc/self/fd/${currentFd}/${component}`, flags);
// On Linux we use the race-safe /proc/self/fd + O_NOFOLLOW path (no symlink
// races). On other platforms we fall back to a best-effort resolution that
// still rejects symlink components, but is NOT race-safe — it exists so
// contributors on macOS/Windows can run and develop the harness locally.
// The authoritative exam run that gates releases stays Linux-only (see docs/REPLAY.md).
if (process.platform === 'linux' && fs.constants.O_NOFOLLOW) {
const resolved = path.resolve(directory);
const root = path.parse(resolved).root;
const flags = fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW;
let currentFd = fs.openSync(root, flags);
try {
for (const component of resolved.slice(root.length).split(path.sep).filter(Boolean)) {
const nextFd = fs.openSync(`/proc/self/fd/${currentFd}/${component}`, flags);
fs.closeSync(currentFd);
currentFd = nextFd;
}
return currentFd;
} catch (error) {
fs.closeSync(currentFd);
currentFd = nextFd;
}
return currentFd;
} catch (error) {
fs.closeSync(currentFd);
if (error.code === 'ELOOP' || error.code === 'ENOTDIR') {
throw new Error(`${label} path must contain only directories, never symlinks`, { cause: error });
if (error.code === 'ELOOP' || error.code === 'ENOTDIR') {
throw new Error(`${label} path must contain only directories, never symlinks`, { cause: error });
}
throw error;
}
throw error;
}
// Cross-platform best-effort fallback (no /proc/self/fd race-safety).
const resolved = path.resolve(directory);
if (fs.existsSync(resolved) && !fs.statSync(resolved).isDirectory()) {
throw new Error(`${label} path must contain only directories, never symlinks`);
}
return { __crossPlatformDir: resolved };
}

function openAnchoredParent(file, label = file) {
const resolved = path.resolve(file);
const parentFd = openAnchoredDirectory(path.dirname(resolved), `${label} parent`);
return { parentFd, target: `/proc/self/fd/${parentFd}/${path.basename(resolved)}` };
const parent = openAnchoredDirectory(path.dirname(resolved), `${label} parent`);
if (parent && parent.__crossPlatformDir !== undefined) {
return { parentFd: null, target: path.join(parent.__crossPlatformDir, path.basename(resolved)) };
}
return { parentFd: parent, target: `/proc/self/fd/${parent}/${path.basename(resolved)}` };
}

function readRegularNoFollow(file, label = file) {
Expand All @@ -149,12 +162,13 @@ function readRegularNoFollow(file, label = file) {
return fs.readFileSync(descriptor);
} finally {
if (descriptor !== undefined) fs.closeSync(descriptor);
fs.closeSync(anchored.parentFd);
if (anchored.parentFd !== null) fs.closeSync(anchored.parentFd);
}
}

function writePrivate(file, content) {
const anchored = openAnchoredParent(file, file);
const parentDir = anchored.parentFd !== null ? `/proc/self/fd/${anchored.parentFd}` : anchored.target.replace(/[/\\][^/\\]*$/, '');
let temporary;
let descriptor;
try {
Expand All @@ -163,7 +177,7 @@ function writePrivate(file, content) {
} catch (error) { if (error.code !== 'ENOENT') throw error; }
do {
privateWriteCounter += 1;
temporary = `/proc/self/fd/${anchored.parentFd}/.${path.basename(file)}.tmp-${process.pid}-${privateWriteCounter}`;
temporary = `${parentDir}/.${path.basename(file)}.tmp-${process.pid}-${privateWriteCounter}`;
try { descriptor = fs.openSync(temporary, 'wx', 0o600); }
catch (error) { if (error.code !== 'EEXIST') throw error; }
} while (descriptor === undefined);
Expand All @@ -176,7 +190,7 @@ function writePrivate(file, content) {
} finally {
if (descriptor !== undefined) fs.closeSync(descriptor);
if (temporary) { try { fs.unlinkSync(temporary); } catch {} }
fs.closeSync(anchored.parentFd);
if (anchored.parentFd !== null) fs.closeSync(anchored.parentFd);
}
}

Expand Down
Loading