fix(hooks): move Gmail push token from URL query string to header#11028
fix(hooks): move Gmail push token from URL query string to header#11028coygeek wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Move the push authentication token from the URL query string to a custom Pub/Sub push config attribute (x-push-token) to prevent token exposure in access logs, reverse-proxy logs, and telemetry surfaces. The gcloud CLI does not expose push config attributes directly, so the subscription is patched via the Pub/Sub REST API after creation. Additionally redact token values from setup/watcher log output, JSON output, and command-line argument logging to eliminate plaintext credential leakage. Co-Authored-By: Claude Opus 4.6 <[email protected]>
| const tokenResult = await runGcloudCommand(["auth", "print-access-token"], 30_000); | ||
| if (tokenResult.code !== 0) { | ||
| return; | ||
| } | ||
| await runGcloud([ | ||
| "pubsub", | ||
| "subscriptions", | ||
| "create", | ||
| subscription, | ||
| "--project", | ||
| projectId, | ||
| "--topic", | ||
| topicName, | ||
| "--push-endpoint", | ||
| pushEndpoint, | ||
| ]); | ||
| const accessToken = tokenResult.stdout.trim(); | ||
| const subPath = `projects/${projectId}/subscriptions/${subscription}`; | ||
| const url = `https://pubsub.googleapis.com/v1/${subPath}?updateMask=pushConfig`; | ||
| try { | ||
| await fetch(url, { | ||
| method: "PATCH", | ||
| headers: { | ||
| Authorization: `Bearer ${accessToken}`, | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ | ||
| pushConfig: { | ||
| pushEndpoint, | ||
| attributes: { "x-push-token": pushToken }, | ||
| }, | ||
| }), | ||
| }); |
There was a problem hiding this comment.
PATCH result ignored
setPushConfigTokenAttribute doesn't check the HTTP response status (or body) from the Pub/Sub REST PATCH. If the request is unauthorized/forbidden, the subscription doesn't exist, or the updateMask is rejected, fetch() still resolves and this function will silently succeed, leaving the subscription without the header attribute while the rest of the setup assumes the token moved out of the URL. Please check res.ok (and ideally log a warning on failure) so setup doesn't report success when the pushConfig attribute was not applied.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/hooks/gmail-setup-utils.ts
Line: 289:309
Comment:
**PATCH result ignored**
`setPushConfigTokenAttribute` doesn't check the HTTP response status (or body) from the Pub/Sub REST PATCH. If the request is unauthorized/forbidden, the subscription doesn't exist, or the updateMask is rejected, `fetch()` still resolves and this function will silently succeed, leaving the subscription without the header attribute while the rest of the setup assumes the token moved out of the URL. Please check `res.ok` (and ideally log a warning on failure) so setup doesn't report success when the pushConfig attribute was not applied.
How can I resolve this? If you propose a fix, please make it concise.| export function redactArgs(args: string[]): string { | ||
| const redacted = [...args]; | ||
| for (let i = 0; i < redacted.length; i++) { | ||
| if ((redacted[i] === "--token" || redacted[i] === "--hook-token") && i + 1 < redacted.length) { | ||
| redacted[i + 1] = redactToken(redacted[i + 1]); | ||
| } | ||
| } | ||
| return redacted.join(" "); | ||
| } |
There was a problem hiding this comment.
Incomplete token redaction
redactArgs() only redacts values following --token and --hook-token. But the arg list also includes --hook-url, and if that URL contains sensitive query parameters (e.g. a token), this log line will still leak it. To fully meet the “no plaintext credentials in logs” goal, redactArgs() should also sanitize --hook-url values (at least redact query params) before joining.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/hooks/gmail.ts
Line: 280:288
Comment:
**Incomplete token redaction**
`redactArgs()` only redacts values following `--token` and `--hook-token`. But the arg list also includes `--hook-url`, and if that URL contains sensitive query parameters (e.g. a token), this log line will still leak it. To fully meet the “no plaintext credentials in logs” goal, `redactArgs()` should also sanitize `--hook-url` values (at least redact query params) before joining.
How can I resolve this? If you propose a fix, please make it concise.|
Heads up: the lint failure ( git fetch origin main && git rebase origin/main && git push --force-with-lease |
|
Update: the lint failure has been resolved by #11093 (merged), which excluded git fetch origin main && git rebase origin/main && git push --force-with-lease |
bfc1ccb to
f92900f
Compare
|
Cleanup note for traceability:
Closing this PR to align with duplicate routing. If maintainers want this exact patch revived, I can reopen or submit a retargeted PR. |
Fix Summary
Move the push authentication token from the URL query string to a custom Pub/Sub push header to prevent token exposure in access logs, reverse-proxy logs, and telemetry surfaces.
Additionally redact token values from setup/watcher log output, JSON output, and command-line argument logging to eliminate plaintext credential leakage.
Issue Linkage
Fixes #11024
Security Snapshot
Implementation Details
Files Changed
src/hooks/gmail-ops.ts(+6/-5)src/hooks/gmail-setup-utils.ts(+68/-14)src/hooks/gmail-watcher.ts(+2/-1)src/hooks/gmail.ts(+25/-0)Technical Analysis
Move the push authentication token from the URL query string to a custom Pub/Sub push header to prevent token exposure in access logs, reverse-proxy logs, and telemetry surfaces.
Validation Evidence
pnpm buildRisk and Compatibility
non-breaking; compatibility impact was not explicitly documented in the original PR body.
AI-Assisted Disclosure
Greptile Overview
Greptile Summary
Confidence Score: 4/5
redactArgscan still leak sensitive query parameters passed via--hook-url.