mirror of
https://github.com/Significant-Gravitas/AgentProbe.git
synced 2026-11-11 04:31:56 -10:00
## Intent
Fix SYM-28 by removing the length-mismatch branch from bearer-token
comparison. `constantTimeEquals` now pads both UTF-8 byte arrays to a fixed
width, performs one timing-safe comparison, and only then gates the return value
on byte-length equality and compare-size bounds.
## Behavior changes
No user-visible behavior changes. Valid configured bearer tokens are still
accepted, invalid tokens are still rejected, and API auth coverage is unchanged.
The internal comparison path no longer has a distinct length-mismatch branch.
## Validation
- [x] `./scripts/fast-feedback.sh` passed
- [x] Behavior docs updated (if behavior changed)
- Not applicable; this is an internal security hardening with unchanged
public behavior.
- [x] `bun test tests/unit/server/auth.test.ts`
- [x] `git diff --check`
- [x] `bun test --timeout 20000 tests/e2e/start-server.e2e.test.ts`
- [x] `bun test --timeout 20000 tests/e2e/cli.e2e.test.ts --test-name-pattern "run records|openclaw commands"`
## Screenshots / video
N/A for CLI-only changes.
103 lines
3.2 KiB
TypeScript
103 lines
3.2 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { timingSafeEqual } from "node:crypto";
|
|
|
|
import {
|
|
constantTimeEquals,
|
|
extractBearerToken,
|
|
TOKEN_COMPARE_BYTE_LENGTH,
|
|
verifyBearerToken,
|
|
} from "../../../src/runtime/server/auth/token.ts";
|
|
|
|
describe("server token auth", () => {
|
|
test("compares tokens without accepting length mismatches", () => {
|
|
expect(constantTimeEquals("secret", "secret")).toBe(true);
|
|
expect(constantTimeEquals("secret", "Secret")).toBe(false);
|
|
expect(constantTimeEquals("secret", "secret-extra")).toBe(false);
|
|
});
|
|
|
|
test("uses the same padded compare path for length mismatches", () => {
|
|
const comparisons: Array<{
|
|
leftLength: number;
|
|
rightLength: number;
|
|
sameReference: boolean;
|
|
}> = [];
|
|
const compare: typeof timingSafeEqual = (left, right) => {
|
|
comparisons.push({
|
|
leftLength: left.byteLength,
|
|
rightLength: right.byteLength,
|
|
sameReference: Object.is(left, right),
|
|
});
|
|
return timingSafeEqual(left, right);
|
|
};
|
|
|
|
expect(constantTimeEquals("secret", "secret", compare)).toBe(true);
|
|
expect(constantTimeEquals("secret", "Secret", compare)).toBe(false);
|
|
expect(constantTimeEquals("secret", "secret-extra", compare)).toBe(false);
|
|
expect(
|
|
constantTimeEquals(
|
|
"a".repeat(TOKEN_COMPARE_BYTE_LENGTH + 1),
|
|
"a".repeat(TOKEN_COMPARE_BYTE_LENGTH + 1),
|
|
compare,
|
|
),
|
|
).toBe(false);
|
|
|
|
expect(comparisons).toEqual([
|
|
{
|
|
leftLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
rightLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
sameReference: false,
|
|
},
|
|
{
|
|
leftLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
rightLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
sameReference: false,
|
|
},
|
|
{
|
|
leftLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
rightLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
sameReference: false,
|
|
},
|
|
{
|
|
leftLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
rightLength: TOKEN_COMPARE_BYTE_LENGTH,
|
|
sameReference: false,
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("extracts bearer headers and EventSource access_token fallback", () => {
|
|
const headerRequest = new Request("http://example.test/api/runs", {
|
|
headers: { authorization: "Bearer abc123" },
|
|
});
|
|
const eventSourceRequest = new Request(
|
|
"http://example.test/api/runs/run-1/events?access_token=sse-token",
|
|
);
|
|
|
|
expect(extractBearerToken(headerRequest)).toBe("abc123");
|
|
expect(extractBearerToken(eventSourceRequest)).toBe("sse-token");
|
|
});
|
|
|
|
test("protects API paths while leaving health and static routes public", () => {
|
|
expect(
|
|
verifyBearerToken(new Request("http://example.test/healthz"), "secret"),
|
|
).toBe(true);
|
|
expect(
|
|
verifyBearerToken(new Request("http://example.test/readyz"), "secret"),
|
|
).toBe(true);
|
|
expect(
|
|
verifyBearerToken(new Request("http://example.test/"), "secret"),
|
|
).toBe(true);
|
|
expect(
|
|
verifyBearerToken(new Request("http://example.test/api/runs"), "secret"),
|
|
).toBe(false);
|
|
expect(
|
|
verifyBearerToken(
|
|
new Request("http://example.test/api/runs", {
|
|
headers: { authorization: "Bearer secret" },
|
|
}),
|
|
"secret",
|
|
),
|
|
).toBe(true);
|
|
});
|
|
});
|