Skip to content

fix(broker): resolve dynamic secrets through the MITM proxy - #269

Merged
dangtony98 merged 1 commit into
mainfrom
fix/dynamic-credential-late-bind
Jun 14, 2026
Merged

fix(broker): resolve dynamic secrets through the MITM proxy#269
dangtony98 merged 1 commit into
mainfrom
fix/dynamic-credential-late-bind

Conversation

@dangtony98

Copy link
Copy Markdown
Contributor

Problem

Proxied requests for an Infisical dynamic credential failed with credential_not_found (502) even though the credential was visible in the credentials table. Static credentials worked; dynamic ones did not.

Root cause is an initialization-ordering bug:

  • attachMITMIfEnabled calls srv.CredentialProvider() and bakes the returned provider into the MITM proxy once, at attach time (cmd/server.go).
  • The old CredentialProvider() only set Dynamic if s.infisicalDynamic != nil at that instant.
  • But s.infisicalDynamic is built later, inside srv.Start().

So the proxy snapshotted a nil dynamic resolver and skipped dynamic resolution for the whole process lifetime. The UI's enumerate/reveal path reads s.infisicalDynamic live, which is why the credential still showed up in the table. That asymmetry was the symptom.

Fix

Bind the resolver through a small lateDynamicResolver adapter that reads s.infisicalDynamic per request instead of snapshotting it. Static and dynamic credentials now resolve identically regardless of attach/Start ordering.

The adapter satisfies the existing Dynamic DynamicCredentialResolver interface field, so:

  • brokercore gains no new public surface (it is unchanged).
  • The init-ordering concern lives entirely in the server package, where the lifecycle problem is.
  • The previous typed-nil guard is no longer needed (the adapter is always a valid non-nil value and handles a nil infisicalDynamic internally).

Tests

  • TestCredentialProvider_LateBindsDynamicResolver: captures the provider while infisicalDynamic is nil (the attach-time condition), asserts the adapter is wired to the live *Server and is nil-safe pre-bind, then assigns the resolver as Start() does and confirms the already-captured provider reaches it.
  • Existing TestInject_DynamicFallback_* brokercore tests continue to cover the Dynamic path unchanged.

go build ./..., go vet, and go test ./... all pass.

Scope

Internal bugfix to credential brokering. No agent-facing API, CLI flag, or env var changes, so no doc/skill updates are needed.

The MITM proxy captures its credential provider in attachMITMIfEnabled,
which runs before Start() builds s.infisicalDynamic. The provider therefore
snapshotted a nil dynamic resolver and never attempted dynamic-secret
resolution, so proxied requests for an Infisical dynamic credential failed
with credential_not_found even though the credential showed up in the
credentials table (the UI enumerates it live).

Bind the resolver through a small lateDynamicResolver adapter that reads
s.infisicalDynamic per request instead of snapshotting it, so static and
dynamic credentials resolve identically regardless of attach/Start ordering.
The adapter satisfies the existing Dynamic interface field, so brokercore
needs no new surface and the typed-nil guard goes away.
@infisical-review-police

Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-agent-vault-269-fix-broker-resolve-dynamic-secrets-through-the-mitm-pr

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes an init-ordering bug where attachMITMIfEnabled captured a nil dynamic resolver at attach time (before Start() built infisicalDynamic), causing dynamic credentials to always fail with credential_not_found through the MITM path. A lateDynamicResolver adapter reads s.infisicalDynamic per-call rather than snapshotting it, so the correct resolver is visible once Start() wires it up.

  • Removes the old typed-nil guard in CredentialProvider() and replaces it with the lateDynamicResolver struct that defers to s.infisicalDynamic on every call.
  • Adds TestCredentialProvider_LateBindsDynamicResolver covering the nil-safe pre-bind state and confirming the already-captured provider reaches the resolver after Start()-style assignment.

Confidence Score: 5/5

Safe to merge; targeted one-file fix with a matching test and no side-effects on public API or CLI surface.

The lateDynamicResolver adapter is write-once safe: infisicalDynamic only transitions nil to non-nil in Start(), and the MITM goroutine is spawned after that write, so Go memory model guarantees visibility to all request goroutines. The nil-check before Resolve cannot race because the field is never reset to nil. The new test correctly reproduces the pre-bind nil path and verifies the live read after assignment.

No files require special attention.

Important Files Changed

Filename Overview
internal/server/server.go Replaces snapshot-at-attach-time infisicalDynamic assignment with a lateDynamicResolver adapter that reads the field per-call; the nil guard is safe because the field is write-once (nil to non-nil in Start()) and the MITM goroutine is only spawned after Start() sets the field.
internal/server/server_test.go Adds TestCredentialProvider_LateBindsDynamicResolver that exercises the pre-bind nil-safe path and the post-bind live-read path; accesses the unexported lateDynamicResolver type directly, which is fine since the test is in the same package.

Reviews (1): Last reviewed commit: "fix(broker): resolve dynamic secrets thr..." | Re-trigger Greptile

@dangtony98
dangtony98 merged commit 30ff25c into main Jun 14, 2026
11 checks passed
@dangtony98
dangtony98 deleted the fix/dynamic-credential-late-bind branch June 14, 2026 23:04
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