Store Twitch and Discord secrets as versioned safeStorage ciphertext behind explicit trusted IPC. Migrate legacy JSON transactionally into authoritative SQLite config and ordered queue repositories with rollback-safe markers and sanitized backups. Redact config exports and update renderer and release-harness contracts for secret-free config responses.
175 lines
6.3 KiB
JavaScript
175 lines
6.3 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const ROOT = path.resolve(__dirname, '..');
|
|
const HELPER_FILE = path.join(__dirname, 'e2e-test-environment.js');
|
|
const SMOKE_FILES = [
|
|
'scripts/smoke-test.js',
|
|
'scripts/smoke-test-template-guide.js',
|
|
'scripts/smoke-test-full.js',
|
|
'scripts/smoke-test-settings-autosave.js',
|
|
'scripts/smoke-test-workspace-ui.js'
|
|
];
|
|
|
|
function inspectSources() {
|
|
const failures = [];
|
|
|
|
if (!fs.existsSync(HELPER_FILE)) {
|
|
failures.push('Missing scripts/e2e-test-environment.js');
|
|
}
|
|
|
|
for (const relativePath of SMOKE_FILES) {
|
|
const source = fs.readFileSync(path.join(ROOT, relativePath), 'utf8');
|
|
const requiredPatterns = [
|
|
["require('./e2e-test-environment')", 'shared isolation helper import'],
|
|
['createE2eEnvironment(', 'isolated test root creation'],
|
|
['getElectronLaunchOptions(', 'isolated Electron launch options'],
|
|
['verifyE2eIsolation(', 'runtime isolation verification'],
|
|
['installOfflineFixtures(', 'offline IPC fixtures'],
|
|
['cleanupE2eEnvironment(', 'guaranteed isolated root cleanup']
|
|
];
|
|
|
|
for (const [pattern, label] of requiredPatterns) {
|
|
if (!source.includes(pattern)) {
|
|
failures.push(`${relativePath}: missing ${label}`);
|
|
}
|
|
}
|
|
|
|
if (/process\.exit\s*\(/.test(source)) {
|
|
failures.push(`${relativePath}: process.exit bypasses cleanup`);
|
|
}
|
|
|
|
if (/electron\.launch\s*\(\s*\{/.test(source)) {
|
|
failures.push(`${relativePath}: raw electron.launch options bypass shared isolation`);
|
|
}
|
|
}
|
|
|
|
const fullSource = fs.readFileSync(path.join(ROOT, 'scripts/smoke-test-full.js'), 'utf8');
|
|
const forbiddenFullPatterns = [
|
|
['backupFile(', 'real-file backup path'],
|
|
['restoreFile(', 'real-file restore path'],
|
|
["path.join(process.cwd(), 'tmp_e2e_full')", 'project-local media path'],
|
|
["process.env.PROGRAMDATA || 'C:\\\\ProgramData'", 'ambient ProgramData path']
|
|
];
|
|
|
|
for (const [pattern, label] of forbiddenFullPatterns) {
|
|
if (fullSource.includes(pattern)) {
|
|
failures.push(`scripts/smoke-test-full.js: contains ${label}`);
|
|
}
|
|
}
|
|
|
|
return failures;
|
|
}
|
|
|
|
function inspectHelper() {
|
|
if (!fs.existsSync(HELPER_FILE)) {
|
|
return [];
|
|
}
|
|
|
|
const failures = [];
|
|
const {
|
|
createE2eEnvironment,
|
|
getElectronLaunchOptions,
|
|
readE2eConfig,
|
|
cleanupE2eEnvironment
|
|
} = require(HELPER_FILE);
|
|
const environment = createE2eEnvironment('isolation-contract');
|
|
|
|
try {
|
|
const expectedDirectories = [
|
|
environment.rootDir,
|
|
environment.programDataDir,
|
|
environment.userDataDir,
|
|
environment.downloadsDir,
|
|
environment.appDataDir
|
|
];
|
|
|
|
for (const directory of expectedDirectories) {
|
|
if (!fs.statSync(directory).isDirectory()) {
|
|
failures.push(`Helper did not create directory: ${directory}`);
|
|
}
|
|
}
|
|
|
|
const config = JSON.parse(fs.readFileSync(environment.configFile, 'utf8'));
|
|
const queue = JSON.parse(fs.readFileSync(environment.queueFile, 'utf8'));
|
|
const launch = getElectronLaunchOptions(environment);
|
|
|
|
if (path.resolve(config.download_path) !== path.resolve(environment.downloadsDir)) {
|
|
failures.push('Seed config download_path is outside the isolated downloads directory');
|
|
}
|
|
if (!Array.isArray(config.streamers) || config.streamers.length !== 0) {
|
|
failures.push('Seed config contains streamers');
|
|
}
|
|
if (!Array.isArray(config.auto_record_streamers) || config.auto_record_streamers.length !== 0) {
|
|
failures.push('Seed config enables auto recording');
|
|
}
|
|
if (!Array.isArray(config.auto_vod_download_streamers) || config.auto_vod_download_streamers.length !== 0) {
|
|
failures.push('Seed config enables automatic VOD downloads');
|
|
}
|
|
if (config.auto_resume_queue_on_startup !== false || config.auto_resume_live_recording !== false) {
|
|
failures.push('Seed config enables automatic resume behavior');
|
|
}
|
|
if (config.auto_cleanup_enabled !== false) {
|
|
failures.push('Seed config enables automatic cleanup');
|
|
}
|
|
if ('client_secret' in config || 'discord_webhook_url' in config) {
|
|
failures.push('Seed config contains secret fields');
|
|
}
|
|
const Database = require('better-sqlite3');
|
|
const database = new Database(path.join(environment.appDataDir, 'app.db'));
|
|
database.exec('CREATE TABLE config_kv (key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at INTEGER NOT NULL)');
|
|
database.prepare('INSERT INTO config_kv(key, value, updated_at) VALUES (?, ?, ?)').run('language', JSON.stringify('sqlite'), 1);
|
|
database.close();
|
|
if (readE2eConfig(environment).language !== 'sqlite') {
|
|
failures.push('Helper did not read authoritative SQLite config');
|
|
}
|
|
if (!Array.isArray(queue) || queue.length !== 0) {
|
|
failures.push('Seed queue is not empty');
|
|
}
|
|
if (path.resolve(launch.env.PROGRAMDATA) !== path.resolve(environment.programDataDir)) {
|
|
failures.push('Electron launch options do not isolate PROGRAMDATA');
|
|
}
|
|
if (launch.args[0] !== `--user-data-dir=${environment.userDataDir}` || launch.args.at(-1) !== '.') {
|
|
failures.push('Electron launch options do not place the isolated userData switch before the app path');
|
|
}
|
|
if (!String(launch.args[1] || '').startsWith('--proxy-server=http://127.0.0.1:')) {
|
|
failures.push('Electron launch options do not install the Chromium offline proxy guard');
|
|
}
|
|
if (launch.env.HTTP_PROXY !== launch.env.HTTPS_PROXY || launch.env.HTTP_PROXY !== launch.env.ALL_PROXY) {
|
|
failures.push('Electron launch options do not install consistent Node proxy guards');
|
|
}
|
|
if (!String(launch.env.HTTP_PROXY || '').startsWith('http://127.0.0.1:')) {
|
|
failures.push('Electron launch options do not route Node HTTP clients to an offline loopback proxy');
|
|
}
|
|
if (launch.env.NO_PROXY !== '') {
|
|
failures.push('Electron launch options allow proxy bypasses');
|
|
}
|
|
} finally {
|
|
cleanupE2eEnvironment(environment);
|
|
}
|
|
|
|
if (fs.existsSync(environment.rootDir)) {
|
|
failures.push('Helper cleanup left the isolated root on disk');
|
|
}
|
|
|
|
return failures;
|
|
}
|
|
|
|
function run() {
|
|
const failures = [...inspectSources(), ...inspectHelper()];
|
|
const summary = {
|
|
files: SMOKE_FILES,
|
|
failures
|
|
};
|
|
|
|
console.log(JSON.stringify(summary, null, 2));
|
|
process.exitCode = failures.length === 0 ? 0 : 1;
|
|
}
|
|
|
|
try {
|
|
run();
|
|
} catch (error) {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
}
|