mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 03:49:31 +00:00
fix(test): check sensitive paths before lstat to fix macOS permission error (#3157)
On macOS, lstat("/etc/master.passwd") throws EACCES before the
sensitive-path pattern check runs. Move pattern matching before
filesystem calls so security errors are thrown consistently
regardless of filesystem permissions.
Fixes #3153
Agent: test-engineer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
44940ceb5b
commit
b99a16616f
2 changed files with 25 additions and 9 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.30.8",
|
||||
"version": "0.30.9",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
|
|
@ -657,14 +657,9 @@ export function validatePromptFilePath(filePath: string): void {
|
|||
// Normalize the path to resolve .. and textual tricks
|
||||
let resolved = resolve(filePath);
|
||||
|
||||
// Follow symlinks to validate the real target path, not the symlink name.
|
||||
// Without this, a symlink like `innocent.txt -> ~/.ssh/id_rsa` would bypass
|
||||
// sensitive path checks because the resolved string wouldn't match patterns.
|
||||
if (existsSync(resolved)) {
|
||||
resolved = realpathSync(resolved);
|
||||
}
|
||||
|
||||
// Check against sensitive path patterns
|
||||
// Check against sensitive path patterns BEFORE any filesystem calls.
|
||||
// On macOS, lstat("/etc/master.passwd") throws EACCES before we can check
|
||||
// the pattern, so we must validate the textual path first.
|
||||
for (const { pattern, description } of SENSITIVE_PATH_PATTERNS) {
|
||||
if (pattern.test(resolved)) {
|
||||
throw new Error(
|
||||
|
|
@ -677,6 +672,27 @@ export function validatePromptFilePath(filePath: string): void {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Follow symlinks to validate the real target path, not the symlink name.
|
||||
// Without this, a symlink like `innocent.txt -> ~/.ssh/id_rsa` would bypass
|
||||
// sensitive path checks because the resolved string wouldn't match patterns.
|
||||
if (existsSync(resolved)) {
|
||||
resolved = realpathSync(resolved);
|
||||
|
||||
// Re-check after symlink resolution — the real path may be sensitive
|
||||
for (const { pattern, description } of SENSITIVE_PATH_PATTERNS) {
|
||||
if (pattern.test(resolved)) {
|
||||
throw new Error(
|
||||
`Security check failed: cannot use '${filePath}' as a prompt file.\n\n` +
|
||||
`This path points to ${description}.\n` +
|
||||
"Prompt contents are sent to the agent and may be logged or stored remotely.\n\n" +
|
||||
"For security, use a plain text file instead:\n" +
|
||||
` 1. Create a new file: echo "Your instructions here" > prompt.txt\n` +
|
||||
" 2. Use it: spawn <agent> <cloud> --prompt-file prompt.txt",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue