Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ jobs:
with: { fetch-depth: 0 }
- name: 'Setup Zig'
uses: ./
with: { version: '${{ matrix.zig_version }}' }
- name: 'Test `zig init`'
run: |
mkdir init
Expand Down
13 changes: 6 additions & 7 deletions common.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const fs = require('fs');
const path = require('path');
const core = require('@actions/core');
const github = require('@actions/github');
const exec = require('@actions/exec');

const VERSIONS_JSON = 'https://ziglang.org/download/index.json';
const MACH_VERSIONS_JSON = 'https://pkg.machengine.org/zig/index.json';
Expand Down Expand Up @@ -173,20 +172,20 @@ function versionLessThan(cur_ver, min_ver) {
if (cur.major != min.major) return cur.major < min.major;
if (cur.minor != min.minor) return cur.minor < min.minor;
if (cur.patch != min.patch) return cur.patch < min.patch;
return cur.dev < min.dev;
return cur_dev < min_dev;
}

// Returns object with keys 'major', 'minor', 'patch', and 'dev'.
// 'dev' is `null` if `str` was not a dev version.
// On failure, returns `null`.
function parseVersion(str) {
const match = /^(\d+)\.(\d+)\.(\d+)(?:-dev\.(\d+)\+[0-9a-f]*)?$/.exec(str);
const match = /^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?:-dev\.(?<dev>\d+)\+[0-9a-f]*)?$/.exec(str);
if (match === null) return null;
return {
major: parseInt(match[0]),
minor: parseInt(match[1]),
patch: parseInt(match[2]),
dev: match[3] === null ? null : parseInt(match[3]),
major: parseInt(match.groups['major']),
minor: parseInt(match.groups['minor']),
patch: parseInt(match.groups['patch']),
dev: match.groups['dev'] === undefined ? null : parseInt(match.groups['dev']),
};
}

Expand Down
6,686 changes: 425 additions & 6,261 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

13 changes: 6 additions & 7 deletions dist/post/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const fs = __nccwpck_require__(9896);
const path = __nccwpck_require__(6928);
const core = __nccwpck_require__(7484);
const github = __nccwpck_require__(3228);
const exec = __nccwpck_require__(5236);

const VERSIONS_JSON = 'https://ziglang.org/download/index.json';
const MACH_VERSIONS_JSON = 'https://pkg.machengine.org/zig/index.json';
Expand Down Expand Up @@ -179,20 +178,20 @@ function versionLessThan(cur_ver, min_ver) {
if (cur.major != min.major) return cur.major < min.major;
if (cur.minor != min.minor) return cur.minor < min.minor;
if (cur.patch != min.patch) return cur.patch < min.patch;
return cur.dev < min.dev;
return cur_dev < min_dev;
}

// Returns object with keys 'major', 'minor', 'patch', and 'dev'.
// 'dev' is `null` if `str` was not a dev version.
// On failure, returns `null`.
function parseVersion(str) {
const match = /^(\d+)\.(\d+)\.(\d+)(?:-dev\.(\d+)\+[0-9a-f]*)?$/.exec(str);
const match = /^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?:-dev\.(?<dev>\d+)\+[0-9a-f]*)?$/.exec(str);
if (match === null) return null;
return {
major: parseInt(match[0]),
minor: parseInt(match[1]),
patch: parseInt(match[2]),
dev: match[3] === null ? null : parseInt(match[3]),
major: parseInt(match.groups['major']),
minor: parseInt(match.groups['minor']),
patch: parseInt(match.groups['patch']),
dev: match.groups['dev'] === undefined ? null : parseInt(match.groups['dev']),
};
}

Expand Down
2 changes: 1 addition & 1 deletion dist/post/index.js.map

Large diffs are not rendered by default.

