Skip to content

--wrap-untrusted wraps Gmail header names, so a lookup by name finds nothing #1183

Description

@postoso

With --wrap-untrusted, the name of every raw Gmail header is wrapped in untrusted-content markers along with its value, so select(.name == "From") matches nothing.

Tested with v0.43.0 (3b5122f4) on macOS:

gog --wrap-untrusted gmail get <id> --json | jq '[.message.payload.headers[] | select(.name == "From")] | length'
# 0
gog gmail get <id> --json | jq '[.message.payload.headers[] | select(.name == "From")] | length'
# 1

Each wrapped name looks like this:

"<<<EXTERNAL_UNTRUSTED_CONTENT id=...>>>\nSource: google_api\n---\nFrom\n<<<END_EXTERNAL_UNTRUSTED_CONTENT id=...>>>"

gmail thread get <tid> --full --json does the same for every header in .thread.messages[].payload.headers[]. Without --wrap-untrusted the names are plain.

The cause looks like the key rule in internal/outfmt/untrusted.go: name and value are content keys regardless of where they sit, so a header entry {name, value} gets both wrapped. Header names are RFC 5322 field names, not message content.

func shouldWrapUntrustedString(path []string, key string, value string) bool {
if value == "" {
return false
}
normalizedKey := normalizeJSONKey(key)
if normalizedKey == "name" && len(path) >= 3 {
parent := normalizeJSONKey(path[len(path)-2])
grandparent := normalizeJSONKey(path[len(path)-3])
if grandparent == "usermention" && parent == "user" && trustedChatUserResourcePattern.MatchString(value) {
return false
}
if grandparent == "emoji" && parent == "customemoji" && trustedChatCustomEmojiResourcePattern.MatchString(value) {
return false
}
}
if untrustedMetadataStringKeys[normalizedKey] {
return false
}
if untrustedContentStringKeys[normalizedKey] {
return true
}
for _, part := range path {
if untrustedContentArrayKeys[normalizeJSONKey(part)] {
return true
}
}
return false
}
func normalizeJSONKey(key string) string {
key = strings.ReplaceAll(key, "_", "")
key = strings.ReplaceAll(key, "-", "")
return strings.ToLower(strings.TrimSpace(key))
}
var untrustedContentStringKeys = map[string]bool{
"a1": true,
"answer": true,
"body": true,
"comment": true,
"content": true,
"description": true,
"descriptionheading": true,
"displayname": true,
"errormessage": true,
"formattedaddress": true,
"formattedtext": true,
"formattedvalue": true,
"inputmessage": true,
"location": true,
"message": true,
"name": true,
"note": true,
"notes": true,
"question": true,
"quote": true,
"raw": true,
"replyto": true,
"renderedtext": true,
"sender": true,
"sheet": true,
"snippet": true,
"subject": true,
"summary": true,
"text": true,
"title": true,
"value": true,
}

Expected: header names stay plain and only values are wrapped, for example by exempting name when the parent path element is headers.

Separately noticed in the flattened .message.headers object: subject and reply_to are wrapped but from is not, even though the From display name is just as sender-controlled. That may be deliberate. I only checked gmail get and gmail thread get.

