mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-21 18:47:02 +03:00
Fix: stop live-server /source from following symlinks out of the workspace (#618)
AI assistance: Cursor Grok 4.6 implemented this change. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
import { describe, it, before, after } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { existsSync, mkdtempSync, readFileSync, writeFileSync, mkdirSync, rmSync, realpathSync } from 'node:fs';
|
||||
import { existsSync, mkdtempSync, readFileSync, writeFileSync, mkdirSync, rmSync, realpathSync, symlinkSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { execFileSync, execSync, spawn } from 'node:child_process';
|
||||
@@ -3319,6 +3319,52 @@ colors: {}
|
||||
}
|
||||
});
|
||||
|
||||
it('/source rejects a symlink that points outside the project root', async () => {
|
||||
const outsideDir = mkdtempSync(join(tmpdir(), 'impeccable-live-outside-'));
|
||||
const outsideFile = join(outsideDir, 'secret.txt');
|
||||
writeFileSync(outsideFile, 'OUTSIDE SECRET');
|
||||
const linkPath = join(serverCwd, 'linked.txt');
|
||||
symlinkSync(outsideFile, linkPath);
|
||||
try {
|
||||
const res = await fetch(`http://localhost:${server.port}/source?token=${server.token}&path=linked.txt`);
|
||||
await res.text().catch(() => {});
|
||||
assert.equal(res.status, 403);
|
||||
} finally {
|
||||
rmSync(linkPath, { force: true });
|
||||
rmSync(outsideDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('/source serves a symlink whose target stays inside the project', async () => {
|
||||
const nestedDir = join(serverCwd, 'alias');
|
||||
mkdirSync(nestedDir, { recursive: true });
|
||||
const realFile = join(nestedDir, 'page.html');
|
||||
writeFileSync(realFile, '<h1>via alias</h1>\n');
|
||||
const linkPath = join(serverCwd, 'alias-link.html');
|
||||
symlinkSync(realFile, linkPath);
|
||||
try {
|
||||
const res = await fetch(`http://localhost:${server.port}/source?token=${server.token}&path=alias-link.html`);
|
||||
assert.equal(res.status, 200);
|
||||
const text = await res.text();
|
||||
assert.ok(text.includes('via alias'));
|
||||
} finally {
|
||||
rmSync(linkPath, { force: true });
|
||||
rmSync(nestedDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('/source returns 404 for a broken symlink', async () => {
|
||||
const linkPath = join(serverCwd, 'broken-link.txt');
|
||||
symlinkSync(join(serverCwd, 'missing-target.txt'), linkPath);
|
||||
try {
|
||||
const res = await fetch(`http://localhost:${server.port}/source?token=${server.token}&path=broken-link.txt`);
|
||||
await res.text().catch(() => {});
|
||||
assert.equal(res.status, 404);
|
||||
} finally {
|
||||
rmSync(linkPath, { 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