feat: Security
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
collectPluginReadRoots,
|
||||
fileUrlToPath,
|
||||
pluginFileParentDir
|
||||
} from './plugin-local-file.rules';
|
||||
|
||||
describe('plugin-local-file.rules', () => {
|
||||
it('resolves linux file URLs to absolute paths', () => {
|
||||
expect(fileUrlToPath('file:///home/ludde/Desktop/TestPlugin/plugin-source.json'))
|
||||
.toBe('/home/ludde/Desktop/TestPlugin/plugin-source.json');
|
||||
});
|
||||
|
||||
it('collects plugin read roots from source and entrypoint URLs', () => {
|
||||
expect(collectPluginReadRoots(
|
||||
'file:///home/ludde/Desktop/TestPlugin/plugin-source.json',
|
||||
'file:///home/ludde/Desktop/TestPlugin/dist/main.js'
|
||||
)).toEqual([
|
||||
'/home/ludde/Desktop/TestPlugin',
|
||||
'/home/ludde/Desktop/TestPlugin/dist'
|
||||
]);
|
||||
});
|
||||
|
||||
it('treats directory file URLs as their own read roots', () => {
|
||||
expect(collectPluginReadRoots('file:///home/ludde/Desktop/TestPlugin/')).toEqual([
|
||||
'/home/ludde/Desktop/TestPlugin'
|
||||
]);
|
||||
expect(collectPluginReadRoots('file:///home/ludde/Desktop/TestPlugin')).toEqual([
|
||||
'/home/ludde/Desktop/TestPlugin'
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { ElectronApi } from '../../../../core/platform/electron/electron-api.models';
|
||||
|
||||
export function pluginFileParentDir(filePath: string): string {
|
||||
const normalized = filePath.replace(/\\/g, '/').replace(/\/+$/, '');
|
||||
const index = normalized.lastIndexOf('/');
|
||||
|
||||
return index > 0 ? normalized.slice(0, index) : normalized;
|
||||
}
|
||||
|
||||
export function pluginReadRootForFileUrl(fileUrl: string): string {
|
||||
const filePath = fileUrlToPath(fileUrl).replace(/\\/g, '/').replace(/\/+$/, '');
|
||||
const basename = filePath.split('/').pop() ?? '';
|
||||
|
||||
if (fileUrl.endsWith('/') || !basename.includes('.')) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
return pluginFileParentDir(filePath);
|
||||
}
|
||||
|
||||
export function collectPluginReadRoots(...fileUrls: Array<string | undefined>): string[] {
|
||||
const roots = new Set<string>();
|
||||
|
||||
for (const fileUrl of fileUrls) {
|
||||
if (!fileUrl?.startsWith('file://')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
roots.add(pluginReadRootForFileUrl(fileUrl));
|
||||
}
|
||||
|
||||
return [...roots];
|
||||
}
|
||||
|
||||
export async function grantPluginReadRoots(
|
||||
api: Pick<ElectronApi, 'grantPluginReadRoot'> | null | undefined,
|
||||
...fileUrls: Array<string | undefined>
|
||||
): Promise<void> {
|
||||
if (!api?.grantPluginReadRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
const roots = collectPluginReadRoots(...fileUrls);
|
||||
|
||||
for (const root of roots) {
|
||||
await api.grantPluginReadRoot(root);
|
||||
}
|
||||
}
|
||||
|
||||
export function fileUrlToPath(fileUrl: string): string {
|
||||
const url = new URL(fileUrl);
|
||||
const decodedPath = decodeURIComponent(url.pathname);
|
||||
|
||||
if (/^\/[A-Za-z]:\//.test(decodedPath)) {
|
||||
return decodedPath.slice(1).replace(/\//g, '\\');
|
||||
}
|
||||
|
||||
return decodedPath;
|
||||
}
|
||||
Reference in New Issue
Block a user