Skip to content

fix(hooks): move Gmail push token from URL query string to header#11028

Closed
coygeek wants to merge 1 commit intoopenclaw:mainfrom
coygeek:fix/de01-gmail-push-token
Closed

fix(hooks): move Gmail push token from URL query string to header#11028
coygeek wants to merge 1 commit intoopenclaw:mainfrom
coygeek:fix/de01-gmail-push-token

Conversation

@coygeek
Copy link
Copy Markdown
Contributor

@coygeek coygeek commented Feb 7, 2026

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

Metric Value
Score 8.3 / 10.0
Severity High
Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L

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

  • Command: pnpm build
  • Status: passed

Risk and Compatibility

non-breaking; compatibility impact was not explicitly documented in the original PR body.

AI-Assisted Disclosure

  • AI-assisted: yes
  • Model: Claude Code

Greptile Overview

Greptile Summary

  • Moves Gmail Pub/Sub push authentication from a URL query parameter to a push config attribute delivered as an HTTP header.
  • Adds token redaction helpers and uses them to avoid printing sensitive CLI argument values in logs and JSON output.
  • Updates setup utilities to PATCH the Pub/Sub subscription pushConfig via the Pub/Sub REST API after creating/updating the subscription.
  • Simplifies tailscale endpoint generation so the public push endpoint is no longer tokenized in the URL.

Confidence Score: 4/5

  • Mostly safe to merge, but setup may silently fail to apply the header-based token and log redaction is not comprehensive for tokenized URLs.
  • The change reduces token exposure by moving it out of the push endpoint URL and adds redaction for common flags, but the Pub/Sub PATCH path ignores non-2xx responses (so the attribute can fail to set without notice) and redactArgs can still leak sensitive query parameters passed via --hook-url.
  • src/hooks/gmail-setup-utils.ts, src/hooks/gmail.ts

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]>
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +289 to +309
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 },
},
}),
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +280 to +288
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(" ");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@leszekszpunar
Copy link
Copy Markdown
Contributor

Heads up: the lint failure (preserve-caught-error in extensions/memory-lancedb/index.ts) is a pre-existing issue on main, not caused by your changes. The fix is in #11061. Once it lands, rebase on main and re-push to get a clean CI run.

git fetch origin main && git rebase origin/main && git push --force-with-lease

@leszekszpunar
Copy link
Copy Markdown
Contributor

Update: the lint failure has been resolved by #11093 (merged), which excluded extensions/ from oxlint scope. A rebase on main should give you a clean CI run.

git fetch origin main && git rebase origin/main && git push --force-with-lease

@coygeek
Copy link
Copy Markdown
Contributor Author

coygeek commented Feb 16, 2026

Cleanup note for traceability:

  • Linked issue #11024 was closed by maintainer as duplicate of #5120.
  • Keeping discussion/work tracking under canonical issue #5120 (linked issue closed as duplicate; canonical issue remains open).

Closing this PR to align with duplicate routing. If maintainers want this exact patch revived, I can reopen or submit a retargeted PR.

@coygeek coygeek closed this Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Gmail push endpoint embeds authentication token in URL query string

2 participants