Skip to content
Open
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
22 changes: 22 additions & 0 deletions apps/vscode/src/providers/ProjectProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,28 @@ export class ProjectProvider implements vscode.CustomTextEditorProvider {
panel.webview.postMessage({ type: 'pasteContent', text });
} else if (msg?.type === 'copyText' && typeof msg.text === 'string') {
await vscode.env.clipboard.writeText(msg.text);
} else if (msg?.type === 'requestFileDialog') {
// The embedded app's "Browse" button can't open an OS file picker from
// inside the sandboxed iframe, so it posts {type:'requestFileDialog'} up to
// the bridge, which forwards it here. Open the native dialog on the host,
// read the chosen files, and post them back as {type:'nativeFilesSelected'};
// the bridge relays that into the iframe. Buffers go as number[] so they
// survive webview message serialization.
try {
const uris = await vscode.window.showOpenDialog({ canSelectMany: true, openLabel: 'Select' });
if (!uris || uris.length === 0) return;
const files = await Promise.all(
uris.map(async (uri) => ({
name: uri.path.split('/').pop() || 'file',
type: '',
lastModified: Date.now(),
buffer: Array.from(await vscode.workspace.fs.readFile(uri)),
}))
);
panel.webview.postMessage({ type: 'nativeFilesSelected', files });
} catch (error) {
this.logger.error(`[ProjectProvider] Native file dialog failed: ${error}`);
}
Comment on lines +861 to +882

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate native file-dialog bridging to trusted iframe origins.

At Line 861, the host accepts requestFileDialog solely by message type. In this flow, openLink() can embed arbitrary URLs, so any embedded origin can request local file selection and receive file bytes back through nativeFilesSelected. This expands local-file exfiltration capability beyond the Dropper origin.

Suggested hardening
@@
-		panel.webview.onDidReceiveMessage(async (msg) => {
+		const trustedOrigin = (() => {
+			try {
+				return new URL(url).origin;
+			} catch {
+				return '';
+			}
+		})();
+		panel.webview.onDidReceiveMessage(async (msg) => {
@@
-			} else if (msg?.type === 'requestFileDialog') {
+			} else if (msg?.type === 'requestFileDialog') {
+				if (msg?.origin !== trustedOrigin) {
+					this.logger.error(`[ProjectProvider] Blocked requestFileDialog from untrusted origin: ${String(msg?.origin)}`);
+					return;
+				}
@@

And in the inline bridge script where iframe messages are forwarded:

- if (event.data.type === 'requestFileDialog') vscode.postMessage({ type: 'requestFileDialog' });
+ if (event.data.type === 'requestFileDialog') vscode.postMessage({ type: 'requestFileDialog', origin: event.origin });

As per coding guidelines, apply repository ESLint/Prettier conventions when introducing this guard logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/vscode/src/providers/ProjectProvider.ts` around lines 861 - 882, The
host currently honors msg.type === 'requestFileDialog' from any iframe and
returns local file bytes via panel.webview.postMessage, allowing arbitrary
embedded origins (via openLink()) to trigger file selection and exfiltrate
files; update ProjectProvider's message handler to validate the message origin
against a whitelist of trusted iframe origins before handling
'requestFileDialog' (and similarly guard forwarding code in the inline bridge
script), e.g., check the source/origin of the incoming webview message or an
allowlist stored on the ProjectProvider instance and only call
vscode.window.showOpenDialog and
panel.webview.postMessage({type:'nativeFilesSelected',...}) when the origin
matches a trusted entry; follow repository ESLint/Prettier conventions when
adding the guard and reuse existing symbols like openLink(), panel.webview, and
the requestFileDialog/nativeFilesSelected message types.

Source: Coding guidelines

}
});
}
Expand Down
Loading