fix(usage): guard malformed Copilot payloads (#110795)

Co-authored-by: Leon-SK668 <17695126+Leon-SK668@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Leon-SK668
2026-07-28 09:55:15 +08:00
committed by GitHub
parent 6ca7c6488e
commit a90bef3970
2 changed files with 19 additions and 1 deletions
+16
View File
@@ -301,6 +301,22 @@ describe("fetchCopilotUsage", () => {
});
});
it.each([
["null", null],
["an array", []],
])("returns an empty window list for a non-object %s payload", async (_label, payload) => {
const mockFetch = createProviderUsageFetch(async () => makeResponse(200, payload));
const result = await fetchCopilotUsage("token", 5000, mockFetch);
expect(result).toEqual({
provider: "github-copilot",
displayName: "Copilot",
windows: [],
plan: undefined,
});
});
it("bounds the usage read and cancels the stream when the body exceeds the JSON byte cap", async () => {
// Larger than the shared 16 MiB readProviderJsonResponse cap so the bounded reader cancels the
// stream mid-flight; if the cap were removed the unbounded res.json() would buffer the whole body.
+3 -1
View File
@@ -9,6 +9,7 @@ import {
type ProviderUsageSnapshot,
type UsageWindow,
} from "openclaw/plugin-sdk/provider-usage";
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
import { PUBLIC_GITHUB_COPILOT_DOMAIN } from "./domain.js";
type CopilotUsageResponse = {
@@ -45,7 +46,8 @@ export async function fetchCopilotUsage(
});
}
const data = await readProviderJsonResponse<CopilotUsageResponse>(res, "github-copilot-usage");
const payload = await readProviderJsonResponse<unknown>(res, "github-copilot-usage");
const data = isRecord(payload) ? (payload as CopilotUsageResponse) : {};
const windows: UsageWindow[] = [];
if (data.quota_snapshots?.premium_interactions) {