Updater 3.4 – Lifecycle Hardening #28
@@ -0,0 +1,86 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve, join } from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { evaluatePayloadGuard } from './updater-lifecycle-3.4.mjs';
|
||||
|
||||
const sha256 = value => createHash('sha256').update(value).digest('hex');
|
||||
|
||||
function git(repo, args, { allowFailure = false, encoding = 'utf8' } = {}) {
|
||||
const result = spawnSync('git', ['-C', repo, ...args], { encoding, maxBuffer: 64 * 1024 * 1024 });
|
||||
if (!allowFailure && result.status !== 0) {
|
||||
throw new Error(`git ${args.join(' ')} fehlgeschlagen: ${(result.stderr || result.stdout || '').trim()}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function readGitFile(repo, commit, path) {
|
||||
const result = git(repo, ['show', `${commit}:${path}`], { allowFailure: true, encoding: null });
|
||||
if (result.status !== 0) return null;
|
||||
return Buffer.from(result.stdout);
|
||||
}
|
||||
|
||||
function normalize(path) {
|
||||
return String(path || '').replaceAll('\\', '/').replace(/^\.\//, '');
|
||||
}
|
||||
|
||||
export function inspectPayload({ packageRoot, repositoryRoot, baseCommit, currentMainCommit }) {
|
||||
const source = resolve(packageRoot);
|
||||
const repo = resolve(repositoryRoot);
|
||||
const manifest = JSON.parse(readFileSync(join(source, 'release-manifest.json'), 'utf8'));
|
||||
const baseExists = git(repo, ['cat-file', '-e', `${baseCommit}^{commit}`], { allowFailure: true }).status === 0;
|
||||
const mainExists = git(repo, ['cat-file', '-e', `${currentMainCommit}^{commit}`], { allowFailure: true }).status === 0;
|
||||
if (!baseExists || !mainExists) {
|
||||
return {
|
||||
...evaluatePayloadGuard({ baseCommit: baseExists ? baseCommit : '', currentMainCommit: mainExists ? currentMainCommit : '', baseIsAncestor: false }),
|
||||
payloadChangedPaths: [],
|
||||
mainChangedPaths: [],
|
||||
};
|
||||
}
|
||||
const baseIsAncestor = git(repo, ['merge-base', '--is-ancestor', baseCommit, currentMainCommit], { allowFailure: true }).status === 0;
|
||||
const mainChangedPaths = new Set(
|
||||
String(git(repo, ['diff', '--name-only', `${baseCommit}..${currentMainCommit}`, '--']).stdout || '')
|
||||
.split(/\r?\n/).map(normalize).filter(Boolean),
|
||||
);
|
||||
const payloadChangedPaths = new Set();
|
||||
const desired = new Map();
|
||||
for (const entry of manifest.files || []) {
|
||||
const path = normalize(entry.path);
|
||||
const baseBytes = readGitFile(repo, baseCommit, path);
|
||||
const baseHash = baseBytes === null ? null : sha256(baseBytes);
|
||||
if (baseHash !== String(entry.sha256)) payloadChangedPaths.add(path);
|
||||
desired.set(path, String(entry.sha256));
|
||||
}
|
||||
for (const value of manifest.removedFiles || []) {
|
||||
const path = normalize(value);
|
||||
if (readGitFile(repo, baseCommit, path) !== null) payloadChangedPaths.add(path);
|
||||
desired.set(path, null);
|
||||
}
|
||||
const conflictingPaths = [];
|
||||
for (const path of payloadChangedPaths) {
|
||||
if (!mainChangedPaths.has(path)) continue;
|
||||
const currentBytes = readGitFile(repo, currentMainCommit, path);
|
||||
const currentHash = currentBytes === null ? null : sha256(currentBytes);
|
||||
if (currentHash !== desired.get(path)) conflictingPaths.push(path);
|
||||
}
|
||||
return {
|
||||
...evaluatePayloadGuard({ baseCommit, currentMainCommit, baseIsAncestor, conflictingPaths }),
|
||||
payloadChangedPaths: [...payloadChangedPaths].sort(),
|
||||
mainChangedPaths: [...mainChangedPaths].sort(),
|
||||
};
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === new URL(`file://${process.argv[1].replaceAll('\\', '/')}`).href) {
|
||||
const [packageRoot, repositoryRoot, baseCommit, currentMainCommit] = process.argv.slice(2);
|
||||
try {
|
||||
if (!packageRoot || !repositoryRoot || !baseCommit || !currentMainCommit) {
|
||||
throw new Error('Aufruf: node check-release-payload-3.4.mjs <packageRoot> <repositoryRoot> <baseCommit> <currentMainCommit>');
|
||||
}
|
||||
const result = inspectPayload({ packageRoot, repositoryRoot, baseCommit, currentMainCommit });
|
||||
process.stdout.write(`${JSON.stringify(result)}\n`);
|
||||
if (!result.allowed) process.exitCode = 2;
|
||||
} catch (error) {
|
||||
process.stderr.write(`[FEHLER] ${error.message}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user