Skip to content

MM-67547: Sanitize Bifrost provider errors before log and user surfaces#579

Open
nickmisasi wants to merge 2 commits intomasterfrom
MM-67547
Open

MM-67547: Sanitize Bifrost provider errors before log and user surfaces#579
nickmisasi wants to merge 2 commits intomasterfrom
MM-67547

Conversation

@nickmisasi
Copy link
Copy Markdown
Collaborator

@nickmisasi nickmisasi commented Mar 30, 2026

Summary

Upstream LLM providers sometimes echo credential material inside error bodies (for example incorrect API key messages, bearer tokens, or JSON apiKey fields). This change redacts those patterns before Bifrost errors are returned, streamed to clients, or logged.

Changes

  • Add llm.SanitizeProviderError / SanitizeProviderErrorMessage with the same redaction approach used elsewhere for OpenAI-style errors (regex passes plus configured-key replacement, then non-printable character sanitization).
  • Apply sanitization to all Bifrost error paths: chat and Responses streaming, embeddings, transcription, and model listing.

Ticket

https://mattermost.atlassian.net/browse/MM-67547

Summary by CodeRabbit

  • New Features

    • Added provider error sanitization to redact sensitive API keys and credentials from error messages.
  • Bug Fixes

    • Unified error handling across chat, embeddings, model listing, and transcription flows to emit sanitized errors instead of raw provider error text.
  • Tests

    • Added comprehensive tests to verify redaction behavior, preservation of error wrapping, and coverage for varied provider error formats.

Redact API keys and tokens from Bifrost error messages using shared llm
sanitization (ported from mattermost-plugin-agents OpenAI path).

Made-with: Cursor
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 30, 2026

🤖 LLM Evaluation Results

OpenAI

⚠️ Overall: 18/19 tests passed (94.7%)

Provider Total Passed Failed Pass Rate
⚠️ OPENAI 19 18 1 94.7%

❌ Failed Evaluations

Show 1 failures

OPENAI

1. TestReactEval/[openai]_react_cat_message

  • Score: 0.00
  • Rubric: The word/emoji is a cat emoji or a heart/love emoji
  • Reason: The output is the text "smiley_cat", which is neither a cat emoji (e.g., 😺/🐱) nor a heart/love emoji (e.g., ❤️/💖).

Anthropic

Overall: 19/19 tests passed (100.0%)

Provider Total Passed Failed Pass Rate
✅ ANTHROPIC 19 19 0 100.0%

This comment was automatically generated by the eval CI pipeline.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Stale comment

Security Review: No Issues Found

This PR adds proper redaction of API keys and secrets from Bifrost provider error messages across all error surfaces (streaming, embeddings, transcription, model listing). The implementation is sound:

  • Regex patterns cover the major provider key formats (OpenAI, Anthropic, bearer tokens, JSON fields), plus a configured-key fallback.
  • regexp.QuoteMeta prevents regex injection from the configured key, and Go's RE2 engine prevents ReDoS.
  • All downstream code paths that consume these errors use .Error(), which returns the sanitized message.
  • The Unwrap() chain preserves the original error for errors.Is/errors.As compatibility — standard Go practice. No current code path traverses the error chain in a way that would expose unsanitized content.

No medium, high, or critical vulnerabilities identified.

Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 139d9e68-e865-45c5-862b-8c9678638bd5

📥 Commits

Reviewing files that changed from the base of the PR and between 35dd39b and 9125387.

📒 Files selected for processing (1)
  • bifrost/bifrost.go
✅ Files skipped from review due to trivial changes (1)
  • bifrost/bifrost.go

📝 Walkthrough

Walkthrough

This PR adds provider-error sanitization utilities and integrates them into Bifrost components by storing API keys on provider structs and passing errors through llm.SanitizeProviderError(...) before emitting or returning them.

Changes

Cohort / File(s) Summary
Bifrost core & streaming
bifrost/bifrost.go
Added apiKey to LLM and updated streaming error emissions to wrap errors with llm.SanitizeProviderError(..., b.apiKey) before sending EventTypeError.
Embeddings provider
bifrost/embeddings.go
EmbeddingProvider stores apiKey; embedding request failures are wrapped with llm.SanitizeProviderError before returning.
Model listing
bifrost/models.go
FetchModels now wraps model-listing errors with llm.SanitizeProviderError(..., cfg.APIKey) instead of returning raw formatted errors.
Transcription
bifrost/transcription.go
Transcriber stores apiKey; transcription request errors are sanitized via llm.SanitizeProviderError before being returned.
Sanitization utility & tests
llm/provider_error.go, llm/provider_error_test.go
New sanitization module that redacts API keys, bearer headers, and non-printable chars; returns SanitizedProviderError when redaction occurs. Comprehensive tests added covering formats, edge cases, and error-wrapping behavior.

Sequence Diagram(s)

