Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ describe('CaptureScreenContextTool', () => {
},
);

it('reports the dedicated symlink error on every platform', async () => {
const target = await captureFile();
const link = join(target.directory, 'linked.png');
await symlink(target.path, link);
const tool = new CaptureScreenContextTool(
async () => ({
appName: 'Finder',
accessibilityText: '',
screenshotPath: link,
}),
target.directory,
);

const result = await tool.build({}).execute(new AbortController().signal);

// The exact message pins the explicit lstat guard: without it, win32
// reads through the link (no error at all) while POSIX falls back to
// O_NOFOLLOW's generic ELOOP message.
expect(result.error?.message).toBe(
'Host returned a symbolic link screenshot path.',
);
});

it('rejects a screenshot outside the Host private directory', async () => {
const outside = await captureFile();
const allowed = await mkdtemp(join(tmpdir(), 'capture-screen-allowed-'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { constants } from 'node:fs';
import { open, unlink } from 'node:fs/promises';
import { lstat, open, unlink } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import {
Expand Down Expand Up @@ -52,6 +52,11 @@ function resolvePrivatePngPath(path: string, captureDirectory: string): string {
}

async function readPrivatePng(path: string): Promise<Buffer> {
// Windows silently ignores O_NOFOLLOW, so a symlinked screenshot path
// would be followed and read on win32. Probe the link itself first.
if ((await lstat(path)).isSymbolicLink()) {
throw new Error('Host returned a symbolic link screenshot path.');
}
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
try {
const stat = await handle.stat();
Expand Down
Loading