fix(googlechat): normalize auth response headers

This commit is contained in:
Peter Steinberger
2026-05-04 08:40:23 +01:00
parent 7129db1960
commit e524878998
3 changed files with 34 additions and 0 deletions

View File

@@ -205,6 +205,7 @@ Docs: https://docs.openclaw.ai
- Feishu: accept and honor `channels.feishu.blockStreaming` at the top level and per account, while keeping the legacy default off so Feishu cards no longer reject documented config or silently drop block replies. Fixes #75555. Thanks @vincentkoc.
- Gateway/update: avoid `launchctl kickstart -k` immediately after fresh macOS update bootstraps, and unlink dangling global plugin-runtime symlinks during packaged postinstall and `doctor --fix` so upgrades no longer SIGTERM the newly booted Gateway or leave bundled plugin imports pointed at pruned `plugin-runtime-deps` trees. Completes #76261 and fixes #76466. (#76929)
- Google Chat: normalize custom Google auth transport headers before google-auth/gaxios interceptors run, restoring webhook token verification when certificate retrieval expects Fetch `Headers`. Fixes #76742. Thanks @donbowman.
- Google Chat: normalize Google auth certificate response headers before google-auth-library reads cache-control, so inbound webhook auth no longer rejects with `res?.headers.get is not a function`. Fixes #76880. Thanks @donbowman.
- Doctor/plugins: reset stale `plugins.slots.memory` and `plugins.slots.contextEngine` references during `doctor --fix`, so cleanup of missing plugin config does not leave unrecoverable slot owners behind. Fixes #76550 and #76551. Thanks @vincentkoc.
- Docs/WhatsApp: merge the duplicate top-level `web` objects in the gateway channel config example so copy-pasted WhatsApp config keeps both `web.whatsapp` and reconnect settings. Fixes #76619. Thanks @WadydX.
- Plugins/Anthropic: expose Claude thinking profiles from the bundled provider-policy artifact so non-runtime callers keep Opus 4.7 `adaptive`, `xhigh`, and `max` instead of downgrading to `high`. Fixes #76779. Thanks @tomascupr and @iAbhi001.

View File

@@ -348,6 +348,9 @@ describe("googlechat google auth runtime", () => {
expect(transport.interceptors.request.add).toHaveBeenCalledWith({
resolved: expect.any(Function),
});
expect(transport.interceptors.response.add).toHaveBeenCalledWith({
resolved: expect.any(Function),
});
expect("window" in globalThis).toBe(false);
} finally {
if (originalWindowDescriptor) {
@@ -369,6 +372,20 @@ describe("googlechat google auth runtime", () => {
expect(normalized.headers.get("x-test")).toBe("1");
});
it("normalizes Google auth response headers before upstream cache-control reads", () => {
const response = {
data: {},
headers: {
"cache-control": "public, max-age=3600",
},
};
const normalized = __testing.normalizeGoogleAuthResponseHeaders(response);
expect(normalized.headers).toBeInstanceOf(Headers);
expect(normalized.headers.get("cache-control")).toBe("public, max-age=3600");
});
it("rejects service-account credentials that override Google auth endpoints", async () => {
await expect(
resolveValidatedGoogleChatCredentials({

View File

@@ -23,6 +23,9 @@ type GoogleAuthTransport = InstanceType<GaxiosModule["Gaxios"]>;
type GoogleAuthRequestWithUnknownHeaders = RequestInit & {
headers?: unknown;
};
type GoogleAuthResponseWithUnknownHeaders = {
headers?: unknown;
};
type GuardedGoogleAuthRequestInit = RequestInit & {
agent?: unknown;
cert?: unknown;
@@ -79,12 +82,24 @@ function normalizeGoogleAuthPreparedRequestHeaders<T extends GoogleAuthRequestWi
return config as T & { headers: Headers };
}
function normalizeGoogleAuthResponseHeaders<T extends GoogleAuthResponseWithUnknownHeaders>(
response: T,
): T & { headers: Headers } {
if (!(response.headers instanceof Headers)) {
response.headers = new Headers(response.headers as HeadersInit | undefined);
}
return response as T & { headers: Headers };
}
function installGoogleAuthHeaderCompatibilityInterceptor(
transport: GoogleAuthTransport,
): GoogleAuthTransport {
transport.interceptors.request.add({
resolved: async (config) => normalizeGoogleAuthPreparedRequestHeaders(config),
});
transport.interceptors.response.add({
resolved: async (response) => normalizeGoogleAuthResponseHeaders(response),
});
return transport;
}
@@ -558,6 +573,7 @@ export const __testing = {
googleAuthTransportPromise = null;
},
normalizeGoogleAuthPreparedRequestHeaders,
normalizeGoogleAuthResponseHeaders,
resolveGoogleAuthEnvProxyUrl,
validateGoogleChatServiceAccountCredentials,
};