(Skipped — changes are primarily error sanitization plumbing and new utility, not a multi-actor control flow requiring sequence diagrams.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Setup Cloud Test Server

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding sanitization of Bifrost provider errors before they are exposed to logs and user surfaces.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MM-67547

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bifrost/bifrost.go`:
- Around line 36-37: The LLM struct currently stores only apiKey causing AWS
creds to be omitted from redaction; add a field like redactionSecrets []string
on the LLM/bifrost struct, populate it from cfg.APIKey, cfg.AWSAccessKeyID and
cfg.AWSSecretAccessKey when constructing the struct, and remove the single
apiKey-only usage, then update llm.SanitizeProviderError to accept variadic
secrets (...string) and change all call sites to pass b.redactionSecrets...
(e.g., where SanitizeProviderError(...) is invoked) so Bedrock/AWS credential
values are included in the redaction set.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b8def679-242d-4b6f-b680-6d6ea6632590

📥 Commits

Reviewing files that changed from the base of the PR and between 7ae2f8e and 35dd39b.

📒 Files selected for processing (6)
  • bifrost/bifrost.go
  • bifrost/embeddings.go
  • bifrost/models.go
  • bifrost/transcription.go
  • llm/provider_error.go
  • llm/provider_error_test.go

Comment on lines +36 to 37
apiKey string // used only to redact configured secrets from provider error surfaces
defaultModel string
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't drop Bedrock credentials from the redaction set.

LLM now retains only cfg.APIKey, but this config also accepts AWSAccessKeyID and AWSSecretAccessKey. Those never reach llm.SanitizeProviderError, so a Bedrock auth error that echoes either value will still leak it through the new streamed error paths. Please carry a full secret list here instead of a single API key.

🔐 Suggested direction
 type LLM struct {
 	client           *bifrostcore.Bifrost
 	provider         schemas.ModelProvider
-	apiKey           string // used only to redact configured secrets from provider error surfaces
+	redactionSecrets []string
 	defaultModel     string
 	inputTokenLimit  int
 	outputTokenLimit int
 	streamingTimeout time.Duration
@@
 	return &LLM{
 		client:             client,
 		provider:           cfg.Provider,
-		apiKey:             cfg.APIKey,
+		redactionSecrets: []string{
+			cfg.APIKey,
+			cfg.AWSAccessKeyID,
+			cfg.AWSSecretAccessKey,
+		},
 		defaultModel:       cfg.DefaultModel,
 		inputTokenLimit:    cfg.InputTokenLimit,

Then widen the llm.SanitizeProviderError(...) helpers to accept ...string and pass b.redactionSecrets... at the call sites.

Also applies to: 200-200

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bifrost/bifrost.go` around lines 36 - 37, The LLM struct currently stores
only apiKey causing AWS creds to be omitted from redaction; add a field like
redactionSecrets []string on the LLM/bifrost struct, populate it from
cfg.APIKey, cfg.AWSAccessKeyID and cfg.AWSSecretAccessKey when constructing the
struct, and remove the single apiKey-only usage, then update
llm.SanitizeProviderError to accept variadic secrets (...string) and change all
call sites to pass b.redactionSecrets... (e.g., where SanitizeProviderError(...)
is invoked) so Bedrock/AWS credential values are included in the redaction set.

@nickmisasi
Copy link
Copy Markdown
Collaborator Author

/update-branch

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Security Review: No Issues Found

This PR adds proper redaction of API keys and secrets from Bifrost provider error messages across all error surfaces (streaming chat/responses, embeddings, transcription, model listing). The implementation is sound:

  • Regex injection safe: regexp.QuoteMeta is correctly used on the configured API key before building the replacement regex, preventing attacker-controlled keys from injecting regex patterns. Go's RE2 engine also prevents ReDoS.
  • All Bifrost error paths covered: Every bifrostErr.Error.Message site is now wrapped with SanitizeProviderError.
  • Unwrap() chain is safe: The SanitizedProviderError.Unwrap() returns the original unsanitized error, which is standard Go practice for errors.Is/errors.As compatibility. I verified that no downstream code in the repository traverses the error chain to extract .Error() from the unwrapped original — all consumers (streaming/streaming.go L465, llm/stream.go L84, api/api_llm_bridge.go L227) call .Error() on the top-level wrapper, which returns the sanitized message.
  • Pattern coverage: Regex patterns cover OpenAI keys (sk-*, sk-proj-*), Anthropic keys (sk-ant-*), Bearer authorization headers, JSON apiKey fields, and the "Incorrect API key provided" message format. The configured key fallback catch-all handles provider-specific key formats not matched by the static patterns.
  • Stored apiKey field: The field is unexported (lowercase), not serialized, and only used for redaction comparisons — no new exposure surface.

No medium, high, or critical vulnerabilities identified.

Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

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.

2 participants