fix: secure assistant generation telemetry

This commit is contained in:
ajnart committed 2026-08-02 09:09:01 +02:00
1 parent 04520cf67c
commit 8bb0ddef42
11 files changed
+181 -10

No files matched your search

@@ -0,0 +1,28 @@
import { describe, expect, test, vi } from "vitest";
vi.mock("@homarr/common/env", () => ({
env: { SECRET_ENCRYPTION_KEY: "a".repeat(64) },
}));
import {
createAssistantGenerationAccessToken,
verifyAssistantGenerationAccessToken,
} from "../assistant-generation-access";
describe("assistant generation access token", () => {
const input = {
userId: "user-1",
threadId: "thread-1",
generationId: "gen-example",
};
test("binds a generation to its user and conversation", () => {
const accessToken = createAssistantGenerationAccessToken(input);
expect(accessToken).toMatch(/^[A-Za-z0-9_-]{43}$/);
expect(verifyAssistantGenerationAccessToken(input, accessToken)).toBe(true);
expect(verifyAssistantGenerationAccessToken({ ...input, userId: "user-2" }, accessToken)).toBe(false);
expect(verifyAssistantGenerationAccessToken({ ...input, threadId: "thread-2" }, accessToken)).toBe(false);
expect(verifyAssistantGenerationAccessToken({ ...input, generationId: "gen-other" }, accessToken)).toBe(false);
});
});
@@ -1,8 +1,13 @@
import { describe, expect, test, vi } from "vitest";
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
vi.mock("@homarr/core/infrastructure/http", () => ({ fetchWithTrustedCertificatesAsync: vi.fn() }));
import { parseOpenRouterGenerationTelemetry } from "../assistant-generation-telemetry";
import {
fetchOpenRouterGenerationTelemetryAsync,
parseOpenRouterGenerationTelemetry,
} from "../assistant-generation-telemetry";
describe("parseOpenRouterGenerationTelemetry", () => {
test("uses provider-native tokens for context and throughput", () => {
@@ -63,4 +68,38 @@ describe("parseOpenRouterGenerationTelemetry", () => {
expect(parseOpenRouterGenerationTelemetry({ data: [] })).toBeNull();
expect(parseOpenRouterGenerationTelemetry({ data: { latency: -1, total_cost: "invalid" } })).toBeNull();
});
test("does not count additional successful provider responses as fallbacks", () => {
expect(
parseOpenRouterGenerationTelemetry({
id: "gen-success",
provider_responses: [
{ status: 200, latency: 100 },
{ status: 200, latency: 120 },
],
}),
).toMatchObject({ fallbackCount: 0 });
});
test("caches completed generation records", async () => {
const mockedFetch = vi.mocked(fetchWithTrustedCertificatesAsync);
mockedFetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: { id: "gen-cached", native_tokens_completion: 4 } }),
} as Awaited<ReturnType<typeof fetchWithTrustedCertificatesAsync>>);
const input = {
baseUrl: "https://openrouter.ai/api/v1",
generationId: "gen-cached",
headers: { Authorization: "Bearer test" },
};
await expect(fetchOpenRouterGenerationTelemetryAsync(input)).resolves.toMatchObject({
generationId: "gen-cached",
outputTokens: 4,
});
await expect(fetchOpenRouterGenerationTelemetryAsync(input)).resolves.toMatchObject({
generationId: "gen-cached",
});
expect(mockedFetch).toHaveBeenCalledTimes(1);
});
});