46 lines
2.1 KiB
JavaScript
46 lines
2.1 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
|
|
const desktopDir = path.resolve(__dirname, '..');
|
|
const mainSource = fs.readFileSync(path.join(desktopDir, 'main.cjs'), 'utf8');
|
|
const preloadSource = fs.readFileSync(path.join(desktopDir, 'preload.cjs'), 'utf8');
|
|
|
|
test('validates only safe http(s) backend URLs', () => {
|
|
const { validateBackendUrl } = require(path.join(desktopDir, 'url-validation.js'));
|
|
for (const value of ['https://api.example.com', 'http://127.0.0.1:8000/api/v1']) {
|
|
assert.equal(validateBackendUrl(value).valid, true, value);
|
|
}
|
|
for (const value of [
|
|
'', 'ftp://api.example.com', 'javascript:alert(1)', 'https://user:pass@api.example.com',
|
|
'https://api.example.com/?token=secret', 'https://api.example.com/#secret',
|
|
'https://', 'not a url'
|
|
]) {
|
|
assert.equal(validateBackendUrl(value).valid, false, value);
|
|
}
|
|
});
|
|
|
|
test('main process uses hardened BrowserWindow defaults', () => {
|
|
assert.match(mainSource, /preload:.*preload\.cjs/);
|
|
assert.match(mainSource, /contextIsolation:\s*true/);
|
|
assert.match(mainSource, /sandbox:\s*true/);
|
|
assert.match(mainSource, /nodeIntegration:\s*false/);
|
|
assert.match(mainSource, /setWindowOpenHandler/);
|
|
assert.match(mainSource, /shell\.openExternal/);
|
|
});
|
|
|
|
test('preload exposes a narrow non-secret API', () => {
|
|
assert.match(preloadSource, /contextBridge\.exposeInMainWorld\(['"]prospectDesktop['"]/);
|
|
assert.match(preloadSource, /getBackendUrl/);
|
|
assert.match(preloadSource, /setBackendUrl/);
|
|
assert.match(preloadSource, /clearSession/);
|
|
assert.doesNotMatch(preloadSource, /process\.env|apiKey|password|token/i);
|
|
});
|
|
|
|
test('desktop source contains no embedded credentials or API keys', () => {
|
|
const files = fs.readdirSync(desktopDir).filter((file) => file.endsWith('.js') || file.endsWith('.html'));
|
|
const source = files.map((file) => fs.readFileSync(path.join(desktopDir, file), 'utf8')).join('\n');
|
|
assert.doesNotMatch(source, /(sk-[A-Za-z0-9]|api[_-]?key\s*[:=]|password\s*[:=]|Bearer\s+)/i);
|
|
});
|