Skip to content

refactor: encapsulate SecretVar internals behind typed SecretType enum and accessor methods#4599

Closed
BearTS wants to merge 3 commits into
06-22-fix_add_missing_check_for_vaultfrom
06-22-feat_make_secretref_and_fromsecret_non_global_fields
Closed

refactor: encapsulate SecretVar internals behind typed SecretType enum and accessor methods#4599
BearTS wants to merge 3 commits into
06-22-fix_add_missing_check_for_vaultfrom
06-22-feat_make_secretref_and_fromsecret_non_global_fields

Conversation

@BearTS

@BearTS BearTS commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

SecretVar's internal fields SecretRef and FromSecret were previously exported, allowing callers to read and write them directly. This PR encapsulates those fields as unexported (secretRef, SecretType) and exposes them through a controlled public API (GetSecretRef(), IsFromSecret(), IsFromVault(), IsFromEnv()). A new SecretType enum (plain_text, env, vault) replaces the boolean FromSecret flag, making the source of a secret explicit. JSON serialization is handled via an explicit MarshalJSON method that emits secret_ref and type fields, with full backward compatibility for the legacy from_secret, env_var, and from_env shapes on unmarshal.

Changes

  • SecretRef and FromSecret fields on SecretVar are now unexported (secretRef). FromSecret bool is replaced by SecretType SecretType with constants SecretTypePlainText, SecretTypeEnv, and SecretTypeVault.
  • A new GetSecretRef() string accessor exposes the secret reference string.
  • A new Type() SecretType accessor exposes the secret type.
  • A new IsFromEnv() bool method is added alongside the existing IsFromSecret() and IsFromVault().
  • An explicit MarshalJSON method is added so JSON serialization emits secret_ref and type fields correctly despite the fields being unexported.
  • inferSecretType is introduced to derive the SecretType from a reference string prefix when reading legacy formats.
  • All internal usages of SecretRef and FromSecret across proxy/TLS configuration, config handlers, hash generation, MCP table serialization, vault operations, and config store are updated to use the new accessor methods.
  • Tests are updated to use GetSecretRef(), IsFromSecret(), IsFromVault(), and IsFromEnv() instead of direct field access, and test fixtures constructing SecretVar struct literals are migrated to NewSecretVar or the new typed field form.
  • The JSON config schema is updated to document secret_ref and type alongside the legacy env_var/from_env fields.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./core/schemas/... ./core/providers/utils/... ./framework/configstore/... ./transports/bifrost-http/...

Verify that secret references (env and vault) round-trip correctly through JSON marshal/unmarshal, that GetSecretRef() returns the expected reference string, and that IsFromSecret(), IsFromVault(), and IsFromEnv() behave as expected. Confirm that legacy JSON payloads using from_secret, env_var, and from_env fields continue to deserialize correctly.

Breaking changes

  • Yes
  • No

Any code outside this repository that directly accessed SecretVar.SecretRef or SecretVar.FromSecret will no longer compile. Callers must migrate to GetSecretRef() and IsFromSecret() respectively. Code constructing SecretVar struct literals with those fields must be updated to use NewSecretVar or the new SecretType-based form. The JSON serialization shape changes from emitting from_secret/env_var/from_env to emitting secret_ref and type; consumers reading that JSON must handle the new format.

Security considerations

