feat: establish NekoNest Cloud control and relay

This commit is contained in:
2026-08-12 23:25:43 +08:00
commit f27606b709
222 changed files with 71456 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import test from "node:test";
import { DAEMON_RELEASE_POLICY } from "../release/daemon-policy.mjs";
import {
ReleaseVerificationError,
formatCloudReleaseEnvironment,
parseReleaseTag,
verifyDaemonRelease,
} from "../release/verify-daemon-release-core.mjs";
const TAG = "v0.2.6";
const API_URL = `https://api.github.com/repos/${DAEMON_RELEASE_POLICY.repository}/releases/tags/${TAG}`;
const RELEASE_BASE = `https://github.com/${DAEMON_RELEASE_POLICY.repository}/releases/download/${TAG}`;
function hash(bytes) {
return createHash("sha256").update(bytes).digest("hex");
}
function fixture() {
const bodies = new Map(
DAEMON_RELEASE_POLICY.assets.map((asset, index) => [
asset.filename,
Buffer.from(`verified-daemon-asset-${index + 1}`),
]),
);
const checksumsText = [
...DAEMON_RELEASE_POLICY.assets.map(
(asset) => `${hash(bodies.get(asset.filename))} ${asset.filename}`,
),
`${"9".repeat(64)} nekonest-server-linux-amd64.tar.gz`,
].join("\n") + "\n";
const checksumsBytes = Buffer.from(checksumsText);
const release = {
tag_name: TAG,
draft: false,
prerelease: false,
html_url: `https://github.com/${DAEMON_RELEASE_POLICY.repository}/releases/tag/${TAG}`,
published_at: "2026-08-12T00:00:00Z",
assets: [
...DAEMON_RELEASE_POLICY.assets.map((asset) => ({
name: asset.filename,
browser_download_url: `${RELEASE_BASE}/${asset.filename}`,
size: bodies.get(asset.filename).byteLength,
state: "uploaded",
digest: `sha256:${hash(bodies.get(asset.filename))}`,
})),
{
name: "checksums.txt",
browser_download_url: `${RELEASE_BASE}/checksums.txt`,
size: checksumsBytes.byteLength,
state: "uploaded",
digest: `sha256:${hash(checksumsBytes)}`,
},
],
};
const requests = [];
const fetchImpl = async (url, init) => {
requests.push({ url: String(url), init });
let bytes;
if (String(url) === API_URL) bytes = Buffer.from(JSON.stringify(release));
else if (String(url) === `${RELEASE_BASE}/checksums.txt`) bytes = checksumsBytes;
else {
const filename = String(url).slice(RELEASE_BASE.length + 1);
bytes = bodies.get(filename);
}
if (!bytes) return new Response("missing", { status: 404 });
return new Response(bytes, {
status: 200,
headers: { "content-length": String(bytes.byteLength) },
});
};
return { bodies, checksumsBytes, fetchImpl, release, requests };
}
async function expectCode(promise, code) {
await assert.rejects(promise, (error) => {
assert.ok(error instanceof ReleaseVerificationError);
assert.equal(error.code, code);
return true;
});
}
test("rejects old or prerelease tags before making a network request", async () => {
let calls = 0;
const fetchImpl = async () => {
calls += 1;
throw new Error("should not fetch");
};
assert.throws(() => parseReleaseTag("v0.2.5"), /不得低于 v0\.2\.6/);
assert.throws(() => parseReleaseTag("v0.2.6-beta.1"), /稳定版本/);
await expectCode(
verifyDaemonRelease({ tag: "v0.2.5", fetchImpl }),
"incompatible_release_version",
);
assert.equal(calls, 0);
});
test("verifies API identity, checksums digest, asset digests, sizes, and downloaded bytes", async () => {
const data = fixture();
const token = "github-test-token-that-must-not-be-returned";
const verified = await verifyDaemonRelease({ tag: TAG, token, fetchImpl: data.fetchImpl });
assert.equal(verified.version, "0.2.6");
assert.equal(verified.assets.length, 3);
assert.equal(verified.assets[0].sha256, hash(data.bodies.get(verified.assets[0].filename)));
assert.equal(data.requests.length, 5);
assert.equal(data.requests[0].init.headers.authorization, `Bearer ${token}`);
for (const request of data.requests.slice(1)) {
assert.equal(request.init.headers.authorization, undefined);
}
const serialized = JSON.stringify(verified);
assert.doesNotMatch(serialized, /github-test-token/);
const environment = formatCloudReleaseEnvironment(verified);
assert.match(environment, /NEKONEST_CLOUD_DAEMON_RELEASE_VERSION=0\.2\.6/);
assert.match(environment, /NEKONEST_CLOUD_DAEMON_LINUX_ARM64_SHA256=[0-9a-f]{64}/);
assert.doesNotMatch(environment, /GITHUB_TOKEN|github-test-token/);
});
test("fails closed on duplicate assets, URL substitution, prerelease metadata, or digest drift", async () => {
{
const data = fixture();
data.release.assets.push({ ...data.release.assets[0] });
await expectCode(
verifyDaemonRelease({ tag: TAG, fetchImpl: data.fetchImpl }),
"release_asset_duplicate",
);
}
{
const data = fixture();
data.release.assets[0].browser_download_url = "https://download.example.test/substituted.zip";
await expectCode(
verifyDaemonRelease({ tag: TAG, fetchImpl: data.fetchImpl }),
"release_asset_url_mismatch",
);
}
{
const data = fixture();
data.release.prerelease = true;
await expectCode(
verifyDaemonRelease({ tag: TAG, fetchImpl: data.fetchImpl }),
"release_identity_invalid",
);
}
{
const data = fixture();
data.release.assets[0].digest = `sha256:${"f".repeat(64)}`;
await expectCode(
verifyDaemonRelease({ tag: TAG, fetchImpl: data.fetchImpl }),
"release_digest_mismatch",
);
}
});
test("detects same-sized downloaded byte substitution even when metadata still looks valid", async () => {
const data = fixture();
const filename = DAEMON_RELEASE_POLICY.assets[0].filename;
const original = data.bodies.get(filename);
data.bodies.set(filename, Buffer.from(original.toString().replace("verified", "VERIFIED")));
assert.equal(data.bodies.get(filename).byteLength, original.byteLength);
await expectCode(
verifyDaemonRelease({ tag: TAG, fetchImpl: data.fetchImpl }),
"release_asset_hash_mismatch",
);
});
test("CLI has a no-network usage failure when the exact tag is missing", () => {
const result = spawnSync(
process.execPath,
["tools/verify-daemon-release.mjs"],
{ cwd: new URL("..", import.meta.url), encoding: "utf8" },
);
assert.equal(result.status, 2);
assert.match(result.stderr, /vX\.Y\.Z/);
assert.equal(result.stdout, "");
});