Activity

  1. added
    P2Normal priority bug or improvement with limited blast radius.
    clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.
    clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.
    clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.
    impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.
    issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.
    on Oct 2, 2026
  2. clawsweeper commented on Oct 2, 2026

    @clawsweeper
    Contributor

    Codex review: this still needs some work. Reviewed October 3, 2026, 12:51 AM ET / 04:51 UTC.

    Summary
    The header-lookup defect remains on current main and v0.43.0. No open fixing PR was found; the repair needs a narrowly defined exemption from the untrusted-content boundary.

    Reproducibility: yes. the reported wrapped/unwrapped Gmail reads provide a concrete reproduction path, and current source deterministically wraps From in raw header entries. This review inspected source without executing Gmail or tests.

    Maintainer decision needed

    Question Recommendation
    Should unwrapped Gmail header identifiers be limited to recognized standard names, or include every syntactically validated custom field name? Preserve recognized identifiers: Start with recognized standard names at the Gmail payload-header boundary and keep custom or malformed names wrapped.

    Why: The shared formatter treats fetched names as untrusted content; syntax validity alone does not settle which externally chosen identifiers may bypass that protection.

    Next step
    Confirm the trusted header-identifier boundary before a focused repair with value-wrapping regressions and a credited Unreleased entry.

    Security
    Needs attention: The defect is concrete, but its exemption must preserve the agent-facing untrusted-content boundary.

    Review details

    Best possible solution:

    Use a Gmail-specific, validated header-identifier exemption that preserves lookups while retaining value wrapping and protection for unrelated names.

    Do we have a high-confidence way to reproduce the issue?

    Yes: the reported wrapped/unwrapped Gmail reads provide a concrete reproduction path, and current source deterministically wraps From in raw header entries. This review inspected source without executing Gmail or tests.

    Is this the best way to solve the issue?

    Unclear: a path-aware exception is the right repair shape, but a parent-key-only exemption is broader than the existing validated metadata pattern.

    AGENTS.md: found and applied where relevant.

    Remaining risk / open question:

    • Exempting every name beneath any headers key could leave unrelated or malformed external text unwrapped; the permitted Gmail identifier boundary must be explicit.

    Codex review notes: model internal, reasoning medium; reviewed against 414e2ff8afa2.

    Label changes

    Label changes:

    • add clawsweeper:needs-security-review: Current issue advisory state selects this label.

    Label justifications:

    • P2: An opt-in output mode breaks established Gmail header selection with a limited blast radius.
    • impact:other: The concrete impact is broken scriptable header lookup, rather than message delivery or authentication.
    Evidence reviewed

    Security concerns:

    • [low] Define the header-name exemption narrowly — internal/outfmt/untrusted.go:224
      The suggested parent-key exception would bypass wrapping for arbitrary fetched names under headers; constrain it to the approved Gmail identifier boundary while preserving value and malformed-name protection.
      Confidence: 0.9

    What I checked:

    • Current source establishes the defect: Array traversal preserves the headers path. The classifier exempts validated Chat resource names, but otherwise classifies both name and value as content, so a raw Gmail header named From cannot survive unchanged. (internal/outfmt/untrusted.go:219, 414e2ff8afa2)
    • Both reported command paths are affected: Message JSON includes the original Gmail message and calls WriteJSON. Thread JSON likewise includes the original thread at internal/cmd/gmail_thread.go:90-108. WriteJSON applies the shared wrapper before encoding. (internal/cmd/gmail_get.go:94, 414e2ff8afa2)
    • Release and default-branch checks: The main branch API returned the inspected checkout SHA. The v0.43.0 classifier has the same global name/value classification and no Gmail header-name exception. The reporter supplied a macOS wrapped/unwrapped comparison yielding zero versus one From matches; this review did not execute Gmail. (internal/outfmt/untrusted.go:219, 3b5122f4c81c)
    • Existing output and safety contracts: Raw API documentation describes marking free text while preserving machine-usable metadata. Formatter tests preserve narrowly validated Chat identifiers while keeping malformed identifiers and display names wrapped. Gmail tests explicitly require instruction-like Reply-To values to remain wrapped. (internal/outfmt/untrusted_test.go:147, 414e2ff8afa2)
    • Related merged work does not fix this defect: Wrap fetched content in untrusted output markers #577 established the opt-in wrapping boundary. feat(gmail): expose Reply-To in flattened headers #1057 added flattened Reply-To and retained wrapping of its sender-controlled value; neither provides a raw header-name exemption. (internal/outfmt/untrusted.go:284, e29e676cefca)
    • Current implementation ownership context: File history and GitHub commit metadata connect steipete to validated metadata exemptions and hashtag1974 to Reply-To wrapping. GitHub metadata also connects VACInc to the original wrapper feature. Local blame and historical patch inspection encountered unavailable blobs; exact source-line introduction was not established. (internal/outfmt/untrusted.go, 414e2ff8afa2)

    Likely related people:

    • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
    • hashtag1974: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
    • VACInc: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
    How this review workflow works
    • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
    • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
    • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
    • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
    • Maintainers can also comment @clawsweeper review to request a fresh review only.
    • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
    • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
    • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
  3. steipete commented on Oct 7, 2026

    @steipete
    Collaborator

    Fixed in #1191. Standard ASCII header identifiers now remain usable for name-based selection at Gmail payload-header paths (including nested MIME parts). Custom/malformed names and all raw values remain wrapped; flattened sender and recipient display text is now wrapped too.

    The new synthetic regressions failed before the repair for message, thread, draft, and raw output. After the fix, focused formatter tests and the full make ci gate passed on AWS Crabbox (cbx_82fe63e89d13, run run_d775bb5d1b14177e01af47d72b9f5411). Final independent Codex review found no P0–P2 issues. Exact-head CI passed at ba3f9c48194ab95ed337992b443b8b4418c47174, including Linux, minimum Go, macOS, Windows, worker, Docker, and CodeQL: https://github.com/openclaw/gogcli/actions/runs/37571124228

    Thanks @postoso for identifying both output inconsistencies.

  4. added a commit that references this issue on Oct 7, 2026
    0cd6ccf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal priority bug or improvement with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions