Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0114c22
quest: claim quest/m0/audit-deps
kixelated Oct 20, 2026
c658bb0
chore(gst): bump gstreamer to 0.24
kixelated Oct 20, 2026
14201ec
chore(tokio)!: bump tikv-jemallocator to 0.7
kixelated Oct 20, 2026
0e0f6ce
chore(hang): replace lazy_static with std LazyLock
kixelated Oct 20, 2026
d8de1ca
ci(dependabot): drop stale holds and cover infra/moq-sh
kixelated Oct 20, 2026
eccc96c
docs(audio): correct the unsafe-libopus maintenance claim
kixelated Oct 20, 2026
ab5803d
refactor(tokio): classify local IPv6 with std
kixelated Oct 20, 2026
9cb0637
fix(net): reject non-hex certificate hashes
kixelated Oct 20, 2026
2bdf22e
chore(auth): use jose's base64url instead of @hexagon/base64
kixelated Oct 20, 2026
57467c4
docs(relay): say why http-cache-reqwest is a prerelease
kixelated Oct 20, 2026
bd09a2c
Merge remote-tracking branch 'origin/main' into quest/m0/audit-deps
kixelated Oct 20, 2026
83399a6
quest(m0): finish the dependency audit and schedule the m1 run
kixelated Oct 20, 2026
fa46830
style(relay): restore the space taplo expects
kixelated Oct 20, 2026
aa1459b
docs(tokio): link the jemalloc migration entry to its PR
kixelated Oct 20, 2026
9b597a1
Merge remote-tracking branch 'origin/main' into merge-audit-deps
kixelated Oct 20, 2026
62ef955
fix(auth): decode keys with jose instead of the dropped @hexagon/base64
kixelated Oct 20, 2026
8bb2e93
docs(moq-gst): note the gstreamer 0.24 break for Rust embedders
kixelated Oct 20, 2026
e24f770
Merge remote-tracking branch 'origin/main' into fix-hexagon-6053
kixelated Oct 20, 2026
62c769a
fix(net): refuse an empty certificate hash
kixelated Oct 20, 2026
7a2d3c2
Merge remote-tracking branch 'origin/main' into merge-main-6053
kixelated Oct 20, 2026
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
20 changes: 6 additions & 14 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,19 @@ updates:
patterns:
- "*"
ignore:
# reqwest-middleware 0.5 requires reqwest 0.13, but http-cache-reqwest's
# latest stable (0.16) still pins reqwest 0.12 / reqwest-middleware 0.4.
# The only pairing is http-cache-reqwest 1.0.0-alpha (pre-release), so we
# hold reqwest-middleware at 0.4 until that line ships stable.
- dependency-name: "reqwest-middleware"
versions: [">=0.5"]

# The Go bindings are generated by NordSecurity/uniffi-bindgen-go, whose
# latest release (v0.7.1+v0.31.0) parses uniffi 0.31 metadata. uniffi 0.32
# changed that encoding, so a 0.32 library makes bindgen-go fail with
# "Invalid string data". Hold uniffi at 0.31 until bindgen-go ships 0.32
# support; the other languages use uniffi's own version-matched bindgen
# and are not affected.
# The Go bindings come from a uniffi-bindgen-go build that only reads the
# metadata of one uniffi minor (0.32 today). The generator is pinned in
# flake.nix and five other places, so a uniffi minor moves with it by
# hand; see the uniffi-bindgen-go comment in flake.nix.
- dependency-name: "uniffi"
versions: [">=0.32"]
versions: [">=0.33"]

- package-ecosystem: "bun"
directories:
- "/"
- "/infra/apt"
- "/infra/rpm"
- "/infra/moq-sh"
- "/demo/pub"
schedule:
interval: "weekly"
Expand Down
63 changes: 36 additions & 27 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ if-addrs = "0.15"
io-uring = "0.7.15"
jsonwebtoken = "11"
kio = { version = "0.6.2", path = "rs/kio" }
lazy_static = "1.5"
libc = "0.2"
libloading = "0.9"
linux-raw-sys = { version = "0.12.1", features = ["ioctl"] }
Expand Down
3 changes: 0 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ ignore = [
# when rustcrypto/RSA publishes a constant-time fix.
"RUSTSEC-2023-0071",

# gstreamer 0.23 pulls paste (proc-macro). gstreamer 0.25 drops it but
# needs Rust 1.92, above the 1.91 workspace MSRV. Build-time only.
# paste is unmaintained (proc-macro, build-time only). Pulled by
# tikv-jemalloc-ctl (moq-tokio's `jemalloc` feature; 0.7 still uses it) and
# by iroh's netwatch via netlink-packet-core 0.8, which 0.9 drops.
"RUSTSEC-2024-0436",

# ttf-parser is unmaintained, with no upgrade available. Reachable only via
Expand Down
1 change: 0 additions & 1 deletion js/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"release": "bun ../common/release.ts"
},
"dependencies": {
"@hexagon/base64": "^2.0.4",
"@moq/clock": "workspace:^",
Comment thread
kixelated marked this conversation as resolved.
"@moq/pattern": "workspace:^",
"@zod/mini": "^4.6.5",
Expand Down
4 changes: 2 additions & 2 deletions js/auth/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env node

import { closeSync, fchmodSync, openSync, readFileSync, writeFileSync } from "node:fs";
import * as base64 from "@hexagon/base64";
import { Command, Option } from "commander";
import { base64url } from "jose";
import type { Algorithm } from "./algorithm.ts";
import { authorize, ClaimsSchema, type Scope, ScopeSchema } from "./claims.ts";
import { Key } from "./key.ts";
Expand Down Expand Up @@ -42,7 +42,7 @@ program
// Written the legacy way when that says the same thing, so older readers load it.
const json = JSON.stringify(k.scope ? { ...k, scope: encodeGrants(k.scope) } : k, null, 2);
if (options.base64) {
return base64.fromArrayBuffer(new TextEncoder().encode(json).buffer, true);
return base64url.encode(json);
}
return json;
};
Expand Down
55 changes: 46 additions & 9 deletions js/auth/src/key.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { expect, test } from "bun:test";
import * as base64 from "@hexagon/base64";
import { exportJWK, generateKeyPair, SignJWT } from "jose";
import { base64url, exportJWK, generateKeyPair, SignJWT } from "jose";
import type { Algorithm } from "./algorithm.ts";
import { authorize, type Claims } from "./claims.ts";
import { INSECURE_TEST_RS256_OTHER, INSECURE_TEST_RSA_KEYS } from "./insecure-test-keys.ts";
Expand All @@ -10,7 +9,7 @@ import { Key, KeyIdSchema } from "./key.ts";
function encodeJwk(obj: unknown): string {
const jsonString = JSON.stringify(obj);
const data = new TextEncoder().encode(jsonString);
return base64.fromArrayBuffer(data.buffer as ArrayBuffer, true); // true for urlSafe
return base64url.encode(data);
}

const testKey = {
Expand Down Expand Up @@ -95,7 +94,7 @@ test("parse - invalid base64url", () => {
test("parse - invalid JSON after base64url decode", () => {
// Base64url encode invalid JSON
const data = new TextEncoder().encode("invalid json");
const invalidJwk = base64.fromArrayBuffer(data.buffer as ArrayBuffer, true); // true for urlSafe
const invalidJwk = base64url.encode(data);

expect(() => {
Key.parse(invalidJwk);
Expand Down Expand Up @@ -126,6 +125,44 @@ test("parse - secret too short", () => {
}).toThrow();
});

test("parse - a field with non-base64 characters is refused, whatever its length", () => {
// Long enough to pass the 32-byte check if the bad characters were silently zeroed.
const invalidKey = { ...testKey, k: `${testKey.k.slice(0, -2)}!!` };

expect(() => Key.parse(encodeJwk(invalidKey))).toThrow();
});

test("parse - accepts a JWK in standard padded base64", () => {
// The note survives as "+" and "/" in standard base64, and the kid length forces padding.
const json = JSON.stringify({ ...testKey, kid: "key-1", note: "~~~???" });
const encoded = Buffer.from(json).toString("base64");
expect(encoded).toMatch(/\+/);
expect(encoded).toMatch(/\//);
expect(encoded).toMatch(/=$/);

const key = Key.parse(encoded);
expect(key.kid).toBe("key-1");
expect((key as { k?: string }).k).toBe(testKey.k);
});

test("generate - an HMAC secret is unpadded base64url that round-trips", async () => {
for (const [alg, bytes] of [
["HS256", 32],
["HS384", 48],
["HS512", 64],
] as const) {
const key = await Key.generate(alg, "generated");
const k = (key as { k: string }).k;
expect(k).toMatch(/^[A-Za-z0-9_-]+$/);
expect(base64url.decode(k).byteLength).toBe(bytes);

const parsed = Key.parse(encodeJwk(key));
expect(parsed).toEqual(key);
const claims = await Key.verify(parsed, await Key.sign(key, testClaims));
expect(claims.root).toBe(testClaims.root);
}
});

test("parse - missing required fields", () => {
const invalidKey = {
alg: "HS256",
Expand Down Expand Up @@ -615,7 +652,7 @@ test("sign - includes kid in header when present", async () => {

// Decode the header to verify kid is present
const [headerB64] = token.split(".");
const headerBuffer = base64.toArrayBuffer(headerB64, true); // true for urlSafe
const headerBuffer = base64url.decode(headerB64);
const header = JSON.parse(new TextDecoder().decode(headerBuffer));

expect(header.kid).toBe("test-key-1");
Expand All @@ -635,7 +672,7 @@ test("sign - no kid in header when not present", async () => {

// Decode the header to verify kid is not present
const [headerB64] = token.split(".");
const headerBuffer = base64.toArrayBuffer(headerB64, true); // true for urlSafe
const headerBuffer = base64url.decode(headerB64);
const header = JSON.parse(new TextDecoder().decode(headerBuffer));

expect(header.kid).toBe(undefined);
Expand All @@ -648,7 +685,7 @@ test("sign - writes iat only when the claims carry one", async () => {

const decodePayload = (token: string) => {
const [, payloadB64] = token.split(".");
const payloadBuffer = base64.toArrayBuffer(payloadB64, true); // true for urlSafe
const payloadBuffer = base64url.decode(payloadB64);
return JSON.parse(new TextDecoder().decode(payloadBuffer));
};

Expand Down Expand Up @@ -688,10 +725,10 @@ test("verify - invalid payload structure", async () => {

// Create a token with invalid payload structure
const headerData = new TextEncoder().encode(JSON.stringify({ alg: "HS256", typ: "JWT" }));
const header = base64.fromArrayBuffer(headerData.buffer as ArrayBuffer, true); // true for urlSafe
const header = base64url.encode(headerData);

const payloadData = new TextEncoder().encode(JSON.stringify({ invalid: "payload" }));
const payload = base64.fromArrayBuffer(payloadData.buffer as ArrayBuffer, true); // true for urlSafe
const payload = base64url.encode(payloadData);
const signature = "invalid";
const invalidToken = `${header}.${payload}.${signature}`;

Expand Down
Loading