Skip to content

redact bearer tokens from captured deploy logs - #1829

Open
mernit wants to merge 1 commit into
mainfrom
eli/redact-token-json-logs
Open

redact bearer tokens from captured deploy logs#1829
mernit wants to merge 1 commit into
mainfrom
eli/redact-token-json-logs

Conversation

@mernit

@mernit mernit commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • beam deploy --format json captures everything printed during the deploy into the JSON logs array — including the "Invocation details" curl/websocat examples, which embed the full workspace bearer token (sdk/src/beta9/abstractions/base/runner.py). Anyone piping --format json output into CI logs or files was persisting a live credential; a user hit exactly this and had to rotate their tokens.
  • StoredStdoutInterceptor now redacts Authorization: Bearer <token> values in captured logs (Bearer [REDACTED]), at the capture choke point so any current or future print during a JSON-formatted deploy is covered.
  • The interactive (non-JSON) invocation snippet is unchanged, so copy-paste curl examples still work.

Fixes #1828

Test plan

  • New unit tests: curl-style and websocat-style header redaction, non-matching output untouched, and end-to-end capture through StoredStdoutInterceptor (sdk/tests/test_logging.py)

Co-authored-by: Cursor cursoragent@cursor.com

Made with Cursor

beam deploy --format json captured the printed invocation examples
(curl/websocat) verbatim, so the JSON logs array included the full
workspace bearer token — which then leaked into CI logs and files.
Captured logs now redact Authorization header values; the interactive
copy-paste snippet is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="sdk/src/beta9/logging.py">

<violation number="1" location="sdk/src/beta9/logging.py:13">
P2: The bearer-token redactor only strips values whose characters all fall inside the RFC 6750 token68 charset (`[A-Za-z0-9-._~+/=]`). The canonical gateway-generated token is base64url and is fully covered, but the SDK also builds tokens from caller-provided values via `BETA9_TOKEN`/`BEAM_TOKEN` or the interactive Token prompt, which accept arbitrary >=64-char strings. If such a token contains a character outside that charset (e.g. `%`, `!`, `#`, `@`, `:`), redaction stops at the first offending character and the rest of the live credential leaks into the JSON `logs` that `beam deploy --format json` persists to CI logs/files — the exact scenario this PR is intended to fix. Consider matching a broader “run up to whitespace/quotes” token instead of restricting to token68, so any non-whitespace/non-quote token is fully replaced; over-redacting is safer than under-redacting for this capture path.</violation>

<violation number="2" location="sdk/src/beta9/logging.py:167">
P1: Captured JSON deploy logs can still persist a live bearer token when the output is split across writes, because redaction runs independently on each `write()` fragment. Buffer captured text across writes or redact the complete assembled log entries before returning the JSON output.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread sdk/src/beta9/logging.py
# Captured logs end up in machine-readable output (e.g. deploy
# --format json) that gets piped into CI logs and files, so strip
# credentials that are fine to show interactively.
data = redact_bearer_tokens(data)

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.

P1: Captured JSON deploy logs can still persist a live bearer token when the output is split across writes, because redaction runs independently on each write() fragment. Buffer captured text across writes or redact the complete assembled log entries before returning the JSON output.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk/src/beta9/logging.py, line 167:

<comment>Captured JSON deploy logs can still persist a live bearer token when the output is split across writes, because redaction runs independently on each `write()` fragment. Buffer captured text across writes or redact the complete assembled log entries before returning the JSON output.</comment>

<file context>
@@ -147,6 +160,11 @@ def __exit__(self, *_, **__):
+            # Captured logs end up in machine-readable output (e.g. deploy
+            # --format json) that gets piped into CI logs and files, so strip
+            # credentials that are fine to show interactively.
+            data = redact_bearer_tokens(data)
         self.logs.append(data)
         if not self.capture_logs:
</file context>

Comment thread sdk/src/beta9/logging.py
# invocation examples). Restricted to the RFC 6750 token68 charset so
# surrounding quotes survive redaction.
_BEARER_TOKEN_PATTERN = re.compile(
r"(Authorization:\s*Bearer\s+)[A-Za-z0-9\-._~+/=]+", re.IGNORECASE

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.

P2: The bearer-token redactor only strips values whose characters all fall inside the RFC 6750 token68 charset ([A-Za-z0-9-._~+/=]). The canonical gateway-generated token is base64url and is fully covered, but the SDK also builds tokens from caller-provided values via BETA9_TOKEN/BEAM_TOKEN or the interactive Token prompt, which accept arbitrary >=64-char strings. If such a token contains a character outside that charset (e.g. %, !, #, @, :), redaction stops at the first offending character and the rest of the live credential leaks into the JSON logs that beam deploy --format json persists to CI logs/files — the exact scenario this PR is intended to fix. Consider matching a broader “run up to whitespace/quotes” token instead of restricting to token68, so any non-whitespace/non-quote token is fully replaced; over-redacting is safer than under-redacting for this capture path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk/src/beta9/logging.py, line 13:

<comment>The bearer-token redactor only strips values whose characters all fall inside the RFC 6750 token68 charset (`[A-Za-z0-9-._~+/=]`). The canonical gateway-generated token is base64url and is fully covered, but the SDK also builds tokens from caller-provided values via `BETA9_TOKEN`/`BEAM_TOKEN` or the interactive Token prompt, which accept arbitrary >=64-char strings. If such a token contains a character outside that charset (e.g. `%`, `!`, `#`, `@`, `:`), redaction stops at the first offending character and the rest of the live credential leaks into the JSON `logs` that `beam deploy --format json` persists to CI logs/files — the exact scenario this PR is intended to fix. Consider matching a broader “run up to whitespace/quotes” token instead of restricting to token68, so any non-whitespace/non-quote token is fully replaced; over-redacting is safer than under-redacting for this capture path.</comment>

<file context>
@@ -2,9 +2,22 @@
+# invocation examples). Restricted to the RFC 6750 token68 charset so
+# surrounding quotes survive redaction.
+_BEARER_TOKEN_PATTERN = re.compile(
+    r"(Authorization:\s*Bearer\s+)[A-Za-z0-9\-._~+/=]+", re.IGNORECASE
+)
+
</file context>
Suggested change
r"(Authorization:\s*Bearer\s+)[A-Za-z0-9\-._~+/=]+", re.IGNORECASE
_BEARER_TOKEN_PATTERN = re.compile(
r"(Authorization:\s*Bearer\s+)[^\s'\";]+", re.IGNORECASE
)

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.

beam deploy --format json includes the workspace bearer token in the logs array

1 participant