jm-tech-power-official/Flash Download
A desktop download manager that opens up to 32 connections against one file and can send each of them out a different proxy route.
Public
Uncategorized
Clone
jm-tech-power-official/flash-download.jmproj
test/secrets.test.js
'use strict';
// Proxy credentials must never reach state.json as plaintext. These tests drive
// Manager with a fake codec (so no Electron/keyring is needed) and assert on the
// actual bytes written to disk.
const assert = require('assert');
const fs = require('fs/promises');
const os = require('os');
const path = require('path');
const { Manager } = require('../src/main/engine/manager');
const { SecretStore } = require('../src/main/engine/secrets');
const tests = [];
const test = (name, fn) => tests.push([name, fn]);
// A reversible "encryption" standing in for safeStorage: good enough to prove
// the split between state.json and the encrypted store, and to prove that what
// lands in credentials.enc is not the readable password.
function fakeCodec({ available = true } = {}) {
return {
available: () => available,
encrypt: (s) => 'enc:' + Buffer.from(s, 'utf8').toString('base64'),
decrypt: (b) => {
if (!String(b).startsWith('enc:')) throw new Error('bad blob');
return Buffer.from(String(b).slice(4), 'base64').toString('utf8');
},
};
}
const readJson = async (p) => JSON.parse(await fs.readFile(p, 'utf8'));
const exists = (p) => fs.access(p).then(() => true, () => false);
async function makeManager(dir, codecOpts) {
const secrets = new SecretStore({ filePath: path.join(dir, 'credentials.enc'), codec: fakeCodec(codecOpts) });
const m = new Manager({ stateDir: dir, defaultDownloadDir: dir, secrets });
await m.load();
return m;
}
const SECRET = 'hunter2-s3cr3t';
const USER = 'alice';
// ---------------------------------------------------------------------------
test('a proxy password never appears in state.json', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'fd-sec-'));
const m = await makeManager(dir);
await m.addRoute({ label: 'p1', type: 'http', host: '127.0.0.1', port: 8080, username: USER, password: SECRET });
await m.shutdown();
const rawState = await fs.readFile(path.join(dir, 'state.json'), 'utf8');
assert.ok(!rawState.includes(SECRET), 'password must not be in state.json');
assert.ok(!rawState.includes(USER), 'username must not be in state.json');
const state = JSON.parse(rawState);
const route = state.routes.find((r) => r.label === 'p1');
assert.ok(route, 'route still recorded');
assert.strictEqual(route.username, undefined, 'no username key in state.json');
assert.strictEqual(route.password, undefined, 'no password key in state.json');
await fs.rm(dir, { recursive: true, force: true });
});
test('credentials.enc holds ciphertext, not the readable secret', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'fd-sec-'));
const m = await makeManager(dir);
await m.addRoute({ label: 'p1', type: 'http', host: '127.0.0.1', port: 8080, username: USER, password: SECRET });
await m.shutdown();
const encPath = path.join(dir, 'credentials.enc');
assert.ok(await exists(encPath), 'credentials.enc written');
const raw = await fs.readFile(encPath, 'utf8');
assert.ok(!raw.includes(SECRET), 'raw secret must not be in the encrypted file');
const blob = Object.values((await readJson(encPath)).secrets)[0];
assert.ok(blob.startsWith('enc:'), 'value is stored as a codec blob');
await fs.rm(dir, { recursive: true, force: true });
});
test('credentials survive a restart and reach the proxy agent', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'fd-sec-'));
const m1 = await makeManager(dir);
const id = await m1.addRoute({ label: 'p1', type: 'socks5', host: '10.0.0.1', port: 1080, username: USER, password: SECRET });
await m1.shutdown();
const m2 = await makeManager(dir); // fresh process
const route = m2.routes.find((r) => r.id === id);
assert.strictEqual(route.username, USER, 'username restored in memory');
assert.strictEqual(route.password, SECRET, 'password restored in memory');
// snapshot (what the renderer sees) exposes only presence, never the secret.
const snap = m2.snapshot().routes.find((r) => r.id === id);
assert.strictEqual(snap.hasAuth, true);
assert.ok(!('password' in snap) && !('username' in snap), 'snapshot hides credentials');
await m2.shutdown();
await fs.rm(dir, { recursive: true, force: true });
});
test('a legacy plaintext state.json is migrated and rewritten clean', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'fd-sec-'));
// Simulate an old install: credentials inline in state.json, no encrypted file.
const legacy = {
v: 1,
settings: {},
routes: [
{ id: 'direct', label: 'direct', type: 'direct', enabled: true, health: 'up' },
{ id: 'old1', label: 'legacy', type: 'http', host: '1.2.3.4', port: 3128, username: USER, password: SECRET, enabled: true, health: 'up' },
],
downloads: [],
};
await fs.writeFile(path.join(dir, 'state.json'), JSON.stringify(legacy, null, 2));
const m = await makeManager(dir); // load() migrates
const rawState = await fs.readFile(path.join(dir, 'state.json'), 'utf8');
assert.ok(!rawState.includes(SECRET), 'legacy password scrubbed from state.json');
assert.ok(await exists(path.join(dir, 'credentials.enc')), 'migrated into encrypted store');
const route = m.routes.find((r) => r.id === 'old1');
assert.strictEqual(route.password, SECRET, 'credential still usable in memory');
await m.shutdown();
// And it is readable again next launch.
const m2 = await makeManager(dir);
assert.strictEqual(m2.routes.find((r) => r.id === 'old1').password, SECRET);
await m2.shutdown();
await fs.rm(dir, { recursive: true, force: true });
});
test('with no keyring, nothing is written as plaintext and nothing persists', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'fd-sec-'));
const m = await makeManager(dir, { available: false });
let warned = false;
m.on('notice', (n) => { if (/password/i.test(n.title)) warned = true; });
await m.addRoute({ label: 'p1', type: 'http', host: '127.0.0.1', port: 8080, username: USER, password: SECRET });
// Usable this session...
assert.strictEqual(m.routes.find((r) => r.label === 'p1').password, SECRET);
assert.ok(warned, 'user is warned that the password will not be saved');
await m.shutdown();
const rawState = await fs.readFile(path.join(dir, 'state.json'), 'utf8');
assert.ok(!rawState.includes(SECRET), 'no plaintext in state.json even without a keyring');
assert.ok(!(await exists(path.join(dir, 'credentials.enc'))), 'no encrypted file when encryption is unavailable');
// Next launch: the credential is gone (session-only), as warned.
const m2 = await makeManager(dir, { available: false });
const route = m2.routes.find((r) => r.label === 'p1');
assert.ok(route, 'route config itself still persists');
assert.ok(!route.password, 'password did not persist');
await m2.shutdown();
await fs.rm(dir, { recursive: true, force: true });
});
test('removing a route drops its stored secret', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'fd-sec-'));
const m = await makeManager(dir);
const id = await m.addRoute({ label: 'p1', type: 'http', host: '127.0.0.1', port: 8080, username: USER, password: SECRET });
await m.removeRoute(id);
await m.shutdown();
const enc = await readJson(path.join(dir, 'credentials.enc'));
assert.deepStrictEqual(enc.secrets, {}, 'removed route leaves no secret behind');
await fs.rm(dir, { recursive: true, force: true });
});
// ---------------------------------------------------------------------------
(async () => {
let failed = 0;
for (const [name, fn] of tests) {
const t0 = Date.now();
try {
await fn();
console.log(` \x1b[32mpass\x1b[0m ${name} \x1b[90m(${Date.now() - t0}ms)\x1b[0m`);
} catch (err) {
failed += 1;
console.log(` \x1b[31mFAIL\x1b[0m ${name}\n ${err.message}`);
}
}
console.log(failed ? `\n${failed} of ${tests.length} failed` : `\n${tests.length} passed`);
process.exit(failed ? 1 : 0);
})();