fix(worker): contain workspace paths against symlinks
This commit is contained in:
@@ -34,6 +34,7 @@ import {
|
||||
promptOpenCodeSession,
|
||||
replyOpenCodePermission,
|
||||
} from './opencode-session';
|
||||
import { assertContainedRealPath } from './path-containment';
|
||||
import { createRedactor, truncate } from './redact';
|
||||
import {
|
||||
listWorkspaceContainerNames,
|
||||
@@ -1058,15 +1059,6 @@ const resolveWorkspace = (jobId: string) => {
|
||||
return workspace;
|
||||
};
|
||||
|
||||
const safeWorkspacePath = (repoDir: string, filePath: string) => {
|
||||
const resolved = path.resolve(repoDir, filePath);
|
||||
const root = path.resolve(repoDir);
|
||||
if (resolved !== root && !resolved.startsWith(`${root}${path.sep}`)) {
|
||||
throw new Error(`Refusing to access path outside repository: ${filePath}`);
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
|
||||
const fileChangedType = async (repoDir: string, filePath: string) => {
|
||||
const status = await run('git', ['status', '--short', '--', filePath], {
|
||||
cwd: repoDir,
|
||||
@@ -1169,7 +1161,11 @@ const recordChangedFiles = async (
|
||||
const materializeEnvFile = async (workspace: ActiveWorkspace) => {
|
||||
const { claim, repoDir } = workspace;
|
||||
if (!claim.job.materializeEnvFile || !claim.job.envFilePath) return;
|
||||
const envPath = safeWorkspacePath(repoDir, claim.job.envFilePath);
|
||||
const envPath = await assertContainedRealPath(
|
||||
repoDir,
|
||||
claim.job.envFilePath,
|
||||
{ forWrite: true },
|
||||
);
|
||||
await mkdir(path.dirname(envPath), { recursive: true });
|
||||
const content = `${claim.secrets
|
||||
.map((secret) => `${secret.name}=${JSON.stringify(secret.value)}`)
|
||||
@@ -1503,7 +1499,7 @@ export const listWorkspaceTree = async (jobId: string) => {
|
||||
|
||||
export const readWorkspaceFile = async (jobId: string, filePath: string) => {
|
||||
const workspace = resolveWorkspace(jobId);
|
||||
const target = safeWorkspacePath(workspace.repoDir, filePath);
|
||||
const target = await assertContainedRealPath(workspace.repoDir, filePath);
|
||||
return await readFile(target, 'utf8');
|
||||
};
|
||||
|
||||
@@ -1513,7 +1509,9 @@ export const writeWorkspaceFile = async (
|
||||
content: string,
|
||||
) => {
|
||||
const workspace = resolveWorkspace(jobId);
|
||||
const target = safeWorkspacePath(workspace.repoDir, filePath);
|
||||
const target = await assertContainedRealPath(workspace.repoDir, filePath, {
|
||||
forWrite: true,
|
||||
});
|
||||
await mkdir(path.dirname(target), { recursive: true });
|
||||
await writeFile(target, content);
|
||||
const diff = await getWorktreeDiff(workspace.repoDir, workspace.redact);
|
||||
|
||||
Reference in New Issue
Block a user