mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-08-28 02:44:59 +00:00
fix: close sandbox metadata read race (#1235)
Some checks are pending
CI / Lint & Type Check (push) Waiting to run
CI / Changed Tests (push) Blocked by required conditions
CI / Select Test Scope (push) Waiting to run
CI / Workspace Unit Tests (push) Blocked by required conditions
CI / Critical Path Coverage (push) Blocked by required conditions
CI / Build (push) Waiting to run
CI / Security Audit (push) Waiting to run
Security Gates / CodeQL (push) Waiting to run
Security Gates / Gitleaks (push) Waiting to run
Some checks are pending
CI / Lint & Type Check (push) Waiting to run
CI / Changed Tests (push) Blocked by required conditions
CI / Select Test Scope (push) Waiting to run
CI / Workspace Unit Tests (push) Blocked by required conditions
CI / Critical Path Coverage (push) Blocked by required conditions
CI / Build (push) Waiting to run
CI / Security Audit (push) Waiting to run
Security Gates / CodeQL (push) Waiting to run
Security Gates / Gitleaks (push) Waiting to run
This commit is contained in:
parent
6ab3feb35f
commit
1cdcd6ec60
2 changed files with 50 additions and 7 deletions
|
|
@ -14,6 +14,7 @@ import {
|
|||
type FilesystemSandboxCommandRunner,
|
||||
} from '../services/filesystem-sandbox-service.js';
|
||||
import {
|
||||
inspectGitMetadataRoots,
|
||||
removeRunSandboxDirectory,
|
||||
runSandboxDirectories,
|
||||
} from '../utils/filesystem-sandbox-runtime.js';
|
||||
|
|
@ -139,6 +140,23 @@ function conformantRunner(): FilesystemSandboxCommandRunner {
|
|||
}
|
||||
|
||||
describe('FilesystemSandboxService', () => {
|
||||
it('rejects a symbolic linked-worktree common-directory pointer', async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'veritas-sandbox-git-metadata-'));
|
||||
roots.push(root);
|
||||
const workspace = path.join(root, 'workspace');
|
||||
const gitDirectory = path.join(root, 'git-directory');
|
||||
const commonDirectory = path.join(root, 'common-directory');
|
||||
await fs.mkdir(workspace);
|
||||
await fs.mkdir(gitDirectory);
|
||||
await fs.mkdir(commonDirectory);
|
||||
await fs.writeFile(path.join(workspace, '.git'), `gitdir: ${gitDirectory}\n`, 'utf8');
|
||||
await fs.symlink(commonDirectory, path.join(gitDirectory, 'commondir'));
|
||||
|
||||
await expect(inspectGitMetadataRoots(workspace)).rejects.toThrow(
|
||||
'Filesystem metadata pointer must be a regular file.'
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['darwin', 'seatbelt'],
|
||||
['linux', 'landlock-bubblewrap'],
|
||||
|
|
|
|||
|
|
@ -552,14 +552,39 @@ async function digestFileContents(filePath: string): Promise<string> {
|
|||
}
|
||||
|
||||
async function readBoundedPathFile(filePath: string, maxBytes: number): Promise<string> {
|
||||
const stat = await fs.lstat(filePath);
|
||||
if (!stat.isFile() || stat.isSymbolicLink()) {
|
||||
throw new Error('Filesystem metadata pointer must be a regular file.');
|
||||
let handle: Awaited<ReturnType<typeof fs.open>> | undefined;
|
||||
try {
|
||||
const noFollow = typeof constants.O_NOFOLLOW === 'number' ? constants.O_NOFOLLOW : 0;
|
||||
handle = await fs.open(filePath, constants.O_RDONLY | noFollow);
|
||||
const before = await handle.stat();
|
||||
if (!before.isFile()) {
|
||||
throw new Error('Filesystem metadata pointer must be a regular file.');
|
||||
}
|
||||
if (before.size > maxBytes) {
|
||||
throw new Error(`Filesystem metadata file exceeds ${maxBytes} bytes.`);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(maxBytes + 1);
|
||||
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
||||
const after = await handle.stat();
|
||||
if (
|
||||
bytesRead > maxBytes ||
|
||||
before.dev !== after.dev ||
|
||||
before.ino !== after.ino ||
|
||||
before.size !== after.size ||
|
||||
before.mtimeMs !== after.mtimeMs
|
||||
) {
|
||||
throw new Error('Filesystem metadata file changed while it was being inspected.');
|
||||
}
|
||||
return buffer.subarray(0, bytesRead).toString('utf8');
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ELOOP') {
|
||||
throw new Error('Filesystem metadata pointer must be a regular file.', { cause: error });
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
await handle?.close();
|
||||
}
|
||||
if (stat.size > maxBytes) {
|
||||
throw new Error(`Filesystem metadata file exceeds ${maxBytes} bytes.`);
|
||||
}
|
||||
return fs.readFile(filePath, 'utf8');
|
||||
}
|
||||
|
||||
async function readGitIdentityValue(cwd: string, key: 'user.name' | 'user.email') {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue