mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-09 03:23:55 +02:00
security(E3): gate GSTACK_SLUG on /welcome path traversal
The /welcome handler interpolates GSTACK_SLUG directly into the filesystem path used to locate the project-local welcome page. Without validation, a slug like "../../etc/passwd" would resolve to ~/.gstack/projects/../../etc/passwd/designs/welcome-page-20260331/finalized.html — classic path traversal. Not exploitable today: GSTACK_SLUG is set by the gstack CLI at daemon launch, and an attacker would already need local env-var access to poison it. But the gate is one regex (^[a-z0-9_-]+$), and a defense-in-depth pass costs us nothing when the cost of being wrong is arbitrary file read via /welcome. Fall back to the safe 'unknown' literal when the slug fails validation — same fallback the code already uses when GSTACK_SLUG is unset. No behavior change for legitimate slugs (they all match the regex). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -280,3 +280,17 @@ describe('Rate limit + denial log wiring', () => {
|
||||
expect(registrySrc).not.toMatch(/CONNECT_RATE_LIMIT\s*=\s*3\s*;/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('E3: /welcome GSTACK_SLUG path traversal gate', () => {
|
||||
test('/welcome validates GSTACK_SLUG against ^[a-z0-9_-]+$ before interpolating into path', () => {
|
||||
const welcomeBlock = sliceBetween(
|
||||
SERVER_SRC,
|
||||
"url.pathname === '/welcome'",
|
||||
'if (fs.existsSync(projectWelcome)) return projectWelcome;'
|
||||
);
|
||||
// Must validate the slug before using it in a path
|
||||
expect(welcomeBlock).toMatch(/\/\^\[a-z0-9_-\]\+\$\/\.test\(rawSlug\)/);
|
||||
// Must fall back to a safe default when the slug fails validation
|
||||
expect(welcomeBlock).toContain("'unknown'");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user