29 changes: 23 additions & 6 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ async function downloadFromMirror(mirror, tarball_filename) {

const tarball_data = await fs.readFile(tarball_path);

const key = minisign.parseKey(MINISIGN_KEY);
const key = await minisign.parseKey(MINISIGN_KEY);
const signature = minisign.parseSignature(signature_data);
if (!minisign.verifySignature(key, signature, tarball_data)) {
if (!await minisign.verifySignature(key, signature, tarball_data)) {
throw new Error(`signature verification failed for '${mirror}/${tarball_filename}'`);
}

Expand All @@ -52,14 +52,31 @@ async function downloadTarball(tarball_filename) {
return await downloadFromMirror(preferred_mirror, tarball_filename);
}

// Fetch the list of mirrors from ziglang.org. Caching the mirror list is awkward in this context,
// so if the list is inaccessible, we just fetch from ziglang.org as a fallback.
let mirrors = [];
let mirrors;
try {
// Fetch an up-to-date list of available mirrors from ziglang.org.
const mirrors_response = await fetch(MIRRORS_URL);
mirrors = (await mirrors_response.text()).split('\n').filter((url) => url.length != 0);
} catch {
// For some reason the mirrors are inaccessible. That's okay; allow ourselves to fall back to ziglang.org below.
// ziglang.org itself is inaccessible. As a fallback (so that ziglang.org outages don't break
// this Action), use a hardcoded mirror list. This was manually fetched from ziglang.org, and I
// expect that at least some of these mirrors will continue working. It only needs to be updated
// if the upstream list changes *significantly*.
mirrors = [
"https://pkg.machengine.org/zig",
"https://zigmirror.hryx.net/zig",
"https://zig.linus.dev/zig",
"https://zig.squirl.dev",
"https://zig.florent.dev",
"https://zig.mirror.mschae23.de/zig",
"https://zigmirror.meox.dev",
"https://ziglang.freetls.fastly.net",
"https://zig.tilok.dev",
"https://zig-mirror.tsimnet.eu/zig",
"https://zig.karearl.com/zig",
"https://pkg.earth/zig",
"https://fs.liujiacai.net/zigbuilds",
];
}

core.info(`Available mirrors: ${mirrors.join(", ")}`);
Expand Down
30 changes: 17 additions & 13 deletions minisign.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
const sodium = require('sodium-javascript');
const crypto = require('node:crypto');

// Parse a minisign key represented as a base64 string.
// Throws exceptions on invalid keys.
function parseKey(key_str) {
async function parseKey(key_str) {
const key_info = Buffer.from(key_str, 'base64');

const id = key_info.subarray(2, 10);
const key = key_info.subarray(10);

if (key.byteLength !== sodium.crypto_sign_PUBLICKEYBYTES) {
if (key.byteLength !== 32) {
throw new Error('invalid public key given');
}

return {
id: id,
key: key
key: await crypto.subtle.importKey('raw', key, 'Ed25519', false, ['verify']),
};
}

Expand Down Expand Up @@ -77,25 +77,29 @@ function parseSignature(sig_buf) {
// Given a parsed key, parsed signature file, and raw file content, verifies the signature,
// including the global signature (hence validating the trusted comment). Does not throw.
// Returns 'true' if the signature is valid for this file, 'false' otherwise.
function verifySignature(pubkey, signature, file_content) {
async function verifySignature(pubkey, signature, file_content) {
if (!signature.key_id.equals(pubkey.id)) {
return false;
return false; // wrong key
}

let signed_content;
if (signature.algorithm.equals(Buffer.from('ED'))) {
signed_content = Buffer.alloc(sodium.crypto_generichash_BYTES_MAX);
sodium.crypto_generichash(signed_content, file_content);
} else {
const hash = crypto.createHash('BLAKE2b512');
hash.update(file_content);
signed_content = hash.digest();
} else if (signature.algorithm.equals(Buffer.from('Ed'))) {
signed_content = file_content;
} else {
return false; // unsupported algorithm
}
if (!sodium.crypto_sign_verify_detached(signature.signature, signed_content, pubkey.key)) {
return false;

if (!await crypto.subtle.verify('Ed25519', pubkey.key, signature.signature, signed_content)) {
return false; // signature verification failure
}

const global_signed_content = Buffer.concat([signature.signature, signature.trusted_comment]);
if (!sodium.crypto_sign_verify_detached(signature.global_signature, global_signed_content, pubkey.key)) {
return false;
if (!await crypto.subtle.verify('Ed25519', pubkey.key, signature.global_signature, global_signed_content)) {
return false; // signature verification failure
}

return true;
Expand Down
Loading