Skip to content

feat(credentials): add in-place credential rotation via upsert#2919

Merged
migmartri merged 4 commits into
chainloop-dev:mainfrom
migmartri:worktree-goofy-gathering-conway
Mar 22, 2026
Merged

feat(credentials): add in-place credential rotation via upsert#2919
migmartri merged 4 commits into
chainloop-dev:mainfrom
migmartri:worktree-goofy-gathering-conway

Conversation

@migmartri

@migmartri migmartri commented Mar 22, 2026

Copy link
Copy Markdown
Member

Summary

  • Introduces WithSecretName functional option to SaveCredentials across all credential backends (Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault), enabling upsert at a known path instead of always generating a new UUID-based path
  • Updates CASBackendUseCase.Update() to pass the existing SecretName when rotating credentials, so rotations overwrite in-place and stop creating orphaned secret entries
  • Adds PutSecretValue to the AWS SecretsManagerIface with fallback to CreateSecret on not-found, and GetSecret-based existence check for GCP before adding a new secret version
  • Updates Writer/ReaderWriter mocks to handle the variadic opts ...SaveOption parameter; adds table-driven upsert tests for all four backends and a casbackend rotation test verifying WithSecretName is forwarded

closes #2918

Introduces WithSecretName functional option to SaveCredentials, allowing
callers to provide an existing secret path for in-place upsert instead of
always generating a new UUID-based path. Updates CAS backend Update() to
pass the existing secret name on rotation, eliminating orphaned credentials
in all supported secret managers (Vault, AWS, GCP, Azure Key Vault).

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 14 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="pkg/credentials/credentials.go">

<violation number="1" location="pkg/credentials/credentials.go:57">
P2: Guard against nil `SaveOption` entries before invoking them to avoid a runtime panic.</violation>
</file>

<file name="app/controlplane/pkg/biz/casbackend_test.go">

<violation number="1" location="app/controlplane/pkg/biz/casbackend_test.go:348">
P2: The empty-secret test case can miss regressions because it asserts an empty captured secret value instead of asserting that no `SaveOption` argument was passed.

(Based on your team's feedback about updating tests for new paths.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/credentials/credentials.go Outdated
Comment thread app/controlplane/pkg/biz/casbackend_test.go Outdated
- Change ApplySaveOptions signature from slice to variadic for idiomatic Go
- Replace TOCTOU GetSecret→CreateSecret upsert in GCP with a single
  CreateSecret that tolerates AlreadyExists, eliminating the race condition
- Add isAlreadyExistsErr helper alongside the removed isNotFoundErr
- Update all call sites and tests accordingly

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 7 files (changes from recent commits).

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="pkg/credentials/gcp/secretmanager.go">

<violation number="1" location="pkg/credentials/gcp/secretmanager.go:118">
P1: Upsert now always calls `CreateSecret`, which adds an unnecessary IAM requirement (`secretmanager.secrets.create`) for rotating existing secrets and can cause `PermissionDenied` in previously working setups.

(Based on your team's feedback about cross-component and version compatibility.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/credentials/gcp/secretmanager.go Outdated
…ation test

The SaveCredentials integration test expectation was registered with
only 3 args. After the upsert change, Update() passes WithSecretName
as a 4th variadic arg, causing the mock not to match. Add mock.Anything
to cover the opts argument.

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Comment thread pkg/credentials/credentials.go
Comment thread pkg/credentials/credentials.go Outdated
Comment thread pkg/credentials/aws/secretmanager.go
Comment thread pkg/credentials/credentials.go Outdated
- Rename WithSecretName to WithExistingSecret for clarity
- Add nil guard in ApplySaveOptions to prevent runtime panic
- GCP upsert: try AddSecretVersion first to avoid requiring
  secretmanager.secrets.create permission for existing secrets
- Improve test assertion for empty-secret case to check opt presence
- Add mockery v3 config for credentials package

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 11 files (changes from recent commits).

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="pkg/credentials/azurekv/keyvault.go">

<violation number="1" location="pkg/credentials/azurekv/keyvault.go:123">
P3: The comment references `WithExistingSecret`, but the option introduced/used by this API is `WithSecretName`. Rename it to keep API naming and docs consistent.

(Based on your team's feedback about naming consistent, domain-specific API terms.) [FEEDBACK_USED]</violation>
</file>

<file name="pkg/credentials/gcp/secretmanager.go">

<violation number="1" location="pkg/credentials/gcp/secretmanager.go:130">
P2: The create-after-NotFound upsert path does not tolerate `AlreadyExists`, so concurrent rotations can fail spuriously instead of proceeding to add a new version.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/credentials/gcp/secretmanager.go
Comment thread pkg/credentials/azurekv/keyvault.go

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

LGTM

@migmartri migmartri merged commit 9dc4ff6 into chainloop-dev:main Mar 22, 2026
15 checks passed
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.

feat: clean up orphaned credentials on CAS backend rotation and allow custom secret name

2 participants