Skip to content

Commit ef28255

Browse files
committed
fix(electron): allow cross-origin XHR/fetch to asset:// scheme
The asset:// scheme was registered with only supportFetchAPI+stream, which caused Chromium to block XHR/fetch from the phtauri://localhost app origin to asset://localhost/... with a CORS error. Extensions loaded from appLocalData/assets/ failed to load their requirejs-config, keymap, snippets, and HTML panels. Add standard+secure+corsEnabled to the scheme privileges (matching phtauri and Tauri's wry asset protocol) and set Access-Control-Allow-Origin:* on responses so cross-origin reads from the app are allowed.
1 parent c0f763f commit ef28255

1 file changed

Lines changed: 16 additions & 6 deletions

File tree

src-electron/main.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,17 @@ protocol.registerSchemesAsPrivileged([
4040
}
4141
},
4242
{
43-
// asset:// is for serving static files only - minimal privileges to match Tauri's security posture
44-
// No standard/secure/corsEnabled - just enough for fetch() to read local assets
43+
// asset:// serves static files (extensions, themes, user assets) from appLocalData/assets/.
44+
// Needs standard+secure+corsEnabled so the phtauri:// app origin can XHR/fetch these files
45+
// (RequireJS text plugin, $.getJSON for requirejs-config.json/keymap.json/snippets.json, etc.).
46+
// Without these privileges Chromium blocks cross-origin requests to asset:// with a CORS error.
47+
// This matches Tauri's wry asset protocol, which is also CORS-enabled.
4548
scheme: 'asset',
4649
privileges: {
50+
standard: true,
51+
secure: true,
4752
supportFetchAPI: true,
53+
corsEnabled: true,
4854
stream: true
4955
}
5056
}
@@ -326,7 +332,7 @@ app.whenReady().then(async () => {
326332
const appDataDir = getAppDataDir();
327333
const assetsDir = path.join(appDataDir, 'assets');
328334

329-
protocol.handle('asset', (request) => {
335+
protocol.handle('asset', async (request) => {
330336
try {
331337
const url = new URL(request.url);
332338
// Decode the path from URL encoding
@@ -341,13 +347,17 @@ app.whenReady().then(async () => {
341347
// Security: Ensure path is under assets directory (prevent directory traversal)
342348
if (!normalizedRequested.startsWith(normalizedAssetsDir)) {
343349
console.error('Asset access denied - path not under assets dir:', requestedPath);
344-
return new Response('Access denied', { status: 403 });
350+
return new Response('Access denied', { status: 403, headers: { 'Access-Control-Allow-Origin': '*' } });
345351
}
346352

347-
return net.fetch(`file://${normalizedRequested}`);
353+
const response = await net.fetch(`file://${normalizedRequested}`);
354+
// Mirror Tauri's wry asset protocol: allow the phtauri:// app origin to XHR/fetch these files.
355+
const headers = new Headers(response.headers);
356+
headers.set('Access-Control-Allow-Origin', '*');
357+
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
348358
} catch (err) {
349359
console.error('Asset protocol error:', err);
350-
return new Response('Not found', { status: 404 });
360+
return new Response('Not found', { status: 404, headers: { 'Access-Control-Allow-Origin': '*' } });
351361
}
352362
});
353363

0 commit comments

Comments
 (0)