mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 16:46:31 +03:00
Test: cover directory, chained, and relative /source symlink escapes (#618)
AI assistance: Cursor Grok 4.6 implemented this change. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
import { describe, it, before, after } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { existsSync, mkdtempSync, readFileSync, writeFileSync, mkdirSync, rmSync, realpathSync, symlinkSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { join, relative } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { execFileSync, execSync, spawn } from 'node:child_process';
|
||||
import {
|
||||
@@ -3365,6 +3365,56 @@ colors: {}
|
||||
}
|
||||
});
|
||||
|
||||
it('/source rejects a directory symlink whose nested file is outside the project', async () => {
|
||||
const outsideDir = mkdtempSync(join(tmpdir(), 'impeccable-live-outside-dir-'));
|
||||
writeFileSync(join(outsideDir, 'cred.txt'), 'OUTSIDE SECRET');
|
||||
const linkPath = join(serverCwd, 'escape-dir');
|
||||
symlinkSync(outsideDir, linkPath);
|
||||
try {
|
||||
const res = await fetch(`http://localhost:${server.port}/source?token=${server.token}&path=escape-dir/cred.txt`);
|
||||
await res.text().catch(() => {});
|
||||
assert.equal(res.status, 403);
|
||||
} finally {
|
||||
rmSync(linkPath, { force: true });
|
||||
rmSync(outsideDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('/source rejects a chained symlink that resolves outside the project', async () => {
|
||||
const outsideDir = mkdtempSync(join(tmpdir(), 'impeccable-live-outside-chain-'));
|
||||
const outsideFile = join(outsideDir, 'secret.txt');
|
||||
writeFileSync(outsideFile, 'OUTSIDE SECRET');
|
||||
const midPath = join(serverCwd, 'mid-link.txt');
|
||||
const linkPath = join(serverCwd, 'double-out.txt');
|
||||
symlinkSync(outsideFile, midPath);
|
||||
symlinkSync(midPath, linkPath);
|
||||
try {
|
||||
const res = await fetch(`http://localhost:${server.port}/source?token=${server.token}&path=double-out.txt`);
|
||||
await res.text().catch(() => {});
|
||||
assert.equal(res.status, 403);
|
||||
} finally {
|
||||
rmSync(linkPath, { force: true });
|
||||
rmSync(midPath, { force: true });
|
||||
rmSync(outsideDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('/source rejects a relative symlink that points outside the project', async () => {
|
||||
const outsideDir = mkdtempSync(join(tmpdir(), 'impeccable-live-outside-rel-'));
|
||||
const outsideFile = join(outsideDir, 'secret.txt');
|
||||
writeFileSync(outsideFile, 'OUTSIDE SECRET');
|
||||
const linkPath = join(serverCwd, 'rel-out.txt');
|
||||
symlinkSync(relative(serverCwd, outsideFile), linkPath);
|
||||
try {
|
||||
const res = await fetch(`http://localhost:${server.port}/source?token=${server.token}&path=rel-out.txt`);
|
||||
await res.text().catch(() => {});
|
||||
assert.equal(res.status, 403);
|
||||
} finally {
|
||||
rmSync(linkPath, { force: true });
|
||||
rmSync(outsideDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('/modern-screenshot.js serves the vendored UMD build', async () => {
|
||||
const res = await fetch(`http://localhost:${server.port}/modern-screenshot.js`);
|
||||
assert.equal(res.status, 200);
|
||||
|
||||
Reference in New Issue
Block a user