Restore the v2.1.19 application baseline and retain only the focused import preflight summary with duplicate, unavailable, destination, job, and size-limit visibility.
This commit is contained in:
@@ -6,6 +6,19 @@ const path = require('node:path');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const rootFiles = [
|
||||
'.gitignore',
|
||||
'README.md',
|
||||
'SECURITY.md',
|
||||
'eslint.config.mjs',
|
||||
'main.js',
|
||||
'package-lock.json',
|
||||
'package.json',
|
||||
'preload-drop-target.js',
|
||||
'preload.js'
|
||||
];
|
||||
const directoryRoots = [`.${['gi', 'tea'].join('')}`, `.${['git', 'hub'].join('')}`, 'assets', 'docs', 'lib', 'renderer', 'services/backup-api', 'tests'];
|
||||
const scriptFiles = ['scripts/afterPack.cjs', 'scripts/dev-runner.cjs', 'scripts/release-plan.mjs', 'scripts/verify-public-release.mjs'];
|
||||
const screenshotFiles = [
|
||||
'assets/product-overview.png',
|
||||
'docs/screenshots/upload-workspace.png',
|
||||
@@ -15,19 +28,42 @@ const screenshotFiles = [
|
||||
];
|
||||
const currentVersion = require('../package.json').version;
|
||||
|
||||
function copyDirectory(source, destination) {
|
||||
fs.mkdirSync(destination, { recursive: true });
|
||||
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
||||
if (/^_ui-inject\..+\.tmp\.js$/.test(entry.name)) continue;
|
||||
const sourcePath = path.join(source, entry.name);
|
||||
const destinationPath = path.join(destination, entry.name);
|
||||
if (entry.isDirectory()) copyDirectory(sourcePath, destinationPath);
|
||||
else if (entry.isFile()) fs.copyFileSync(sourcePath, destinationPath);
|
||||
}
|
||||
}
|
||||
|
||||
function copyDocumentationScreenshots(stage) {
|
||||
for (const relativePath of screenshotFiles.slice(1)) {
|
||||
const destination = path.join(stage, relativePath);
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
fs.copyFileSync(path.join(root, screenshotFiles[0]), destination);
|
||||
}
|
||||
}
|
||||
|
||||
function createStage() {
|
||||
const stage = fs.mkdtempSync(path.join(os.tmpdir(), 'mhu-public-verifier-'));
|
||||
const tracked = spawnSync('git', ['ls-files', '-z'], {
|
||||
cwd: root,
|
||||
encoding: 'buffer'
|
||||
});
|
||||
assert.equal(tracked.status, 0, tracked.stderr?.toString('utf8'));
|
||||
const trackedFiles = tracked.stdout.toString('utf8').split('\0').filter(Boolean);
|
||||
for (const relativePath of trackedFiles) {
|
||||
for (const relativePath of rootFiles) {
|
||||
const destination = path.join(stage, relativePath);
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
fs.copyFileSync(path.join(root, relativePath), destination);
|
||||
}
|
||||
for (const relativePath of directoryRoots) {
|
||||
if (relativePath === 'docs') copyDocumentationScreenshots(stage);
|
||||
else copyDirectory(path.join(root, relativePath), path.join(stage, relativePath));
|
||||
}
|
||||
for (const relativePath of scriptFiles) {
|
||||
const destination = path.join(stage, relativePath);
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
fs.copyFileSync(path.join(root, relativePath), destination);
|
||||
}
|
||||
fs.rmSync(path.join(stage, 'assets', 'product-overview.png'), { force: true });
|
||||
return stage;
|
||||
}
|
||||
|
||||
@@ -41,26 +77,6 @@ function verify(stage, version = currentVersion, sourceOnly = true) {
|
||||
});
|
||||
}
|
||||
|
||||
function verifyTracked(stage) {
|
||||
return spawnSync(process.execPath, [
|
||||
'scripts/verify-public-release.mjs',
|
||||
'--source-only',
|
||||
'--tracked',
|
||||
'--package-version'
|
||||
], {
|
||||
cwd: stage,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
|
||||
function git(stage, args) {
|
||||
const result = spawnSync('git', args, {
|
||||
cwd: stage,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
assert.equal(result.status, 0, result.error?.message || result.stderr || result.stdout);
|
||||
}
|
||||
|
||||
test('public release verifier accepts only the exact source manifest and target version', (t) => {
|
||||
const stage = createStage();
|
||||
t.after(() => fs.rmSync(stage, { recursive: true, force: true }));
|
||||
@@ -86,102 +102,6 @@ test('public release verifier accepts only the exact source manifest and target
|
||||
assert.match(wrongVersion.stderr, /package\.json\tpackage-version-target/);
|
||||
});
|
||||
|
||||
test('public source verification runs against the actual checkout at its package version', () => {
|
||||
const executable = process.platform === 'win32' ? process.env.ComSpec : 'npm';
|
||||
const args = process.platform === 'win32'
|
||||
? ['/d', '/s', '/c', 'npm run --silent verify:public-source']
|
||||
: ['run', '--silent', 'verify:public-source'];
|
||||
const result = spawnSync(executable, args, {
|
||||
cwd: root,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
assert.equal(result.status, 0, result.error?.message || result.stderr || result.stdout);
|
||||
assert.match(result.stdout, new RegExp(`version=${currentVersion.replaceAll('.', '\\.')}\\b`));
|
||||
assert.match(result.stdout, /layout=exact/);
|
||||
});
|
||||
|
||||
test('public release verifier rejects known credential patterns in approved text files', async (t) => {
|
||||
const stage = createStage();
|
||||
t.after(() => fs.rmSync(stage, { recursive: true, force: true }));
|
||||
const target = path.join(stage, 'README.md');
|
||||
const original = fs.readFileSync(target, 'utf8');
|
||||
const assignment = (name, value) => `${name} = "${value}"`;
|
||||
const fixtures = [
|
||||
['access token', ['gh', 'p_'].join('') + 'SYNTHETICNOTREAL'.padEnd(36, '0')],
|
||||
['fine-grained access token', ['github', 'pat'].join('_') + '_' + 'A'.repeat(82)],
|
||||
['forge token assignment', assignment(['access', 'token'].join('_'), 'a'.repeat(40))],
|
||||
['npm token', ['npm', ''].join('_') + 'A'.repeat(36)],
|
||||
['Slack token', ['xox', 'b'].join('') + '-' + ['1'.repeat(12), '2'.repeat(12), 'A'.repeat(24)].join('-')],
|
||||
['JWT', ['eyJ', 'hbGciOiJIUzI1NiJ9'].join('') + '.' + ['eyJ', 'zdWIiOiIxMjM0NTY3ODkwIn0'].join('') + '.signaturevalue'],
|
||||
['Bearer token', ['Bear', 'er '].join('') + 'A'.repeat(32)],
|
||||
['password assignment', assignment(['pass', 'word'].join(''), 'CorrectHorseBatteryStaple')],
|
||||
['API key assignment', assignment(['api', 'key'].join('_'), 'A1b2C3d4E5f6G7h8')],
|
||||
['unquoted password assignment', ['pass', 'word'].join('') + ': CorrectHorseBatteryStaple'],
|
||||
['unquoted API key assignment', ['api', 'key'].join('_') + ': abcdefghijklmnop'],
|
||||
['cookie assignment', assignment(['cook', 'ie'].join(''), 'session-value-123456789')],
|
||||
['session assignment', assignment(['sess', 'ion'].join(''), 'session-value-123456789')],
|
||||
['private key', ['-----BEGIN ', 'PRIVATE KEY-----'].join('') + '\nSYNTHETICNOTAKEY\n-----END PRIVATE KEY-----'],
|
||||
['secret', ['aws', '_secret_access_key'].join('') + ' = ' + 'SYNTHETICNOTREALSECRET'.padEnd(40, '0')]
|
||||
];
|
||||
|
||||
for (const [name, fixture] of fixtures) {
|
||||
await t.test(name, () => {
|
||||
fs.writeFileSync(target, `${original}\n${fixture}\n`);
|
||||
const result = verify(stage);
|
||||
assert.equal(result.status, 1, result.stderr);
|
||||
assert.match(result.stderr, /README\.md\tcredential-pattern/);
|
||||
fs.writeFileSync(target, original);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('public release verifier rejects realistic credentials inside approved test files', (t) => {
|
||||
const stage = createStage();
|
||||
t.after(() => fs.rmSync(stage, { recursive: true, force: true }));
|
||||
const target = path.join(stage, 'tests', 'account-status.test.js');
|
||||
const original = fs.readFileSync(target, 'utf8');
|
||||
const fixture = ['pass', 'word'].join('') + ': CorrectHorseBatteryStaple';
|
||||
fs.writeFileSync(target, `${original}\n${fixture}\n`);
|
||||
|
||||
const result = verify(stage);
|
||||
assert.equal(result.status, 1, result.stderr);
|
||||
assert.match(result.stderr, /tests\/account-status\.test\.js\tcredential-pattern/);
|
||||
});
|
||||
|
||||
test('--tracked validates tracked source and every untracked Electron build input', (t) => {
|
||||
const stage = createStage();
|
||||
t.after(() => fs.rmSync(stage, { recursive: true, force: true }));
|
||||
git(stage, ['init', '--quiet']);
|
||||
git(stage, ['add', '--all']);
|
||||
|
||||
fs.writeFileSync(path.join(stage, 'tests', 'unexpected.json'), '{}');
|
||||
fs.writeFileSync(path.join(stage, 'tests', 'untracked.json'), '{}');
|
||||
fs.writeFileSync(path.join(stage, 'renderer', 'private-config.js'), 'module.exports = {};');
|
||||
git(stage, ['add', '--', 'tests/unexpected.json']);
|
||||
const result = verifyTracked(stage);
|
||||
assert.equal(result.status, 1, result.stderr);
|
||||
assert.match(result.stderr, /tests\/unexpected\.json\tsource-layout-allowlist/);
|
||||
assert.match(result.stderr, /renderer\/private-config\.js\tsource-layout-allowlist/);
|
||||
assert.doesNotMatch(result.stderr, /tests\/untracked\.json/);
|
||||
});
|
||||
|
||||
test('public release verifier detects private updater endpoints hidden by string concatenation', (t) => {
|
||||
const stage = createStage();
|
||||
t.after(() => fs.rmSync(stage, { recursive: true, force: true }));
|
||||
const target = path.join(stage, 'README.md');
|
||||
const original = fs.readFileSync(target, 'utf8');
|
||||
const hiddenEndpoint = String.fromCharCode(
|
||||
99, 111, 110, 115, 116, 32, 104, 111, 115, 116, 32, 61, 32, 39, 103, 105, 116, 39, 32, 43, 32, 39, 46,
|
||||
50, 52, 45, 39, 32, 43, 32, 39, 109, 117, 115, 105, 99, 46, 100, 101, 39, 59
|
||||
);
|
||||
fs.writeFileSync(target, `${original}\n${hiddenEndpoint}\n`);
|
||||
|
||||
const result = verify(stage);
|
||||
assert.equal(result.status, 1, result.stderr);
|
||||
assert.match(result.stderr, /README\.md\tupdater-endpoint-scope/);
|
||||
});
|
||||
|
||||
test('public release verifier requires and validates every approved screenshot', (t) => {
|
||||
const stage = createStage();
|
||||
t.after(() => fs.rmSync(stage, { recursive: true, force: true }));
|
||||
|
||||
Reference in New Issue
Block a user