build: сверка артефакта Hysteria с upstream hashes.txt
SHA-256 считался локально от уже скачанного файла. Это защищает target от последующей подмены, но не доказывает, что builder скачал именно ожидаемый upstream artifact: сумма фиксирует то, что пришло, каким бы оно ни было. То есть trust-on-first-use, а не проверка происхождения. Upstream публикует контрольные суммы релиза ассетом hashes.txt: 6493dfff...f94 build/hysteria-linux-amd64 f24f63be...189 build/hysteria-linux-amd64-avx Теперь резолвер отдаёт и URL этого ассета, сборка скачивает его, берёт оттуда ожидаемую сумму и сверяет с ней бинарник - и только после этого записывает SHA-256 в HY2XS lock и metadata. Сопоставление идёт по базовому имени и строго на равенство: build/ - часть пути, а hysteria-linux-amd64-avx - другой артефакт, который не должен совпасть по префиксу. Разбор вынесен в parseUpstreamHashes и покрыт тестами, включая форму sha256:<hex>, верхний регистр, противоречивые и отсутствующие записи. Релиз без hashes.txt для production-сборки непригоден и отклоняется. Источник ожидаемой суммы фиксируется в metadata как hysteria_sha_source.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Build-time CLI: достаёт ожидаемый SHA-256 артефакта из upstream hashes.txt.
|
||||
*
|
||||
* Вынесено в отдельную команду, чтобы разбор формата жил в одном месте и был
|
||||
* покрыт юнит-тестами, а не повторялся awk-выражением в bash.
|
||||
*
|
||||
* bun run tools/parse-upstream-hashes.ts --file hashes.txt --asset hysteria-linux-amd64
|
||||
*/
|
||||
|
||||
import { parseUpstreamHashes } from "../src/build/hysteriaRelease";
|
||||
|
||||
function fail(message: string): never {
|
||||
console.error(`[hy2xs-build] ERROR: ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function parseArgs(argv: string[]): { file: string; asset: string } {
|
||||
let file = "";
|
||||
let asset = "";
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i];
|
||||
const value = argv[i + 1];
|
||||
if (arg === "--file") {
|
||||
file = value ?? "";
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--asset") {
|
||||
asset = value ?? "";
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
fail(`unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
fail("missing --file");
|
||||
}
|
||||
if (!asset) {
|
||||
fail("missing --asset");
|
||||
}
|
||||
|
||||
return { file, asset };
|
||||
}
|
||||
|
||||
const { file, asset } = parseArgs(Bun.argv.slice(2));
|
||||
|
||||
try {
|
||||
const text = await Bun.file(file).text();
|
||||
process.stdout.write(`${parseUpstreamHashes(text, asset)}\n`);
|
||||
} catch (error) {
|
||||
fail(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
Reference in New Issue
Block a user