From e7a3cd22c3cb778f0bde2f9c8401eda43bd28733 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 14 Aug 2026 12:21:42 -0700 Subject: [PATCH] fix(gmail): avoid duplicate sanitized content --- CHANGELOG.md | 1 + docs/gmail-workflows.md | 4 ++- internal/cmd/gmail_get.go | 9 +------ internal/cmd/gmail_sanitize_test.go | 40 ++++++++++++++++++++++------- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 796a17090..3188a533e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Gmail: emit sanitized message headers and bodies once in `gmail get --json --sanitize-content`, while retaining the `message` envelope and `--results-only` unwrapping. (#986) — thanks @ronny-rentner. - Sheets: add read-only Connected Sheets discovery, full data-source descriptions and execution status, plus bounded reads for anchored data-source tables (extracts) behind an explicitly opted-in BigQuery scope. (#938) — thanks @ryo-touch. ## v0.36.0 - 2026-08-13 diff --git a/docs/gmail-workflows.md b/docs/gmail-workflows.md index 191cff31a..45377e294 100644 --- a/docs/gmail-workflows.md +++ b/docs/gmail-workflows.md @@ -23,7 +23,9 @@ gog gmail thread get --sanitize-content --json ``` `--sanitize-content` strips unsafe/raw payload details while keeping useful -message text for automation. +message text for automation. Message JSON remains under the `message` key; +add `--results-only` to emit that sanitized message directly. Both shapes emit +the message headers and body once. ## Filters diff --git a/internal/cmd/gmail_get.go b/internal/cmd/gmail_get.go index 75ee7dc1e..0e5fa3b3e 100644 --- a/internal/cmd/gmail_get.go +++ b/internal/cmd/gmail_get.go @@ -74,14 +74,7 @@ func (c *GmailGetCmd) Run(ctx context.Context, flags *RootFlags) error { if outfmt.IsJSON(ctx) { if c.SanitizeContent { output := sanitizedGmailMessage(msg, format == gmailFormatFull, c.UseIndexedAttachmentIDs) - payload := map[string]any{ - "message": output, - "headers": output.Headers, - } - if format == gmailFormatFull && output.Body != "" { - payload["body"] = output.Body - } - return outfmt.WriteJSON(ctx, stdoutWriter(ctx), payload) + return outfmt.WriteJSON(ctx, stdoutWriter(ctx), map[string]any{"message": output}) } // Include a flattened headers map for easier querying // (e.g., jq '.headers.to' instead of complex nested queries) diff --git a/internal/cmd/gmail_sanitize_test.go b/internal/cmd/gmail_sanitize_test.go index f213933fa..f982a685b 100644 --- a/internal/cmd/gmail_sanitize_test.go +++ b/internal/cmd/gmail_sanitize_test.go @@ -97,21 +97,43 @@ func TestGmailGetCmd_SanitizeContent_JSONUsesSafeEnvelope(t *testing.T) { if strings.Contains(result.stdout, "payload") || strings.Contains(result.stdout, "unsubscribe") { t.Fatalf("sanitized JSON should not expose raw Gmail payload/unsubscribe: %s", result.stdout) } + var envelope map[string]json.RawMessage + if err := json.Unmarshal([]byte(result.stdout), &envelope); err != nil { + t.Fatalf("decode JSON envelope: %v", err) + } + if len(envelope) != 1 || envelope["message"] == nil { + t.Fatalf("sanitized JSON should contain the message once, got: %s", result.stdout) + } + var parsed struct { - Body string `json:"body"` - Message struct { - ID string `json:"id"` - Headers map[string]string `json:"headers"` - } `json:"message"` + ID string `json:"id"` + Headers map[string]string `json:"headers"` + Body string `json:"body"` } - if err := json.Unmarshal([]byte(result.stdout), &parsed); err != nil { - t.Fatalf("decode JSON: %v", err) + if err := json.Unmarshal(envelope["message"], &parsed); err != nil { + t.Fatalf("decode sanitized message: %v", err) } if parsed.Body != "Hello [url removed]" { t.Fatalf("unexpected body: %q", parsed.Body) } - if parsed.Message.Headers["subject"] != "Visit [url removed] now" { - t.Fatalf("unexpected sanitized subject: %#v", parsed.Message.Headers) + if parsed.Headers["subject"] != "Visit [url removed] now" { + t.Fatalf("unexpected sanitized subject: %#v", parsed.Headers) + } + + result = executeWithGmailTestService( + t, + []string{"--json", "--results-only", "--account", "a@b.com", "gmail", "get", "m1", "--sanitize-content"}, + newGmailServiceFromServer(t, srv), + ) + if result.err != nil { + t.Fatalf("Execute with --results-only: %v\nstderr=%q", result.err, result.stderr) + } + var direct gmailSanitizedMessageOutput + if err := json.Unmarshal([]byte(result.stdout), &direct); err != nil { + t.Fatalf("decode direct sanitized message: %v", err) + } + if direct.ID != parsed.ID || direct.Body != parsed.Body || direct.Headers["subject"] != parsed.Headers["subject"] { + t.Fatalf("--results-only did not unwrap the sanitized message: %s", result.stdout) } }