Encapsulating secretRef as an unexported field and replacing the ambiguous FromSecret bool with an explicit SecretType enum prevents external packages from constructing a SecretVar in an inconsistent state (e.g., setting FromSecret: true without a valid reference, or bypassing the constructor's resolution logic). This reduces the risk of accidentally leaking or misrepresenting secret reference metadata in API responses.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ce995e09-e547-490e-a0c2-8a7150e438b3

📥 Commits

Reviewing files that changed from the base of the PR and between 6ccf345 and f1142a1.

📒 Files selected for processing (18)
  • core/providers/utils/utils.go
  • core/schemas/secretvar.go
  • core/schemas/secretvar_test.go
  • core/schemas/vault.go
  • core/schemas/vault_test.go
  • framework/configstore/clientconfig.go
  • framework/configstore/clientconfig_redaction_test.go
  • framework/configstore/encryption_test.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/encryption_test.go
  • framework/configstore/tables/mcp.go
  • framework/configstore/tables/virtualkey_secretvar_test.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/ctx_test.go
  • transports/config.schema.json

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved error messages when secret-derived proxy/TLS configuration values resolve to empty strings.
    • Clearer admin credential warnings and better preservation of secret reference metadata during admin password update flows.
    • Updated config hashing/redaction so change detection and stored serialized secret references are consistent for secret-backed fields.
  • New Features

    • Expanded API/config validation to accept secret-backed shapes for the external MCP client URL (with reference/type metadata).

Walkthrough

SecretVar's exported SecretRef and FromSecret fields are replaced by an internal secretRef field and a new public SecretType discriminator enum. JSON serialization now uses secret_ref and type fields with support for legacy formats. All construction, serialization, vault integration, configstore hashing/storage, HTTP handlers, provider utilities, and test assertions are updated to use new accessor methods (GetSecretRef(), Type(), IsFromEnv()) and revised state-branching logic based on SecretType.

Changes

SecretVar struct refactor and API surface change

Layer / File(s) Summary
SecretVar struct, SecretType discriminator, and core accessors
core/schemas/secretvar.go
SecretType enum introduced with PlainText, Env, Vault constants. SecretVar struct replaces exported SecretRef/FromSecret with public SecretType and internal secretRef. New accessors: GetSecretRef(), Type(), IsFromEnv(). Helper methods IsFromSecret, IsFromVault, IsRedacted, Equals, ShouldPreserveStored, IsSet updated to branch on SecretType.
SecretVar construction and reference resolution
core/schemas/secretvar.go
NewSecretVar parses plain reference strings (vault.*, env.*) and JSON with new (type/secret_ref) or legacy (env_var/from_env) formats. Infers SecretType and populates secretRef. Resolves vault/env references into Val based on derived type.
SecretVar serialization and database persistence
core/schemas/secretvar.go
MarshalJSON emits secret_ref and type. UnmarshalJSON decodes new, legacy, and plain-string reference formats, populating secretRef/SecretType and resolving into Val. Scan reads vault./env. prefixed DB strings to restore secretRef/SecretType. Value persists secretRef when IsFromSecret() true; otherwise stores Val.
SecretVar redaction and metadata preservation
core/schemas/secretvar.go
Redacted and FullyRedacted preserve internal secretRef/SecretType in returned copies, ensuring secret-reference metadata is not lost during redaction.
SecretVar unit tests and marshaling validation
core/schemas/secretvar_test.go
Test fixtures and assertions updated to use GetSecretRef(), IsFromSecret(), IsFromVault() instead of field access. New TestSecretVar_MarshalJSON validates JSON output for plain, env-ref, and vault-ref values with secret_ref/type fields.

Vault storage and removal integration with SecretType

Layer / File(s) Summary
Vault store and removal with internal fields
core/schemas/vault.go
removeOwnedVaultSecretVar checks SecretType == SecretTypeVault and reads secretRef. StoreVaultSecretVar writes e.secretRef = "vault."+path and sets e.SecretType = SecretTypeVault.
Vault unit tests
core/schemas/vault_test.go
Store/removal tests updated to construct fixtures with secretRef/SecretType and assert via IsFromVault()/GetSecretRef(). Owned-field and owned-map tests verify vault-backed conversions and env-sourced entries.

Configstore hashing, storage, and redaction

Layer / File(s) Summary
Config hash generation
framework/configstore/clientconfig.go
GenerateClientConfigHash, GenerateKeyHash, GenerateMCPClientHash replace .SecretRef with .GetSecretRef() in SHA256 hash inputs.
MCP client storage and headers
framework/configstore/rdb.go, framework/configstore/tables/mcp.go
UpdateMCPClientConfig and TableMCPClient.BeforeSave switch from value.SecretRef to value.GetSecretRef() when serializing secret-backed headers for encrypted storage.
Redaction test validation
framework/configstore/clientconfig_redaction_test.go
Tests updated to expect secret_ref/type JSON fields instead of legacy env_var/from_env, validating reference metadata and type discriminators in redacted output.

Transport handlers and HTTP config flow

Layer / File(s) Summary
HTTP config handler
transports/bifrost-http/handlers/config.go
getConfig uses FullyRedacted() for "from secret" admin password redaction. updateConfig interpolates GetSecretRef() in validation errors; password hashing preserves existing secret metadata by cloning SecretVar and updating only Val.
HTTP config lib
transports/bifrost-http/lib/config.go
preserveSecretVar clones secret-backed instances and overwrites only .Val. loadAuthConfig logs unresolved references via GetSecretRef() and removes prior early-return fallback.
Plugins handler
transports/bifrost-http/handlers/plugins.go
isSecretVarObject broadens detection to require type or legacy from_secret for secret_ref maps. marshalSecretVarObject emits type field when present for new format compatibility.

Provider utilities and schema extension

Layer / File(s) Summary
Provider error formatting
core/providers/utils/utils.go
ConfigureProxy and ConfigureTLS format unresolved secret URL/CA errors using GetSecretRef() instead of direct field access.
Schema utilities and validation
core/schemas/utils.go, transports/config.schema.json
SecretVarAsString returns secretRef for secret-backed values. mcp_external_client_url schema extended with secret_ref, type, from_secret properties.

Encryption and persistence tests

Layer / File(s) Summary
Encryption round-trip and field validation
framework/configstore/encryption_test.go, framework/configstore/tables/encryption_test.go
Encryption tests verify secret-backed fields report IsFromSecret() true and are not encrypted. Round-trip tests validate that env-reference metadata survives database saves/loads via GetSecretRef() and IsFromSecret().
Provider config round-trip tests
framework/configstore/tables/encryption_test.go
TestTableKey_*UnresolvedSecretVar_RoundTrip tests refactored to use NewSecretVar("env.<VAR>") constructors. Assertions validate GetSecretRef() and IsFromSecret() for preserved env references across GORM round-trips.
Virtual key and auth tests
framework/configstore/tables/virtualkey_secretvar_test.go, transports/bifrost-http/lib/config_test.go, transports/bifrost-http/lib/ctx_test.go
Virtual-key JSON and round-trip tests, auth credential hashing tests, and direct-key validation tests updated to use IsFromSecret()/GetSecretRef() for reference metadata validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • akshaydeo
  • danpiths

Poem

🐇 The rabbit sealed the secret tight,
With types that set the reference right.
No more exported fields in sight—
Just SecretType to mark the light.
From vault to env, the metadata stays,
Through redaction's haze and cryptic ways. 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main refactoring change: encapsulating SecretVar fields and exposing them through accessor methods (Ref()/IsFromSecret()) with explicit MarshalJSON.
Description check ✅ Passed The PR description is comprehensive and includes all required template sections: Summary, Changes, Type of change, Affected areas, How to test, Breaking changes, Security considerations, and a completed Checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 06-22-feat_make_secretref_and_fromsecret_non_global_fields

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

@BearTS BearTS changed the title feat: make secretRef and fromSecret non global fields refactor: encapsulate SecretVar fields and expose via Ref()/IsFromSecret() accessors with explicit MarshalJSON Jun 22, 2026
@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

Safe to merge after restoring the early-return fail-closed guards in loadAuthConfig for unresolved admin credentials.

The loadAuthConfig function in transports/bifrost-http/lib/config.go lost its protective early-return path when an env/vault-backed admin username or password resolves to an empty string. In the old code those guards preserved the existing DB credential and returned; in the new code they log a warning and fall through, causing the function to compare an empty password against the stored bcrypt hash (fails), call FlushSessions (drops all active sessions), and write an empty-string bcrypt hash to the database — overwriting the valid credential. All other accessor renames and encapsulation changes across the 19 files are mechanically correct.

transports/bifrost-http/lib/config.go — the loadAuthConfig function around lines 3694–3700 needs the early-return guards restored.

Important Files Changed

Filename Overview
core/schemas/secretvar.go Core encapsulation refactor: exports SecretRef/FromSecret replaced with unexported secretRef+SecretType, new accessors GetSecretRef()/IsFromSecret()/IsFromVault()/IsFromEnv(), and explicit MarshalJSON/UnmarshalJSON with backward-compat handling for old from_secret/env_var formats. No issues found.
transports/bifrost-http/lib/config.go Most accessor renames are correct, but the loadAuthConfig function loses its fail-closed early-return when admin credentials reference an unresolved env/vault secret — unresolved credentials now fall through and corrupt the stored auth config.
framework/configstore/clientconfig.go Hash generation updated to use GetSecretRef() in four places; logic is equivalent to the old direct field access.
framework/configstore/tables/mcp.go Header serialization updated from .SecretRef to .GetSecretRef() in BeforeSave; functionally identical.
core/schemas/secretvar_test.go Tests updated to use GetSecretRef()/IsFromSecret() and construct SecretVar literals with unexported fields (valid within the same package); coverage includes marshal/unmarshal round-trips, backward compat, redaction, vault refs, and scan.
core/schemas/vault.go StoreVaultSecretVar now directly assigns e.secretRef and e.SecretType instead of exported fields; removeOwnedVaultSecretVar accesses field.secretRef and field.SecretType correctly within the same package.
core/schemas/utils.go SecretVarAsString updated to use e.secretRef directly (same package) — no functional change.
framework/configstore/rdb.go Single accessor rename in UpdateMCPClientConfig header serialization; no behavioral change.
core/providers/utils/utils.go Simple accessor renames from .SecretRef to .GetSecretRef() in proxy/TLS error messages; no behavioral changes.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class SecretVar {
        +string Val
        -string secretRef
        +SecretType SecretType
        +GetSecretRef() string
        +IsFromSecret() bool
        +IsFromVault() bool
        +IsFromEnv() bool
        +IsSet() bool
        +IsRedacted() bool
        +Redacted() SecretVar
        +FullyRedacted() SecretVar
        +MarshalJSON() []byte
        +UnmarshalJSON(data []byte) error
        +Scan(value any) error
        +Value() driver.Value
    }
    class SecretType {
        <<enumeration>>
        plain_text
        env
        vault
    }
    class NewSecretVar {
        <<constructor>>
        +NewSecretVar(value string) SecretVar
    }
    SecretVar --> SecretType
    NewSecretVar --> SecretVar
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
classDiagram
    class SecretVar {
        +string Val
        -string secretRef
        +SecretType SecretType
        +GetSecretRef() string
        +IsFromSecret() bool
        +IsFromVault() bool
        +IsFromEnv() bool
        +IsSet() bool
        +IsRedacted() bool
        +Redacted() SecretVar
        +FullyRedacted() SecretVar
        +MarshalJSON() []byte
        +UnmarshalJSON(data []byte) error
        +Scan(value any) error
        +Value() driver.Value
    }
    class SecretType {
        <<enumeration>>
        plain_text
        env
        vault
    }
    class NewSecretVar {
        <<constructor>>
        +NewSecretVar(value string) SecretVar
    }
    SecretVar --> SecretType
    NewSecretVar --> SecretVar
Loading

Reviews (4): Last reviewed commit: "feat: add secret type instead of from se..." | Re-trigger Greptile

Comment thread core/schemas/secretvar.go Outdated
Comment thread core/schemas/secretvar.go Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/schemas/vault_test.go (1)

174-178: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen the env-map assertion to fail on unintended mutation (Line 174).

This check only verifies “not stored” when metadata still equals env.X; if the entry is mutated, the test can pass silently.

Suggested fix
-	if env := m.Headers["X-Env"]; env.IsFromSecret() && env.Ref() == "env.X" {
-		if stored["bifrost/m/1/headers/X-Env"] != "" {
-			t.Error("env-sourced header should not be vault-stored")
-		}
-	}
+	env := m.Headers["X-Env"]
+	if !env.IsFromSecret() || env.Ref() != "env.X" {
+		t.Errorf("env entry metadata mutated: val=%q ref=%q fromSecret=%v", env.Val, env.Ref(), env.IsFromSecret())
+	}
+	if stored["bifrost/m/1/headers/X-Env"] != "" {
+		t.Error("env-sourced header should not be vault-stored")
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/schemas/vault_test.go` around lines 174 - 178, The conditional check in
the headers validation block only performs the vault storage assertion when
env.Ref() equals "env.X", which means if the reference value gets mutated
unexpectedly, the condition becomes false and the assertion never runs, allowing
the bug to pass silently. Strengthen this test by either adding an explicit
assertion that verifies env.Ref() must equal "env.X" before performing the
storage check, or by moving the vault storage check outside of the conditional
logic to ensure it always validates that env-sourced headers are not stored in
the vault, regardless of how the metadata reference might change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/schemas/secretvar.go`:
- Around line 258-270: The SecretVar.MarshalJSON method is calling sonic.Marshal
directly instead of using the package-level Marshal() wrapper function for
consistency with other MarshalJSON implementations in the same package (such as
OptionalJSON.MarshalJSON). Replace the sonic.Marshal call with Marshal on the
line that returns the marshaled struct in the SecretVar.MarshalJSON method to
ensure consistency across the package.

---

Outside diff comments:
In `@core/schemas/vault_test.go`:
- Around line 174-178: The conditional check in the headers validation block
only performs the vault storage assertion when env.Ref() equals "env.X", which
means if the reference value gets mutated unexpectedly, the condition becomes
false and the assertion never runs, allowing the bug to pass silently.
Strengthen this test by either adding an explicit assertion that verifies
env.Ref() must equal "env.X" before performing the storage check, or by moving
the vault storage check outside of the conditional logic to ensure it always
validates that env-sourced headers are not stored in the vault, regardless of
how the metadata reference might change.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e2139b3-7596-4a90-a84f-bde2e848dcc7

📥 Commits

Reviewing files that changed from the base of the PR and between 8b61327 and 983be90.

📒 Files selected for processing (11)
  • core/providers/utils/utils.go
  • core/schemas/secretvar.go
  • core/schemas/secretvar_test.go
  • core/schemas/utils.go
  • core/schemas/vault.go
  • core/schemas/vault_test.go
  • framework/configstore/clientconfig.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/mcp.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config.go

Comment thread core/schemas/secretvar.go Outdated
@BearTS BearTS marked this pull request as ready for review June 22, 2026 07:34
@BearTS BearTS force-pushed the 06-22-feat_make_secretref_and_fromsecret_non_global_fields branch from 983be90 to 6ccf345 Compare June 22, 2026 08:38
@BearTS BearTS force-pushed the 06-22-fix_add_missing_check_for_vault branch from 8b61327 to cc29fc9 Compare June 22, 2026 08:38
@coderabbitai coderabbitai Bot requested a review from Pratham-Mishra04 June 22, 2026 08:40

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@transports/bifrost-http/lib/config.go`:
- Around line 3694-3700: In the config loading path around
authConfig.AdminUserName and authConfig.AdminPassword validation, when either
credential has IsFromSecret() returning true and GetValue() returning empty, add
an early return or skip mechanism to prevent persistence rather than just
logging a warning. Currently the code logs a warning but continues to persist
the empty credentials at line 3755, which overwrites valid passwords in the
database. Instead, when authConfig.AdminUserName or authConfig.AdminPassword
meets the condition (IsFromSecret() is true and GetValue() is empty), return
early from the function or set a flag to skip the persistence step entirely,
matching the error-handling behavior in the HTTP handler path.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a2634024-30f0-4713-b00d-193788c61971

📥 Commits

Reviewing files that changed from the base of the PR and between 983be90 and 6ccf345.

📒 Files selected for processing (11)
  • core/providers/utils/utils.go
  • core/schemas/secretvar.go
  • core/schemas/secretvar_test.go
  • core/schemas/utils.go
  • core/schemas/vault.go
  • core/schemas/vault_test.go
  • framework/configstore/clientconfig.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/mcp.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config.go
✅ Files skipped from review due to trivial changes (2)
  • framework/configstore/tables/mcp.go
  • core/providers/utils/utils.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • core/schemas/utils.go
  • framework/configstore/rdb.go
  • core/schemas/vault.go
  • framework/configstore/clientconfig.go
  • core/schemas/vault_test.go
  • core/schemas/secretvar.go
  • core/schemas/secretvar_test.go

Comment on lines 3694 to 3700
// Fail-closed: if env/vault reference is unresolved, don't persist empty credentials.
if authConfig.AdminUserName != nil && authConfig.AdminUserName.GetValue() == "" && authConfig.AdminUserName.IsFromSecret() {
logger.Warn("username set with external reference but value is empty: %s — skipping auth config", authConfig.AdminUserName.SecretRef)
if dbAuthConfig != nil {
config.GovernanceConfig.AuthConfig = dbAuthConfig
}
return
logger.Warn("username set with external reference but value is empty: %s", authConfig.AdminUserName.Ref())
}
if authConfig.AdminPassword != nil && authConfig.AdminPassword.GetValue() == "" && authConfig.AdminPassword.IsFromSecret() {
logger.Warn("password set with external reference but value is empty: %s — skipping auth config", authConfig.AdminPassword.SecretRef)
if dbAuthConfig != nil {
config.GovernanceConfig.AuthConfig = dbAuthConfig
}
return
logger.Warn("password set with external reference but value is empty: %s", authConfig.AdminPassword.Ref())
}

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there's any early-return or persistence skip logic for unresolved secrets
# after the warning logs in loadAuthConfig

# Search for the full loadAuthConfig function to see if there's protection downstream
ast-grep --pattern $'func loadAuthConfig($_, $_, $_) {
  $$$
}'

Repository: maximhq/bifrost

Length of output: 8787


Skip persistence when external secret references are unresolved, not just warn.

The comment claims "Fail-closed: if env/vault reference is unresolved, don't persist empty credentials," but the code only logs a warning (lines 3694–3700) and continues processing. When an unresolved environment variable causes GetValue() to return empty:

  1. Line 3705: filePassword becomes ""
  2. If the DB has an existing valid password, passwordMatch will be false (line 3712)
  3. Code continues past line 3725 without early return
  4. Empty password is hashed and persisted at line 3755, overwriting the valid DB password

The HTTP handler path (handlers/config.go) correctly returns an error for unresolved secrets. The config-file loading path should similarly return early or skip persistence when IsFromSecret() is true and the value is empty, to prevent credential loss during transient env var unavailability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@transports/bifrost-http/lib/config.go` around lines 3694 - 3700, In the
config loading path around authConfig.AdminUserName and authConfig.AdminPassword
validation, when either credential has IsFromSecret() returning true and
GetValue() returning empty, add an early return or skip mechanism to prevent
persistence rather than just logging a warning. Currently the code logs a
warning but continues to persist the empty credentials at line 3755, which
overwrites valid passwords in the database. Instead, when
authConfig.AdminUserName or authConfig.AdminPassword meets the condition
(IsFromSecret() is true and GetValue() is empty), return early from the function
or set a flag to skip the persistence step entirely, matching the error-handling
behavior in the HTTP handler path.

Comment thread core/schemas/secretvar_test.go Outdated
Comment on lines +322 to +323
a: NewSecretVarFromRef("env.TEST", "test"),
b: NewSecretVarFromRef("env.TEST", "test"),

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 NewSecretVarFromRef is undefined — test package will not compile

The function NewSecretVarFromRef is called throughout secretvar_test.go and vault_test.go (24+ call sites) but is not defined anywhere in the codebase. The PR description lists it as a new constructor to be added, and the test migration from direct struct literals to NewSecretVarFromRef is the entire point of the change, but the function definition is missing from secretvar.go (or any other file in core/schemas). Running go test ./core/schemas/... will fail with undefined: NewSecretVarFromRef.

@BearTS BearTS changed the title refactor: encapsulate SecretVar fields and expose via Ref()/IsFromSecret() accessors with explicit MarshalJSON refactor: encapsulate SecretVar internals behind typed SecretType enum and accessor methods Jun 22, 2026
@BearTS BearTS closed this Jun 22, 2026
Comment on lines 3694 to 3700
// Fail-closed: if env/vault reference is unresolved, don't persist empty credentials.
if authConfig.AdminUserName != nil && authConfig.AdminUserName.GetValue() == "" && authConfig.AdminUserName.IsFromSecret() {
logger.Warn("username set with external reference but value is empty: %s — skipping auth config", authConfig.AdminUserName.SecretRef)
if dbAuthConfig != nil {
config.GovernanceConfig.AuthConfig = dbAuthConfig
}
return
logger.Warn("username set with external reference but value is empty: %s", authConfig.AdminUserName.GetSecretRef())
}
if authConfig.AdminPassword != nil && authConfig.AdminPassword.GetValue() == "" && authConfig.AdminPassword.IsFromSecret() {
logger.Warn("password set with external reference but value is empty: %s — skipping auth config", authConfig.AdminPassword.SecretRef)
if dbAuthConfig != nil {
config.GovernanceConfig.AuthConfig = dbAuthConfig
}
return
logger.Warn("password set with external reference but value is empty: %s", authConfig.AdminPassword.GetSecretRef())
}

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 Fail-closed guard replaced with no-op warning, breaking auth config safety

The refactor removed the return (and config.GovernanceConfig.AuthConfig = dbAuthConfig) that followed each warning when an admin credential backed by an unresolved env/vault reference was detected. The comment still says "Fail-closed" but the code no longer is.

When AdminUserName or AdminPassword resolves to an empty string (e.g., the env var is missing at startup), the new code logs a warning and falls through to the matching logic. There, filePassword is "", the bcrypt comparison against the stored hash fails (passwordMatch = false), FlushSessions is called (dropping all active sessions), and then an empty-string password is hashed and written to the database — overwriting the valid stored credential. This is the opposite of the intended fail-closed behavior.

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.

1 participant