Skip to content

feat(provider): add devolutions server provider support#5712

Merged
Skarlso merged 3 commits intoexternal-secrets:mainfrom
rbstp:feat/dvls
Jan 10, 2026
Merged

feat(provider): add devolutions server provider support#5712
Skarlso merged 3 commits intoexternal-secrets:mainfrom
rbstp:feat/dvls

Conversation

@rbstp
Copy link
Copy Markdown
Contributor

@rbstp rbstp commented Dec 10, 2025

Problem Statement

We want to add a provider for Devolutions Server that uses our go-dvls library.

Related Issue

Fixes #5682

Proposed Changes

Added the provider, based on the following PRs:

Checklist

  • I have read the contribution guidelines
  • All commits are signed with git commit --signoff
  • My changes have reasonable test coverage
  • All tests pass with make test
  • I ensured my PR is ready for review with make reviewable

Question

I did not modify the CODEOWNERS.md file but I am uncertain if it is needed. It was modified in the ngrok PR, and not in the Barbican one.

Summary

Adds Devolutions Server (DVLS) as a new provider to External Secrets Operator.

Key Changes

  • API & CRDs

    • New v1 API types: DVLSProvider, DVLSAuth, DVLSAuthSecretRef; SecretStoreProvider gains optional dvls field and corresponding deepcopy implementations.
    • CRD/schema updates for SecretStore and ClusterSecretStore (v1 and v1beta1) and bundle manifests to expose dvls configuration and validation. (One duplicate dvls block was observed in config/crds/bases/external-secrets.io_secretstores.yaml.)
  • Provider implementation

    • providers/v1/dvls/provider.go: provider implementation, store validation (server URL, scheme/host checks, secret selectors), capabilities (read/write), and maintenance status.
    • providers/v1/dvls/auth.go: NewDVLSClient reads AppID/AppSecret via SecretKeySelector and constructs a go-dvls client.
    • providers/v1/dvls/client.go: SecretsClient implementation (GetSecret/GetSecretMap, PushSecret, DeleteSecret, SecretExists, Validate, Close) with timeout helpers and not-found handling.
    • pkg/register/dvls.go: provider registration (build tag dvls || all_providers).
  • Tests

    • Unit tests for auth, client, and provider validation (including cross-namespace secret access checks) and updated CRD snapshots.
  • Modules & dependencies

    • New module providers/v1/dvls with its own go.mod and local replace directives.
    • Adds github.com/Devolutions/go-dvls v0.15.0 and updates several indirect golang.org/x/* deps (notably golang.org/x/crypto -> v0.46.0).
    • Root go.mod: replace directive for local providers/v1/dvls.
  • Documentation & examples

    • docs/provider/devolutions-server.md, mkdocs nav entry, provider stability/feature table update.
    • Example snippets: dvls-secret-store.yaml, dvls-external-secret.yaml, dvls-push-secret.yaml.
  • Misc

Estimated review effort: High.

@github-actions github-actions bot added area/provider kind/feature Categorizes issue or PR as related to a new feature. kind/documentation Categorizes issue or PR as related to documentation. kind/dependency dependabot and upgrades size/l labels Dec 10, 2025
@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Dec 11, 2025

Please update the golang.org/x/crypto library to 0.46.0 :) 🙇

@rbstp rbstp force-pushed the feat/dvls branch 2 times, most recently from 1e4c6b4 to 4accffa Compare December 12, 2025 15:59
@rbstp
Copy link
Copy Markdown
Contributor Author

rbstp commented Dec 12, 2025

I had forgotten to run make reviewable after to update the base go.mod. Should be good now.

@rbstp
Copy link
Copy Markdown
Contributor Author

rbstp commented Dec 26, 2025

@Skarlso is anything required on our end?

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Dec 26, 2025

No. We will, eventually, get to this. :) Please be patient. :)

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 26, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 26, 2025

Walkthrough

Adds Devolutions Server (DVLS) support across the project: new API types and deepcopy logic, CRD/schema and bundle updates, DVLS provider implementation and registration, auth/client logic (get/push/delete/exists), module changes, tests, docs, and example snippets.

Changes

Cohort / File(s) Summary
API types & deepcopy
apis/externalsecrets/v1/secretstore_dvls_types.go, apis/externalsecrets/v1/secretstore_types.go, apis/externalsecrets/v1/zz_generated.deepcopy.go
Add DVLSProvider, DVLSAuth, DVLSAuthSecretRef types; add DVLS *DVLSProvider to SecretStoreProvider; kubebuilder markers and generated DeepCopy implementations; update deepcopy to handle DVLS.
CRDs & bundle
config/crds/bases/...external-secrets.io_clustersecretstores.yaml, config/crds/bases/...external-secrets.io_secretstores.yaml, deploy/crds/bundle.yaml
Introduce dvls provider schema (required serverUrl, required auth.secretRef with appId/appSecret, optional insecure, CA/connection/version fields) across CRDs and bundled manifests for v1 and v1beta1.
Provider implementation & client
providers/v1/dvls/provider.go, providers/v1/dvls/auth.go, providers/v1/dvls/client.go, providers/v1/dvls/*_test.go, providers/v1/dvls/client_test.go, providers/v1/dvls/provider_test.go
Implement DVLS provider: store validation, secret selector validation, NewClient creation; implement SecretsClient (GetSecret, GetSecretMap, PushSecret, DeleteSecret, SecretExists, Validate, Close) with timeout helpers and error handling; add comprehensive unit tests.
Provider module & registration
providers/v1/dvls/go.mod, pkg/register/dvls.go, go.mod
Add dvls submodule and go.mod with local replaces; add root go.mod replace for local provider and indirect require for github.com/Devolutions/go-dvls; add build-tagged pkg/register/dvls.go registering provider in init.
Docs & examples
docs/provider/devolutions-server.md, docs/api/spec.md, docs/introduction/stability-support.md, docs/snippets/dvls-secret-store.yaml, docs/snippets/dvls-external-secret.yaml, docs/snippets/dvls-push-secret.yaml, hack/api-docs/mkdocs.yml
Add DVLS provider documentation and API spec entries, stability/support table row, mkdocs nav update, and example snippets (SecretStore, ExternalSecret, PushSecret).
CRD snapshots / tests
tests/__snapshot__/clustersecretstore-v1.yaml, tests/__snapshot__/secretstore-v1.yaml
Update test snapshots to include spec.dvls blocks with auth.secretRef.appId, auth.secretRef.appSecret, serverUrl, and insecure.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Pull request implements all core requirements from issue #5682: adds DVLS provider support, enables secret synchronization from Devolutions Server to Kubernetes, and provides community-requested integration.
Out of Scope Changes check ✅ Passed All changes align with stated objectives. Code additions cover DVLS provider API types, client implementation, CRD schemas, documentation, tests, and provider registration. No unrelated modifications detected.

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


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Dec 26, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 26, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 2

🧹 Nitpick comments (4)
config/crds/bases/external-secrets.io_clustersecretstores.yaml (1)

1958-2038: DVLS provider schema looks consistent and ready to ship

The dvls provider block is structurally in line with other providers:

  • auth.secretRef.appId / appSecret use the standard SecretKeySelector shape.
  • auth.secretRef and serverUrl are correctly marked as required.
  • Naming (serverUrl, descriptions using ServerURL) matches existing patterns like chef.serverUrl.

No changes are required here. If, in future, DVLS needs custom CA handling for on‑prem/self‑signed deployments, you can extend this schema with caBundle/caProvider fields similar to other TLS‑enabled providers, but that can be a follow‑up and isn’t a blocker for this PR.

apis/externalsecrets/v1/secretstore_dvls_types.go (1)

23-32: Consider adding URL format validation to ServerURL.

The ServerURL field is marked as required, which is good. Consider adding a kubebuilder format validation to ensure it's a valid URL:

// ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
// +kubebuilder:validation:Required
// +kubebuilder:validation:Format=uri
ServerURL string `json:"serverUrl"`

This would provide early validation feedback to users if they provide an invalid URL format.

deploy/crds/bundle.yaml (1)

3981-3983: Consider adding basic URL validation for serverUrl.

The serverUrl field accepts any string without format constraints. While CRD schema validation doesn't support native URL format validation, consider adding:

  • A maxLength constraint to prevent excessively long values (consistent with other string fields in this schema)
  • Optionally, a pattern to enforce a basic URL structure (e.g., ^https?://)

This is a minor hardening suggestion; the provider code likely validates this at runtime anyway.

🔎 Suggested improvement
                        serverUrl:
                          description: ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
+                         maxLength: 1024
+                         pattern: ^https?://
                          type: string
providers/v1/dvls/client.go (1)

158-166: DeleteSecret lacks timeout protection and ignores context.

Other DVLS operations (GetSecret, PushSecret, SecretExists) use timeout wrappers, but DeleteSecret calls DeleteByID directly without timeout protection or context propagation. This inconsistency could cause the operation to hang indefinitely if the DVLS server is unresponsive.

🔎 Proposed fix to add timeout protection
 // DeleteSecret deletes a secret from DVLS.
-func (c *Client) DeleteSecret(_ context.Context, ref esv1.PushSecretRemoteRef) error {
+func (c *Client) DeleteSecret(ctx context.Context, ref esv1.PushSecretRemoteRef) error {
 	vaultID, entryID, err := c.parseSecretRef(ref.GetRemoteKey())
 	if err != nil {
 		return err
 	}
 
-	return c.dvls.DeleteByID(vaultID, entryID)
+	return runWithTimeoutErr(ctx, c.timeout, "delete entry", func() error {
+		return c.dvls.DeleteByID(vaultID, entryID)
+	})
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25a972a and 7f2e73f.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧬 Code graph analysis (6)
apis/externalsecrets/v1/secretstore_types.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-32)
providers/v1/dvls/client_test.go (4)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
apis/generators/v1alpha1/types_password.go (1)
  • Password (77-82)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/dvls/auth.go (2)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-32)
pkg/register/dvls.go (1)
providers/v1/dvls/provider.go (3)
  • NewProvider (127-129)
  • ProviderSpec (132-136)
  • MaintenanceStatus (139-141)
apis/externalsecrets/v1/zz_generated.deepcopy.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSAuth (35-39)
  • DVLSAuthSecretRef (42-50)
  • DVLSProvider (24-32)
providers/v1/dvls/client.go (3)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/keepersecurity/client.go (1)
  • Secret (105-111)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
🪛 Gitleaks (8.30.0)
providers/v1/dvls/client_test.go

[high] 266-278: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🪛 YAMLlint (1.37.1)
docs/snippets/dvls-external-secret.yaml

[error] 1-1: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (55)
go.mod (3)

35-35: LGTM!

The replace directive for the DVLS provider is correctly placed and follows the established pattern for provider modules in this monorepo.


101-101: LGTM!

The golang.org/x/* dependency updates are consistent with the maintainer's request to update golang.org/x/crypto to v0.46.0. These indirect dependency bumps align with the module graph requirements.

Also applies to: 381-381, 479-486


198-198: No action required. The go-dvls library at v0.14.0 is the latest stable release and has no known security vulnerabilities.

providers/v1/dvls/go.mod (1)

91-94: LGTM!

The replace directives correctly map the APIs and runtime modules to their local paths, following the standard pattern for provider modules in this monorepo.

docs/introduction/stability-support.md (2)

98-98: LGTM!

The Devolutions Server entry in the Provider Stability table is correctly formatted, appropriately marked as alpha stability, and positioned alphabetically after Barbican.


140-140: LGTM!

The Devolutions Server entry in the Provider Feature Support table correctly indicates support for store validation and push secret, which is appropriate for an alpha-stage provider.

pkg/register/dvls.go (1)

1-30: LGTM!

The DVLS provider registration file correctly follows the established pattern with appropriate build tags and uses the standard registration mechanism via the init function. The code properly references the provider's NewProvider(), ProviderSpec(), and MaintenanceStatus() functions.

docs/snippets/dvls-secret-store.yaml (1)

1-17: LGTM!

The DVLS SecretStore example provides a clear and valid configuration demonstrating the serverUrl and authentication setup using secretRef. The use of HTTPS for serverUrl is a good security practice.

hack/api-docs/mkdocs.yml (1)

163-163: LGTM!

The Devolutions Server documentation entry is correctly added to the navigation with the proper formatting and documentation path.

apis/externalsecrets/v1/secretstore_types.go (1)

203-205: LGTM! DVLS provider field follows established patterns.

The addition of the DVLS provider field is consistent with other providers in the struct. The comment, JSON tag, and optional marker are all correctly applied.

docs/snippets/dvls-push-secret.yaml (1)

16-17: The remoteKey format 'vault-uuid/entry-uuid' is correct and matches the DVLS provider implementation. According to the official documentation (docs/provider/devolutions-server.md), secrets are referenced using the format <vault-id>/<entry-id>, where vault-id is the UUID of the vault and entry-id is the UUID of the credential entry. The PushSecret type definition confirms remoteKey is the correct field name for this reference.

tests/__snapshot__/secretstore-v1.yaml (1)

300-311: LGTM! Snapshot structure aligns with DVLS provider types.

The new DVLS configuration block follows the established pattern for provider snapshots and correctly mirrors the DVLSProvider, DVLSAuth, and DVLSAuthSecretRef types defined in the API.

docs/provider/devolutions-server.md (1)

1-117: Excellent documentation with clear guidance and limitations.

The documentation is comprehensive and well-structured, covering authentication, setup, examples, and troubleshooting. The limitations section at lines 93-98 is particularly valuable for users.

tests/__snapshot__/clustersecretstore-v1.yaml (1)

300-311: LGTM! ClusterSecretStore snapshot correctly mirrors SecretStore structure.

The DVLS configuration is consistent with the SecretStore snapshot and appropriately includes namespace fields for cluster-scoped secret references.

docs/snippets/dvls-external-secret.yaml (1)

1-61: Good example coverage; YAMLlint error is expected.

The three ExternalSecret examples effectively demonstrate different DVLS usage patterns. The YAMLlint syntax error on line 1 is expected since {% raw %} is template syntax for documentation generation, not standard YAML.

apis/externalsecrets/v1/zz_generated.deepcopy.go (2)

1263-1310: LGTM! Auto-generated DeepCopy methods follow standard patterns.

The generated DeepCopy implementations for DVLSAuth, DVLSAuthSecretRef, and DVLSProvider correctly handle nested structures and nil checks as expected.


3546-3550: LGTM! DVLS field properly integrated into SecretStoreProvider.

The DVLS field is correctly deep-copied within SecretStoreProvider.DeepCopyInto, following the same pattern as other provider types.

providers/v1/dvls/auth_test.go (4)

32-57: LGTM! Success case properly validates secret retrieval.

The test correctly sets up a fake client with a secret and validates that getSecretValue returns the expected app ID.


59-73: LGTM! Error handling validated for missing secrets.

The test correctly verifies that getSecretValue returns an appropriate error when the secret doesn't exist.


75-99: LGTM! Key not found scenario properly tested.

The test validates that an error is returned with a descriptive message when the requested key doesn't exist in the secret.


101-127: LGTM! Namespace override behavior verified.

The test correctly validates that when a selector specifies a namespace, the secret is retrieved from that namespace rather than the default, which is critical for ClusterSecretStore functionality.

providers/v1/dvls/provider_test.go (10)

40-71: LGTM! Valid configuration test case.

The test correctly validates a properly configured SecretStore with all required DVLS fields.


73-103: LGTM! ClusterSecretStore namespace validation works correctly.

The test properly validates that ClusterSecretStore requires namespace in secret references, which is a critical security boundary.


105-137: LGTM! ClusterSecretStore succeeds with explicit namespace.

The test confirms that when namespace is provided in secret references, ClusterSecretStore validation passes.


139-150: LGTM! Nil provider check validated.

The test ensures appropriate error handling when DVLS provider configuration is missing.


152-181: LGTM! Required serverUrl field validated.

The test confirms that an empty serverUrl is properly caught during validation.


183-212: LGTM! AppID key validation works correctly.

The test ensures that missing appId key is caught with a descriptive error message.


214-243: LGTM! AppSecret name validation works correctly.

The test validates that missing appSecret name is properly detected.


245-276: LGTM! Cross-namespace reference validation for AppID.

The test correctly validates that SecretStore (namespace-scoped) cannot reference secrets in other namespaces without being a ClusterSecretStore.


278-309: LGTM! Cross-namespace reference validation for AppSecret.

The test validates the same cross-namespace constraint for AppSecret, ensuring consistency with AppID validation.


312-333: LGTM! Provider metadata tests validated.

The tests confirm that Capabilities returns ReadWrite, NewProvider returns the correct type, ProviderSpec is populated, and MaintenanceStatus indicates maintained status.

apis/externalsecrets/v1/secretstore_dvls_types.go (1)

34-50: LGTM! Authentication types properly structured.

The DVLSAuth and DVLSAuthSecretRef types are well-designed with appropriate validation annotations. Using SecretKeySelector from esmeta provides consistent secret reference handling across providers.

config/crds/bases/external-secrets.io_secretstores.yaml (1)

1958-2038: DVLS provider schema looks consistent and correct

dvls is wired into spec.provider in line with other providers, with auth.secretRef.appId/appSecret and serverUrl correctly modeled and marked as required. The nested SecretKeySelector shapes (key/name/namespace) match the rest of this CRD, so the schema should serialize/deserialize cleanly against the new DVLS Go types.

providers/v1/dvls/auth.go (2)

33-51: LGTM! Clean authentication flow with proper error handling.

The function correctly retrieves credentials from Kubernetes secrets and constructs the DVLS client. Error messages are context-rich and helpful.


53-74: LGTM! Proper secret retrieval with namespace override support.

The function correctly handles the optional namespace override (lines 60-62) and provides clear error messages for both secret retrieval and missing key scenarios.

Note: The unused storeKind parameter (line 53) is marked with _, which is appropriate for maintaining interface consistency with other providers in the ESO codebase.

providers/v1/dvls/client_test.go (5)

34-98: LGTM! Well-designed test infrastructure.

The mock credential client provides flexible error injection capabilities, and the test stubs properly implement the required interfaces for comprehensive testing.


100-150: LGTM! Comprehensive parsing and validation tests.

The test suite thoroughly covers valid inputs, invalid formats, and edge cases like spaces and empty components. The validation and initialization tests are appropriate.


152-319: LGTM! Excellent coverage of credential type mappings.

The tests comprehensively cover all supported credential subtypes (Default, AccessCode, ApiKey, AzureServicePrincipal, ConnectionString, PrivateKey), including edge cases like unsupported types and partial data. The assertions correctly verify field mappings and the omission of empty fields.


257-281: Static analysis false positive: test data, not a security issue.

The Gitleaks tool flagged lines 266-278 as containing a private key. This is test data (a truncated private key string literal used in the PrivateKey credential type test) and does not represent a security concern. Test fixtures commonly include sample cryptographic material.


321-447: LGTM! Thorough CRUD operation tests.

The test suite provides excellent coverage of:

  • GetSecret and GetSecretMap with not-found and success scenarios
  • SecretExists with present/absent cases and error propagation
  • DeleteSecret verification
  • PushSecret for multiple subtypes, not-found errors, and unsupported subtype handling

All test logic is correct and assertions are appropriate.

deploy/crds/bundle.yaml (2)

3913-3987: Schema structure looks correct and follows project conventions.

The DVLS provider schema definition:

  • Properly defines required fields (auth, serverUrl, secretRef, appId, appSecret)
  • Uses consistent validation patterns for Kubernetes resource references (name, namespace, key constraints)
  • Aligns with the standard SecretKeySelector pattern used by other providers in the project

15612-15686: Consistent schema definition across CRD types.

This schema block is identical to the one added earlier in the bundle, which is correct—both SecretStore and ClusterSecretStore should share the same DVLS provider configuration structure.

providers/v1/dvls/provider.go (4)

1-36: LGTM on the license, imports, and Provider struct.

The structure follows ESO provider conventions with proper interface compliance check via var _ esv1.Provider = &Provider{}.


37-52: LGTM on NewClient implementation.

The method correctly extracts the provider configuration, gets the store kind for namespace resolution, creates the DVLS client, and wraps it in the SecretsClient interface.


54-80: LGTM on the rest of ValidateStore.

The validation correctly checks for empty ServerURL, validates the URL scheme, and validates auth secret references.


82-141: LGTM on Capabilities and helper/factory functions.

  • Capabilities correctly returns SecretStoreReadWrite.
  • validateAuthSecretRef and requireSecretSelector provide clear validation with descriptive error messages.
  • getDVLSProvider safely extracts the provider configuration.
  • Factory functions follow the ESO registration pattern.
providers/v1/dvls/client.go (9)

1-51: LGTM on the package setup, constants, and interface definitions.

The credentialClient interface provides a clean abstraction over the DVLS library, enabling easier testing and mocking.


53-75: LGTM on realCredentialClient adapter and NewClient constructor.

The adapter correctly delegates to the DVLS library methods, and the constructor initializes with a sensible 15-second default timeout.


77-133: LGTM on GetSecret, GetSecretMap, and GetAllSecrets.

  • Proper key parsing and timeout handling.
  • Correct use of esv1.NoSecretErr for not-found cases.
  • Property extraction logic handles both specific property requests and full JSON marshaling.
  • GetAllSecrets explicitly returns not implemented, which is appropriate for DVLS limitations.

135-156: PushSecret only updates existing entries.

The implementation returns an error if the entry is not found (lines 148-150), meaning it cannot create new entries. This may be intentional given DVLS API constraints, but it's worth confirming this aligns with the expected behavior documented in the provider docs.


168-197: LGTM on SecretExists, Validate, and Close.

  • SecretExists correctly interprets not-found errors as false without error.
  • Validate checks for nil client initialization.
  • Close is appropriately a no-op since there's no persistent connection to clean up.

199-233: LGTM on parseSecretRef and entryToSecretMap helpers.

  • parseSecretRef properly validates the key format and trims whitespace.
  • entryToSecretMap correctly converts string values to byte slices.

235-250: LGTM on extractPushValue.

Clear validation with descriptive error messages for missing key and empty values.


270-299: Goroutine may leak on timeout, but this is acceptable.

The runWithTimeout pattern spawns a goroutine that continues executing even after the context times out. The result is discarded but the goroutine eventually completes. This is a common trade-off when the underlying library doesn't support context cancellation natively. Just be aware that under sustained timeouts, goroutines may accumulate temporarily.


301-316: Error detection fallback is fragile but pragmatic.

The string-based checks for "404" and "not found" (lines 311-315) are brittle, but they serve as a reasonable fallback when the DVLS library doesn't expose a typed not-found error. The primary check via dvls.IsNotFound(err) should handle most cases.

@rbstp
Copy link
Copy Markdown
Contributor Author

rbstp commented Dec 26, 2025

My apologies, I just wanted to validate if there was a blocker on our end :)

I have fixed the 2 issues from CodeRabbit.

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: 2

🧹 Nitpick comments (2)
config/crds/bases/external-secrets.io_clustersecretstores.yaml (1)

1958-2038: DVLS provider schema looks consistent and correct

The dvls block follows the existing provider patterns: auth is properly nested, appId/appSecret use the standard SecretKeySelector-style schema (key/name/namespace + validations), and required chains (secretRefauthdvls) are wired correctly. I don’t see any schema or validation issues here.

If you later find users need custom CA handling for DVLS (similar to caBundle/caProvider on other providers), that could be added as optional fields without breaking this schema. For this PR, this looks good to ship.

providers/v1/dvls/client.go (1)

301-315: String-based fallback may match unrelated errors.

The primary check using dvls.IsNotFound(err) is correct. However, the fallback string matching on line 315 ("404" or "not found") could potentially match unrelated errors. Consider limiting the fallback to just the RequestError type check, or verify this broader matching is needed based on actual DVLS error behavior.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f2e73f and da4707a.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
🚧 Files skipped from review as they are similar to previous changes (8)
  • hack/api-docs/mkdocs.yml
  • providers/v1/dvls/provider_test.go
  • docs/introduction/stability-support.md
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • tests/snapshot/clustersecretstore-v1.yaml
  • providers/v1/dvls/auth_test.go
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • docs/provider/devolutions-server.md
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧬 Code graph analysis (5)
apis/externalsecrets/v1/secretstore_types.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-32)
pkg/register/dvls.go (1)
providers/v1/dvls/provider.go (3)
  • NewProvider (130-132)
  • ProviderSpec (135-139)
  • MaintenanceStatus (142-144)
providers/v1/dvls/auth.go (2)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-32)
providers/v1/dvls/client_test.go (4)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
apis/generators/v1alpha1/types_password.go (1)
  • Password (77-82)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/dvls/client.go (1)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
🪛 Gitleaks (8.30.0)
providers/v1/dvls/client_test.go

[high] 266-278: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🪛 YAMLlint (1.37.1)
docs/snippets/dvls-external-secret.yaml

[error] 1-1: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (31)
apis/externalsecrets/v1/secretstore_types.go (1)

203-206: LGTM! Clean provider integration.

The DVLS provider field follows the established pattern used by all other providers in this file. The comment, optional marker, and JSON tag are consistent with conventions.

docs/snippets/dvls-secret-store.yaml (1)

1-17: LGTM! Clear and correct example.

The SecretStore configuration correctly demonstrates DVLS provider setup with proper field names matching the Go struct JSON tags and appropriate secret references.

pkg/register/dvls.go (2)

1-1: LGTM! Correct build tag for conditional compilation.

The build tag allows the DVLS provider to be conditionally compiled, enabling flexible binary configurations.


27-30: LGTM! Provider registration follows established pattern.

The registration correctly wires the DVLS provider into the External Secrets operator using the standard three-function registration pattern (NewProvider, ProviderSpec, MaintenanceStatus).

docs/snippets/dvls-push-secret.yaml (1)

1-17: LGTM! Clear PushSecret example.

The PushSecret configuration correctly demonstrates writing secrets to DVLS with appropriate API version (v1alpha1), secret mapping, and the hierarchical remoteKey format using vault and entry UUIDs.

tests/__snapshot__/secretstore-v1.yaml (1)

300-311: LGTM! DVLS schema correctly integrated.

The DVLS provider schema is properly structured and consistent with the type definitions introduced in the PR.

docs/snippets/dvls-external-secret.yaml (1)

1-61: LGTM! Well-structured documentation examples.

The three examples effectively demonstrate different DVLS usage patterns (single property fetch, dataFrom extract, and secret entry access). The YAMLlint syntax error is a false positive—the {% raw %} and {% endraw %} tags are intentional Liquid/Jekyll template markers for documentation generation.

providers/v1/dvls/auth.go (1)

34-51: LGTM! Clean authentication flow.

The function properly retrieves credentials from Kubernetes secrets, initializes the DVLS client, and wraps the credential client interface. Error handling is appropriate with context-enriched messages.

apis/externalsecrets/v1/secretstore_dvls_types.go (1)

23-50: LGTM! Well-structured type definitions.

The DVLS provider types are cleanly defined with proper kubebuilder validation annotations and JSON tags. The use of the existing esmeta.SecretKeySelector type for credentials follows established patterns in the codebase.

providers/v1/dvls/go.mod (1)

1-94: LGTM! Module configuration is correct.

The Go version (1.25.5) now matches the root module, addressing the previous review concern. Dependencies and replace directives are properly configured for the DVLS provider module.

go.mod (1)

35-35: LGTM! DVLS provider properly integrated.

The DVLS provider is correctly wired into the root module with appropriate replace directives and dependency declarations. The go-dvls library (v0.14.0) is properly included as an indirect dependency.

Also applies to: 149-149, 198-198

providers/v1/dvls/client_test.go (6)

1-32: LGTM!

License header and imports are properly structured. The test file correctly imports the necessary testing utilities and DVLS dependencies.


34-79: Well-designed mock for testing DVLS client behavior.

The mock correctly simulates DVLS API responses including 404 errors via dvls.RequestError, and tracks operations for assertion purposes.


100-134: Good coverage of secret reference parsing.

Tests cover the key edge cases: valid format, missing separator, empty vault/entry IDs, and whitespace handling.


257-281: Test data for private key mapping is appropriate.

The static analysis tool flagged this as a potential secret leak, but this is a false positive — the key is clearly truncated test data (MIIE...) used solely to verify that the entryToSecretMap function correctly maps private key fields. Using representative test data here is necessary for comprehensive test coverage.


321-350: Good test coverage for GetSecret operations.

Tests properly validate the not-found error mapping to esv1.NoSecretErr and successful secret retrieval with property extraction.


380-447: Comprehensive PushSecret test coverage.

Tests cover the key scenarios: updating Default and AccessCode subtypes, handling not-found entries, and rejecting unsupported subtypes. The assertions verify both the error conditions and the actual data mutations.

providers/v1/dvls/provider.go (5)

32-35: LGTM!

Good use of compile-time interface compliance check with var _ esv1.Provider = &Provider{}.


37-52: Clean client creation with proper error handling.

The method correctly extracts the provider configuration, creates the underlying DVLS client, and wraps it in the ESO client abstraction.


54-83: Thorough URL and auth validation.

The URL validation correctly handles all cases:

  1. Empty URL check
  2. Parse error with proper wrapping
  3. Missing scheme/host without nil-error wrapping (addressing previous review feedback)
  4. Scheme type validation (http/https only)

Auth validation ensures both appId and appSecret are properly configured.


90-118: Well-structured validation helpers.

The validateAuthSecretRef function properly validates both credentials with clear error messages, and requireSecretSelector provides reusable validation for secret references.


129-144: Factory functions follow ESO provider registration patterns.

The NewProvider, ProviderSpec, and MaintenanceStatus functions provide the expected interface for provider registration in the External Secrets ecosystem.

deploy/crds/bundle.yaml (2)

3913-3987: LGTM! DVLS provider schema is well-structured.

The schema follows Kubernetes CRD conventions and is consistent with other provider configurations in this bundle. Required fields are properly marked, validation patterns are appropriate, and descriptions are clear.


15654-15728: LGTM! Schema is consistent across CRD definitions.

This DVLS provider schema is identical to the one at lines 3913-3987, which is expected and correct for maintaining consistency across different CRD types (e.g., SecretStore and ClusterSecretStore).

providers/v1/dvls/client.go (7)

39-75: Well-structured client design with interface abstraction.

Good use of the credentialClient interface for testability and the compile-time interface assertion. The separation of concerns is clean.


77-110: LGTM!

Proper handling of not-found errors with esv1.NoSecretErr, clean property extraction, and sensible JSON fallback when no property is specified.


112-133: LGTM!

GetSecretMap is consistent with GetSecret. The "not implemented" return for GetAllSecrets is acceptable given DVLS API constraints.


168-197: LGTM!

SecretExists correctly returns (false, nil) for not-found cases. Validate and Close implementations are appropriate for this client.


199-233: LGTM!

parseSecretRef correctly uses SplitN with limit 2 to allow entry IDs containing slashes. The whitespace trimming and empty-value validation are good defensive measures.


235-250: LGTM!

Thorough validation with clear, contextual error messages.


270-292: Timeout implementation is acceptable given library constraints.

The goroutine continues running after context timeout because the go-dvls library's GetById and Update methods do not accept context parameters for cancellation. The buffered channel (size 1) properly prevents goroutine leaks by allowing the result to be written even when the caller has already returned. This is the correct pattern for wrapping non-context-aware operations.

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

♻️ Duplicate comments (1)
providers/v1/dvls/client.go (1)

158-168: Timeout handling correctly implemented.

The previous concern about missing timeout protection has been addressed. DeleteSecret now properly wraps the DVLS API call with timeout handling consistent with other methods.

🧹 Nitpick comments (2)
providers/v1/dvls/auth_test.go (1)

33-122: Test coverage for cross-namespace security is good, with a minor assertion improvement opportunity.

The test properly validates the security boundary between SecretStore and ClusterSecretStore regarding cross-namespace secret access.

🔎 Optional: Simplify success case assertions

Lines 116-119 check if an error (when present) doesn't contain a specific message, even in success scenarios. Consider using a direct assertion instead:

 } else {
-    if err != nil {
-        assert.NotContains(t, err.Error(), "cannot get Kubernetes secret")
-    }
+    assert.NoError(t, err)
 }

This makes the test's intent clearer: when expectError is false, there should be no error at all.

deploy/crds/bundle.yaml (1)

3981-3983: Consider adding URL format validation to serverUrl.

The serverUrl field accepts any string without format validation. Adding format: uri would provide client-side validation and improve the user experience by catching configuration errors earlier.

🔎 Suggested enhancement
                        serverUrl:
                          description: ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
                          type: string
+                         format: uri
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da4707a and c44fe65.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
🚧 Files skipped from review as they are similar to previous changes (11)
  • docs/introduction/stability-support.md
  • apis/externalsecrets/v1/secretstore_types.go
  • providers/v1/dvls/go.mod
  • tests/snapshot/clustersecretstore-v1.yaml
  • providers/v1/dvls/provider_test.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • docs/snippets/dvls-secret-store.yaml
  • tests/snapshot/secretstore-v1.yaml
  • docs/snippets/dvls-push-secret.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • providers/v1/dvls/auth.go
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧬 Code graph analysis (6)
pkg/register/dvls.go (1)
providers/v1/dvls/provider.go (3)
  • NewProvider (130-132)
  • ProviderSpec (135-139)
  • MaintenanceStatus (142-144)
providers/v1/dvls/auth_test.go (3)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSProvider (24-33)
  • DVLSAuth (36-40)
  • DVLSAuthSecretRef (43-51)
providers/v1/vault/util/vault.go (1)
  • Auth (31-33)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-49)
providers/v1/dvls/provider.go (3)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-49)
apis/externalsecrets/v1/secretstore_dvls_types.go (2)
  • DVLSAuthSecretRef (43-51)
  • DVLSProvider (24-33)
apis/externalsecrets/v1/provider_schema_maintenance.go (1)
  • MaintenanceStatusMaintained (29-29)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
providers/v1/vault/util/vault.go (1)
  • Auth (31-33)
providers/v1/dvls/client.go (2)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/keepersecurity/client.go (1)
  • Secret (105-111)
providers/v1/dvls/client_test.go (1)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
🪛 Gitleaks (8.30.0)
providers/v1/dvls/client_test.go

[high] 266-278: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🪛 YAMLlint (1.37.1)
docs/snippets/dvls-external-secret.yaml

[error] 1-1: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (39)
hack/api-docs/mkdocs.yml (1)

163-163: LGTM! Documentation navigation entry added correctly.

The Devolutions Server provider documentation link is properly positioned in alphabetical order within the Provider section.

providers/v1/dvls/client.go (13)

33-37: LGTM! Constants are well-defined.

The default timeout of 15 seconds is appropriate for DVLS API operations, and error message constants improve consistency.


39-75: LGTM! Clean abstraction design.

The credentialClient interface and realCredentialClient adapter provide good separation for testing, and the constructor properly initializes the default timeout.


77-110: LGTM! GetSecret implementation is robust.

The method correctly handles secret retrieval with timeout protection, proper not-found error handling, and supports both property-specific and full secret extraction.


112-128: LGTM! GetSecretMap follows the same robust pattern.

Consistent implementation with proper timeout and error handling.


130-133: GetAllSecrets appropriately marked as not implemented.

This aligns with the documented limitations in the provider documentation. The error message is clear.


170-186: LGTM! SecretExists implementation is correct.

The method properly distinguishes between "not found" (returns false, nil) and actual errors (returns false, err).


188-199: LGTM! Lifecycle methods are appropriate.

Validate performs a simple but necessary initialization check, and Close being a no-op is acceptable if the underlying DVLS client doesn't require explicit cleanup.


201-220: LGTM! parseSecretRef is robust.

The parsing logic properly validates the key format, trims whitespace, and provides clear error messages for invalid inputs.


222-235: LGTM! entryToSecretMap conversion is straightforward.

The method properly converts DVLS credential map strings to byte arrays expected by Kubernetes secrets.


237-252: LGTM! extractPushValue validation is thorough.

The function properly validates that the secret key is specified, exists in the Kubernetes secret, and contains a non-empty value.


254-270: LGTM! Entry operation helpers properly use timeouts.

Both methods correctly wrap DVLS API calls with timeout protection. The value copy at line 261 is appropriate to avoid mutating the input entry.


272-301: LGTM! Generic timeout helpers are well-designed.

The implementation properly uses goroutines with buffered channels to prevent leaks, handles context cancellation, and provides type-safe wrappers for both value-returning and error-only functions.


303-314: LGTM! isNotFoundError provides robust detection.

The dual check using both dvls.IsNotFound() and HTTP 404 status ensures reliable not-found error detection across different DVLS client responses.

go.mod (2)

35-35: LGTM! DVLS provider module correctly integrated.

The provider replacement, requirement declaration, and go-dvls dependency are properly added. The module structure follows the existing pattern for other providers.

Also applies to: 149-149, 198-198


101-101: LGTM! Dependency updates align with maintainer feedback.

The golang.org/x dependencies have been updated to newer versions, including golang.org/x/crypto v0.46.0 as requested by the maintainer in PR comments.

Also applies to: 381-381, 479-483, 485-485

pkg/register/dvls.go (1)

1-30: LGTM! Provider registration follows established patterns.

The build tag, registration call, and structure match the existing provider registration approach. The provider is properly initialized during package init.

docs/provider/devolutions-server.md (1)

1-117: LGTM! Comprehensive provider documentation.

The documentation thoroughly covers authentication setup, configuration, credential types, usage examples, and limitations. Well-structured and informative.

Note: A potential inconsistency between the documentation's claim that "Push secret creates or updates" (line 91) and the actual implementation behavior was flagged in the client.go review.

docs/snippets/dvls-external-secret.yaml (1)

1-61: LGTM! Example snippets are clear and demonstrate key use cases.

The three examples effectively illustrate:

  1. Fetching specific properties
  2. Extracting all fields with dataFrom
  3. Working with Secret entry types

The YAMLlint syntax error on line 1 is a false positive; {% raw %} is valid mkdocs template syntax for including literal YAML in documentation.

apis/externalsecrets/v1/secretstore_dvls_types.go (4)

17-21: LGTM! Clean package structure.

The package declaration and imports are minimal and appropriate. Using only esmeta.SecretKeySelector for credential references follows the secure pattern used across other ESO providers.


23-33: LGTM! Well-structured provider configuration.

The DVLSProvider type follows Kubernetes API conventions:

  • Clear documentation with example
  • Format=uri validation on ServerURL prevents invalid URLs
  • Required annotations on critical fields
  • JSON tags follow camelCase convention

35-40: LGTM! Auth structure supports future extensibility.

The DVLSAuth type provides a clear grouping for authentication configuration and allows for future addition of alternative auth methods without breaking the API.


42-51: LGTM! Secure credential handling.

The DVLSAuthSecretRef type correctly uses SecretKeySelector for both credentials, ensuring secrets are referenced rather than embedded. This follows ESO security best practices and prevents credential exposure in the API specifications.

deploy/crds/bundle.yaml (2)

15654-15728: Schema duplication is appropriate.

This second dvls schema block is identical to the first (lines 3913-3987), which is expected for a CRD bundle that defines the schema across multiple resource types. The same validation and enhancement suggestions from the first block apply here.


3922-3947: The schema structure is correct as designed. The appId and appSecret fields at the secretRef level are properly marked as required, which ensures users must provide both secret references. Within those objects, the key, name, and namespace properties intentionally do not have a required array because this pattern is consistent across 426+ SecretKeySelector definitions throughout the bundle—allowing flexibility where some fields may be defaulted by the provider while others require explicit configuration. This is documented in the field description: "Some instances of this field may be defaulted, in others it may be required."

Likely an incorrect or invalid review comment.

providers/v1/dvls/client_test.go (5)

34-79: LGTM! Clean mock implementation.

The mock credential client is well-structured with proper error injection, operation tracking, and defensive nil handling in the constructor.


81-98: LGTM! Appropriate test stubs.

The push secret stubs correctly implement the required interfaces with minimal viable implementations suitable for testing.


100-134: LGTM! Comprehensive parsing tests.

The test cases thoroughly cover valid formats, invalid formats, empty components, and whitespace handling.


152-319: LGTM! Thorough credential mapping tests.

The tests comprehensively cover all supported credential subtypes (Default, AccessCode, ApiKey, AzureServicePrincipal, ConnectionString, PrivateKey) including error cases (unsupported types) and edge cases (partial data). The verification that empty fields are excluded from the secret map (lines 314-317) is particularly good.

Note: The static analysis tool flagged line 266 as containing a private key, but this is a false positive—it's a test fixture string literal, not an actual secret.


321-447: LGTM! Solid CRUD operation tests.

The tests effectively validate:

  • Secret retrieval with not-found handling and successful mapping
  • Existence checks with error propagation
  • Delete operations with tracking verification
  • Push operations across multiple subtypes with proper error handling for not-found and unsupported scenarios
providers/v1/dvls/provider.go (8)

32-52: LGTM! Standard provider implementation.

The NewClient method follows the External Secrets provider pattern correctly: extracts configuration, obtains store metadata, creates the underlying client, and wraps it appropriately.


54-83: LGTM! Proper validation logic.

The validation correctly handles:

  • Empty ServerURL check
  • URL parsing errors (separate from scheme/host validation)
  • Scheme/host presence validation
  • Scheme restriction to http/https
  • Authentication reference validation

The previous issue with error wrapping when parsing succeeds but scheme/host are empty has been properly addressed.


85-88: LGTM! Correct capability declaration.

The provider correctly declares SecretStoreReadWrite capability, which aligns with the implementation of both read operations (GetSecret, GetSecretMap) and write operations (PushSecret, DeleteSecret) in the client.


90-106: LGTM! Thorough authentication validation.

The function properly validates both appId and appSecret references by checking required fields and delegating to the standard esutils.ValidateSecretSelector for consistency with other providers.


108-118: LGTM! Clear validation helper.

The helper ensures both Name and Key are present in the secret selector with contextual error messages.


120-127: LGTM! Defensive configuration extraction.

The function correctly validates the configuration hierarchy with appropriate nil checks at each level.


129-132: LGTM! Standard factory function.


134-144: LGTM! Proper registration metadata.

Both functions correctly provide the metadata needed for provider registration:

  • ProviderSpec returns the schema structure for CRD generation
  • MaintenanceStatus appropriately declares the provider as maintained
config/crds/bases/external-secrets.io_clustersecretstores.yaml (1)

1958-2038: DVLS provider schema is consistent and correctly wired into ClusterSecretStore v1.

The dvls block mirrors existing provider patterns: nested auth.secretRef using standard SecretKeySelectors for appId and appSecret, and serverUrl required alongside auth. Required lists and field shapes look correct, and descriptions are clear and aligned with neighboring providers. I don’t see any schema issues here.

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

♻️ Duplicate comments (1)
providers/v1/dvls/client.go (1)

135-156: Documentation and implementation mismatch for PushSecret.

The implementation requires the entry to exist beforehand (line 149 returns an error if not found), but past review comments indicate the documentation states "creates or updates."

Please align the documentation and implementation:

  • Option 1 (simpler): Update the documentation to clarify that entries must exist before pushing secrets
  • Option 2: Extend the implementation to create new entries when missing

Based on past review comments suggesting this issue.

🧹 Nitpick comments (3)
providers/v1/dvls/auth_test.go (1)

86-88: Error from AddToScheme is silently ignored.

The blank identifier suppresses any error from corev1.AddToScheme(scheme). While this typically succeeds, silently ignoring errors in tests can mask unexpected issues.

🔎 Consider using require to fail fast on setup errors
+import "github.com/stretchr/testify/require"
+
 scheme := runtime.NewScheme()
-_ = corev1.AddToScheme(scheme)
+require.NoError(t, corev1.AddToScheme(scheme))
 kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(secret).Build()
apis/externalsecrets/v1/secretstore_dvls_types.go (1)

23-32: Consider adding URL format validation to ServerURL.

The DVLSProvider struct is well-defined and follows Kubernetes API conventions. However, adding URL format validation to the ServerURL field would help catch configuration errors earlier.

🔎 Proposed addition of URL format validation
 // DVLSProvider configures a store to sync secrets using Devolutions Server.
 type DVLSProvider struct {
 	// ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
+	// +kubebuilder:validation:Pattern=`^https?://[a-zA-Z0-9\-\.]+[a-zA-Z0-9]+(:[0-9]+)?(/.*)?$`
 	// +kubebuilder:validation:Required
 	ServerURL string `json:"serverUrl"`
 
 	// Auth defines the authentication method to use.
 	// +kubebuilder:validation:Required
 	Auth DVLSAuth `json:"auth"`
 }

Alternatively, you can use the simpler format validator:

 // DVLSProvider configures a store to sync secrets using Devolutions Server.
 type DVLSProvider struct {
 	// ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
+	// +kubebuilder:validation:Format=uri
 	// +kubebuilder:validation:Required
 	ServerURL string `json:"serverUrl"`
 
 	// Auth defines the authentication method to use.
 	// +kubebuilder:validation:Required
 	Auth DVLSAuth `json:"auth"`
 }
deploy/crds/bundle.yaml (1)

3981-3983: Consider adding format validation for serverUrl.

The serverUrl field currently accepts any string without format validation. Adding a URL format constraint or pattern validation would catch configuration errors earlier at apply time rather than at runtime.

🔎 Example: Add format validation
                       serverUrl:
                         description: ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
                         type: string
+                        format: uri
+                        pattern: ^https?://

Alternatively, if format: uri is too strict, a simple pattern ensuring the URL starts with http:// or https:// would be helpful.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c44fe65 and 750708e.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
🚧 Files skipped from review as they are similar to previous changes (11)
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/go.mod
  • tests/snapshot/secretstore-v1.yaml
  • apis/externalsecrets/v1/secretstore_types.go
  • tests/snapshot/clustersecretstore-v1.yaml
  • docs/introduction/stability-support.md
  • providers/v1/dvls/provider_test.go
  • docs/snippets/dvls-secret-store.yaml
  • docs/snippets/dvls-push-secret.yaml
  • hack/api-docs/mkdocs.yml
  • docs/api/spec.md
🧰 Additional context used
🧬 Code graph analysis (4)
providers/v1/dvls/auth_test.go (2)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSProvider (24-32)
  • DVLSAuth (35-39)
  • DVLSAuthSecretRef (42-50)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-49)
providers/v1/dvls/client_test.go (5)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
apis/generators/v1alpha1/types_password.go (1)
  • Password (77-82)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
providers/v1/keepersecurity/client.go (1)
  • Secret (105-111)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
apis/externalsecrets/v1/zz_generated.deepcopy.go (2)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSAuth (35-39)
  • DVLSAuthSecretRef (42-50)
  • DVLSProvider (24-32)
providers/v1/vault/util/vault.go (1)
  • Auth (31-33)
providers/v1/dvls/provider.go (3)
providers/v1/dvls/client.go (2)
  • NewClient (70-75)
  • Client (42-45)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-49)
apis/externalsecrets/v1/secretstore_dvls_types.go (2)
  • DVLSAuthSecretRef (42-50)
  • DVLSProvider (24-32)
🪛 Gitleaks (8.30.0)
providers/v1/dvls/client_test.go

[high] 266-278: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🪛 YAMLlint (1.37.1)
docs/snippets/dvls-external-secret.yaml

[error] 1-1: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (31)
pkg/register/dvls.go (1)

1-30: LGTM! Clean provider registration.

The DVLS provider registration follows the established pattern with correct build tags and clean initialization. The init() function properly registers the provider with all required parameters.

go.mod (5)

35-35: LGTM! Correct replace directive.

The DVLS provider replace directive is properly positioned alphabetically and follows the established pattern for provider modules in this monorepo.


101-101: LGTM! Addresses maintainer feedback.

The golang.org/x/crypto update to v0.46.0 addresses the specific request from Skarlso mentioned in the PR comments.


149-149: LGTM! Proper provider dependency declaration.

The DVLS provider is correctly added as an indirect dependency with the standard placeholder version, consistent with other local provider modules.


381-381: LGTM! Transitive dependency updates.

The golang.org/x/* module updates are expected when adding a new provider and running make reviewable, as mentioned in the PR comments. These transitive dependencies are properly managed.

Also applies to: 479-485


198-198: No action required. The github.com/Devolutions/go-dvls v0.14.0 dependency is actively used in the DVLS provider implementation (providers/v1/dvls/) and no known security vulnerabilities were found for this version.

config/crds/bases/external-secrets.io_clustersecretstores.yaml (1)

1958-2038: DVLS provider CRD schema looks consistent and complete

The dvls block follows existing provider patterns (required auth.secretRef with appId/appSecret selectors and a required serverUrl), and the SecretKeySelector shapes and constraints match the rest of the CRD. I don’t see schema or validation gaps here.

apis/externalsecrets/v1/zz_generated.deepcopy.go (1)

1263-1310: Auto-generated DeepCopy code looks correct.

The generated DeepCopy implementations for DVLSAuth, DVLSAuthSecretRef, and DVLSProvider follow the established pattern used by other providers in this file. The SecretStoreProvider.DeepCopyInto method correctly handles the optional DVLS field with nil-check and allocation.

Also applies to: 3556-3560

docs/snippets/dvls-external-secret.yaml (1)

1-61: Documentation snippets are well-structured.

The three ExternalSecret examples demonstrate key use cases for the DVLS provider. The static analysis error about {% raw %} is a false positive—these are Jinja2 template directives required by MkDocs for proper rendering and are consistent with other snippet files in the project.

docs/provider/devolutions-server.md (2)

1-117: Comprehensive provider documentation.

The documentation covers all essential aspects: authentication, configuration, usage patterns, limitations, and troubleshooting. The credential type mapping table (lines 56-67) is particularly helpful for users.


14-14: Verify menu item name in DVLS UI.

"Applications identities" may be the exact menu label in DVLS. If not, consider correcting to "Application Identities" for grammatical consistency.

config/crds/bases/external-secrets.io_secretstores.yaml (1)

1958-2038: DVLS provider schema is consistent and well‑structured

The dvls block mirrors existing provider patterns (auth via SecretRefs with appId/appSecret and a required serverUrl), field names and descriptions are clear, and required lists enforce the minimal config correctly. I don't see any schema or compatibility issues here.

apis/externalsecrets/v1/secretstore_dvls_types.go (3)

1-21: LGTM! Clean structure and minimal imports.

The license header, package declaration, and imports are properly structured. The single import of esmeta is necessary for the SecretKeySelector type used in secret references.


34-39: LGTM! Good design for future extensibility.

The DVLSAuth struct is well-designed. While it currently contains only SecretRef, this structure provides a clean path for adding alternative authentication methods in the future (e.g., OAuth, token-based auth) without breaking changes.


41-50: LGTM! Secure credential handling using Kubernetes standards.

The DVLSAuthSecretRef struct correctly uses esmeta.SecretKeySelector for both AppID and AppSecret fields, following Kubernetes best practices for referencing sensitive data stored in secrets. The required annotations and clear naming make the API intuitive.

deploy/crds/bundle.yaml (1)

15654-15728: LGTM! Duplicate schema is expected.

This hunk adds the identical DVLS provider schema as hunk 1, which is the correct approach for CRD bundle files where the same provider configuration needs to be available across multiple CRD types (e.g., SecretStore and ClusterSecretStore).

The same suggestion about serverUrl format validation from the previous hunk applies here as well.

providers/v1/dvls/client_test.go (3)

257-281: LGTM! Comprehensive test coverage for PrivateKey credential type.

The test correctly validates all fields (username, password, private-key, public-key, passphrase) are properly mapped from the DVLS entry to the secret map.

Note: The static analysis warning about the private key on lines 266-278 is a false positive - this is test data, not an actual secret.


296-318: LGTM! Good edge case coverage for partial credentials.

The test correctly validates that empty fields are not included in the secret map, which is the expected behavior.


380-447: LGTM! Good coverage of PushSecret scenarios.

The tests validate:

  • Successful updates for Default and AccessCode credential types
  • Error handling when entry is not found
  • Error handling for unsupported credential subtypes

The mock tracking correctly verifies that updates are applied.

providers/v1/dvls/provider.go (5)

37-52: LGTM! Clean client creation flow.

The method correctly:

  • Extracts and validates the DVLS provider configuration
  • Obtains the store kind for proper secret resolution
  • Creates the DVLS client with appropriate error wrapping
  • Returns a wrapped SecretsClient

54-83: LGTM! ValidateStore correctly validates all required configuration.

The validation logic properly:

  • Checks for required serverUrl
  • Separates URL parsing errors from empty scheme/host validation
  • Validates allowed schemes (http/https)
  • Validates authentication secret references

The previous issue with wrapping nil errors has been properly addressed.


85-88: LGTM! Capabilities correctly reflect read/write support.


90-118: LGTM! Validation helpers provide thorough secret reference validation.

The functions correctly:

  • Validate required fields (name, key) are present
  • Use framework utilities for namespace/scope validation
  • Return clear, specific error messages

120-144: LGTM! Factory and registration functions follow the provider pattern.

These functions correctly implement the provider registration interface, enabling the DVLS provider to be discovered and initialized by the External Secrets operator.

providers/v1/dvls/client.go (7)

33-75: LGTM! Well-structured client with good separation of concerns.

The design correctly:

  • Uses interface abstraction for testability (credentialClient)
  • Provides an adapter for the real DVLS client
  • Sets a reasonable default timeout (15 seconds)
  • Follows the External Secrets SecretsClient interface

77-128: LGTM! GetSecret and GetSecretMap correctly implement secret retrieval.

Both methods properly:

  • Parse the vault-id/entry-id format
  • Use timeout-wrapped DVLS calls
  • Map not-found errors to esv1.NoSecretErr
  • Support property-specific or full secret retrieval

158-168: LGTM! DeleteSecret now correctly includes timeout handling.

The method properly uses runWithTimeoutErr to ensure the delete operation cannot hang indefinitely. The previous timeout handling issue has been resolved.


170-199: LGTM! SecretExists, Validate, and Close are correctly implemented.

  • SecretExists properly interprets not-found errors as false existence
  • Validate ensures the client is initialized
  • Close is appropriately a no-op for the DVLS client

201-220: LGTM! Robust key parsing with good validation.

The method correctly:

  • Splits the key into vault and entry IDs
  • Trims whitespace for flexibility
  • Validates both components are present
  • Returns clear, descriptive error messages

222-252: LGTM! Helper functions provide robust data extraction and validation.

  • entryToSecretMap correctly delegates to the library's ToCredentialMap method
  • extractPushValue thoroughly validates the secret key exists and is non-empty

254-314: LGTM! Well-implemented timeout handling and error detection.

The timeout wrappers correctly:

  • Use context.WithTimeout for deadline enforcement
  • Employ goroutine+select pattern with buffered channels to prevent leaks
  • Provide generic implementations via type parameters
  • Return descriptive timeout errors

The isNotFoundError function properly checks both the DVLS library's IsNotFound helper and explicit 404 status codes.

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: 0

🧹 Nitpick comments (1)
deploy/crds/bundle.yaml (1)

3981-3983: Consider adding URI format validation to serverUrl.

The serverUrl field is defined as a plain string without format validation. Consider adding format: uri to ensure users provide valid URLs.

🔎 Proposed enhancement
                       serverUrl:
                         description: ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
                         type: string
+                        format: uri

This would provide client-side validation and better user feedback when an invalid URL is provided.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 750708e and 4fd2031.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
🚧 Files skipped from review as they are similar to previous changes (16)
  • hack/api-docs/mkdocs.yml
  • providers/v1/dvls/go.mod
  • docs/snippets/dvls-secret-store.yaml
  • pkg/register/dvls.go
  • docs/snippets/dvls-push-secret.yaml
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • docs/provider/devolutions-server.md
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • providers/v1/dvls/auth_test.go
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • tests/snapshot/secretstore-v1.yaml
  • docs/introduction/stability-support.md
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • docs/api/spec.md
  • tests/snapshot/clustersecretstore-v1.yaml
🧰 Additional context used
🧬 Code graph analysis (5)
providers/v1/dvls/auth.go (3)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-32)
providers/v1/vault/util/vault.go (1)
  • Auth (31-33)
apis/externalsecrets/v1/secretstore_types.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-32)
providers/v1/dvls/client.go (3)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/keepersecurity/client.go (1)
  • Secret (105-111)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
apis/externalsecrets/v1/zz_generated.deepcopy.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSAuth (35-39)
  • DVLSAuthSecretRef (42-50)
  • DVLSProvider (24-32)
providers/v1/dvls/client_test.go (1)
providers/v1/dvls/client.go (2)
  • Client (42-45)
  • NewClient (70-75)
🪛 Gitleaks (8.30.0)
providers/v1/dvls/client_test.go

[high] 266-278: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🪛 YAMLlint (1.37.1)
docs/snippets/dvls-external-secret.yaml

[error] 1-1: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (19)
go.mod (2)

101-101: LGTM!

The golang.org/x/* module updates are consistent and properly coordinated across the dependency graph.

Also applies to: 381-381, 479-485


198-198: No action needed — v0.14.0 is a valid and secure release.

The go-dvls v0.14.0 release exists and is free from known security vulnerabilities. The version was released on December 10, 2025 with legitimate feature additions (methods to flatten credential entries and set secret values).

providers/v1/dvls/client.go (8)

33-51: LGTM!

The interface abstraction and constants are well-defined. The 15-second default timeout is reasonable for API operations.


42-75: LGTM!

The adapter pattern cleanly wraps the DVLS credential service, and the constructor properly initializes the client with sensible defaults.


77-110: LGTM!

GetSecret properly handles timeouts, not-found scenarios, and supports both property extraction and full secret retrieval.


112-133: LGTM!

GetSecretMap correctly delegates to the entry conversion logic, and GetAllSecrets appropriately returns an unimplemented error.


158-186: LGTM!

DeleteSecret now includes proper timeout handling, and SecretExists correctly infers existence from API responses.


188-199: LGTM!

The validation logic is appropriate, and the no-op Close implementation is acceptable for the DVLS client.


201-314: LGTM!

The helper functions are well-structured with proper validation, error handling, and timeout management using goroutines and context cancellation.


135-156: The documentation at docs/provider/devolutions-server.md (line 91) accurately states: "Push secret updates an existing entry's password field. The entry must already exist in DVLS." This aligns correctly with the implementation, which enforces this requirement by returning an error if the entry is not found. No documentation update is needed.

apis/externalsecrets/v1/secretstore_types.go (1)

203-206: LGTM!

The DVLS provider field is properly integrated into SecretStoreProvider, following the established pattern for other providers.

docs/snippets/dvls-external-secret.yaml (1)

1-61: LGTM!

The YAML snippets provide comprehensive examples for different DVLS usage patterns. The static analysis syntax error is a false positive—the {% raw %} tags are intentional Liquid template syntax for documentation rendering.

providers/v1/dvls/auth.go (1)

31-49: LGTM!

The authentication logic correctly uses resolvers.SecretKeyRef, which properly enforces ClusterSecretStore-only cross-namespace secret access. Error handling is appropriate with clear context in error messages.

providers/v1/dvls/client_test.go (3)

34-79: LGTM!

The mock credential client is well-designed with proper error injection capabilities and operation tracking for test verification.


100-319: LGTM!

Excellent test coverage for parseSecretRef, Validate, entryToSecretMap, and various credential subtypes. The tests handle edge cases like whitespace, empty fields, and unsupported types.


321-446: LGTM!

Comprehensive integration tests covering GetSecret, GetSecretMap, SecretExists, DeleteSecret, and PushSecret operations for various scenarios including not-found cases and different credential subtypes. The static analysis warning about a private key on line 266 is a false positive—it's a truncated example key in test data.

deploy/crds/bundle.yaml (2)

3916-3980: LGTM! Authentication schema is well-designed.

The authentication schema properly uses Kubernetes secret references for both Application ID and Application Secret, which is secure and follows external-secrets conventions. The required fields, validation patterns, and descriptions are all appropriate.


15654-15728: Schema duplication is expected in bundle.yaml.

This hunk contains an identical DVLS provider schema definition. This duplication is normal for bundle.yaml files that consolidate multiple CRD definitions (e.g., SecretStore and ClusterSecretStore).

The same observations from the first hunk apply here:

  • Consider adding format: uri validation to the serverUrl field (line 15723).
  • The authentication schema is well-designed and secure.
apis/externalsecrets/v1/zz_generated.deepcopy.go (1)

1263-1277: LGTM! DVLS deepcopy implementations are correctly generated.

The auto-generated deepcopy methods for DVLS types follow the established patterns consistently:

  • DVLSAuth, DVLSAuthSecretRef, DVLSProvider: Each implements the standard deepcopy pattern with shallow copy followed by recursive DeepCopyInto calls on nested fields
  • SecretStoreProvider DVLS handling: Correctly implements the nil-check, allocation, and recursive deepcopy pattern used by all optional providers
  • Alphabetical ordering: DVLS is properly placed between Device42 and Infisical

The implementations match the type definitions provided in the relevant code snippets and are consistent with similar providers (Device42, Barbican, etc.) throughout the file.

Also applies to: 1279-1294, 1296-1310, 3556-3560

Copy link
Copy Markdown
Contributor

@Skarlso Skarlso left a comment

Choose a reason for hiding this comment

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

I have a couple of questions here and there. :) Nothing big all in all. Well done.

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: 5

🤖 Fix all issues with AI agents
In @docs/snippets/dvls-external-secret.yaml:
- Around line 1-61: Remove the Jinja raw markers `{% raw %}` and `{% endraw %}`
that wrap the YAML document; they are invalid YAML tokens causing yamllint
failures. Edit the dvls-external-secret.yaml snippet to keep the three
Kubernetes ExternalSecret documents (the three 'apiVersion:
external-secrets.io/v1' / 'kind: ExternalSecret' blocks with their metadata
names database-credentials, api-credentials, and app-secret) and delete only the
`{% raw %}` and `{% endraw %}` lines so the file begins with '---' and ends with
the final resource without any Jinja raw tags.

In @providers/v1/dvls/auth.go:
- Around line 33-41: The code dereferences provider.Auth.SecretRef when calling
resolvers.SecretKeyRef; add explicit nil checks for provider, provider.Auth, and
provider.Auth.SecretRef at the start of the function (before using
provider.Auth.SecretRef) and return clear wrapped errors (e.g., "missing
provider auth", "missing provider auth secretRef") to avoid panics; then call
resolvers.SecretKeyRef with the validated pointers as before and update the
returned fmt.Errorf messages to use consistent, user-facing wording like "failed
to get app ID" and "failed to get app secret" for the appID/appSecret error
cases.

In @providers/v1/dvls/client.go:
- Around line 230-245: The function extractPushValue should explicitly check for
a nil secret.Data before indexing it; add an early guard in extractPushValue
that returns a clear error (including secret.Name and the expected secret key
from data.GetSecretKey()) when secret.Data == nil so callers receive a
descriptive "no data in secret" style error rather than the generic "not found"
error when attempting to access secret.Data[data.GetSecretKey()].
- Around line 126-153: PushSecret currently assumes the incoming *corev1.Secret
and its Data map are non-nil and can panic; add guards at the start of
Client.PushSecret to return a clear error if secret == nil or secret.Data == nil
(or missing the key used by extractPushValue) before calling extractPushValue,
and adjust extractPushValue callers (or the function itself) to handle absent
keys gracefully; keep existing logic for parsing remote key (parseSecretRef),
fetching entry (c.dvls.GetByID), and updating via
existingEntry.SetCredentialSecret and c.dvls.Update.
- Around line 247-258: Replace fragile string matching in isNotFoundError with a
status-code check: when casting to dvls.RequestError (reqErr), check
reqErr.StatusCode == http.StatusNotFound instead of
strings.Contains(reqErr.Error(), "404"); also ensure the net/http package is
imported so http.StatusNotFound is available. This updates the fallback path in
isNotFoundError to use the RequestError.StatusCode field for reliable not-found
detection.
🧹 Nitpick comments (6)
config/crds/bases/external-secrets.io_secretstores.yaml (1)

2032-2044: Consider tightening serverUrl schema (URI format + optional CEL validation for https-by-default).
Right now serverUrl is just type: string, which is consistent with many providers but loses some cheap validation. At minimum, consider format: uri. If insecure is intended to allow plain HTTP (as the description says), consider a CEL rule to require https:// unless insecure: true.

Proposed diff (URI format)
                   serverUrl:
                     description: ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
+                    format: uri
                     type: string
providers/v1/dvls/auth_test.go (1)

117-122: Test approach for non-error case could be clearer.

The logic here validates that Kubernetes secret retrieval succeeded by checking that specific error messages are absent, even when err != nil. This works because the DVLS client creation will fail with the test server URL, but the goal is to verify the secret access phase passed.

Consider mocking DVLS client creation for test clarity

While the current approach is functional, consider using a test interface or dependency injection to mock dvls.NewClient so the test can:

  1. Explicitly verify successful secret retrieval
  2. Return success instead of relying on a fake server error
  3. Make the test's intent more obvious to future maintainers

Example pattern:

// Define interface in production code
type dvlsClientFactory interface {
    NewClient(appID, appSecret, serverURL string) (Client, error)
}

// Use mock in tests to return success

This would eliminate the need to parse error messages to infer which phase succeeded.

deploy/crds/bundle.yaml (1)

3987-3989: Add validation constraints to the serverUrl field.

The serverUrl field lacks schema-level validation (no pattern, format, or length constraints). This could allow invalid URLs to be accepted at admission time, leading to runtime errors when the provider attempts to connect.

Consider adding validation to the source Go type (likely in apis/externalsecrets/v1/secretstore_dvls_types.go) using kubebuilder markers:

  • URL format validation (e.g., +kubebuilder:validation:Pattern for http/https URLs)
  • Minimum length constraint
  • Consider using +kubebuilder:validation:Format=uri if supported
Example kubebuilder markers for the source Go type
// ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https?://`
ServerURL string `json:"serverUrl"`
providers/v1/dvls/client.go (3)

194-213: parseSecretRef: good validation; consider rejecting keys with leading/trailing / early for clearer errors. (Optional.)


103-124: Consider using a shared error sentinel for GetAllSecrets instead of creating a new error each call. The Senhasegura provider defines errNotImplemented = errors.New("not implemented") and returns it directly, which is more efficient than wrapping a string constant with errors.New() on each invocation. This pattern improves code consistency across providers.


155-179: Consider treating "not found" on delete as success for idempotency. Vault provider already implements this pattern—when a secret doesn't exist during deletion, it returns success rather than propagating the error. This aligns with standard delete semantics and makes DeleteSecret idempotent. The isNotFoundError helper already exists in this file and is used in other methods. Verify whether controllers expect this behavior and adjust accordingly.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fd2031 and e569456.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
🚧 Files skipped from review as they are similar to previous changes (11)
  • providers/v1/dvls/go.mod
  • apis/externalsecrets/v1/secretstore_types.go
  • tests/snapshot/secretstore-v1.yaml
  • docs/snippets/dvls-secret-store.yaml
  • hack/api-docs/mkdocs.yml
  • providers/v1/dvls/provider_test.go
  • providers/v1/dvls/provider.go
  • docs/snippets/dvls-push-secret.yaml
  • providers/v1/dvls/client_test.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • docs/provider/devolutions-server.md
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧬 Code graph analysis (3)
providers/v1/dvls/auth_test.go (2)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSProvider (24-38)
  • DVLSAuth (41-45)
  • DVLSAuthSecretRef (48-56)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-49)
apis/externalsecrets/v1/zz_generated.deepcopy.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSAuth (41-45)
  • DVLSAuthSecretRef (48-56)
  • DVLSProvider (24-38)
providers/v1/dvls/client.go (2)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/keepersecurity/client.go (1)
  • Secret (105-111)
🪛 YAMLlint (1.37.1)
docs/snippets/dvls-external-secret.yaml

[error] 1-1: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (16)
config/crds/bases/external-secrets.io_secretstores.yaml (1)

1958-2044: No duplicate YAML keys detected—dvls: appears exactly once at line 1958 under the provider properties block with the correct structure. No action needed.

pkg/register/dvls.go (1)

1-30: LGTM! Clean provider registration.

The DVLS provider registration follows the established pattern for External Secrets providers, with appropriate build tags and standard initialization.

apis/externalsecrets/v1/secretstore_dvls_types.go (2)

23-56: Well-structured API types with proper validation.

The DVLS provider types follow External Secrets conventions, with appropriate kubebuilder validation markers and clear field documentation. The use of esmeta.SecretKeySelector for credentials is consistent with other providers.


29-33: Verify the Insecure field is required for legitimate use cases.

The Insecure field enables plain HTTP connections, which introduces security risks. While the comment appropriately warns against production use, please confirm:

  • Is this needed for specific development/testing scenarios or on-premise deployments without TLS?
  • Are there additional safeguards or documentation to prevent accidental misuse in production?

The warning comment is helpful, but consider whether this capability should be feature-gated or accompanied by additional runtime warnings when enabled.

providers/v1/dvls/auth_test.go (1)

34-67: Well-structured cross-namespace security test.

The table-driven test correctly validates that:

  • ClusterSecretStore can access secrets in any namespace
  • SecretStore is restricted to its own namespace

This aligns with External Secrets security model.

go.mod (3)

35-35: LGTM! DVLS provider module properly integrated.

The replace directive and require statement follow the established pattern for External Secrets provider modules.

Also applies to: 149-149


101-101: LGTM! Dependency updates applied as requested.

The golang.org/x/* module updates (including crypto v0.46.0 as requested in PR comments) are appropriate.

Also applies to: 381-381, 479-485


198-198: No security vulnerabilities found for github.com/Devolutions/go-dvls v0.14.0 in public databases. The dependency is safe to use at this version.

apis/externalsecrets/v1/zz_generated.deepcopy.go (2)

1263-1310: DVLS deepcopy implementations look correct for the new API structs.


3556-3560: DVLS added to SecretStoreProvider.DeepCopyInto correctly (nil-check + deep copy).

providers/v1/dvls/client.go (2)

31-68: Nice seam for testing via credentialClient + realCredentialClient adapter.


70-101: GetSecret: good not-found mapping to esv1.NoSecretErr + sensible default property=password.

tests/__snapshot__/clustersecretstore-v1.yaml (1)

300-312: Snapshot addition for spec.provider.dvls matches expected schema shape (serverUrl + auth.secretRef).

docs/introduction/stability-support.md (1)

100-100: DVLS provider documentation and table entries verified. The provider doc exists at docs/provider/devolutions-server.md with complete authentication setup instructions. Both table entries (lines 100 and 142) are properly formatted and consistent with other providers.

providers/v1/dvls/auth.go (2)

17-29: Clean package structure and imports.

The package declaration, imports, and overall structure are well-organized. The function correctly wraps the DVLS client's credential entry in a realCredentialClient for the provider interface.

Note: The credentialClient interface and realCredentialClient struct are not defined in this file, but based on the AI summary, they are expected to be defined in provider.go.

Also applies to: 48-48


43-46: The review comment's primary concern about ServerURL validation is not applicable. The provider.ServerURL is already comprehensively validated in ValidateStore() (lines 61–79 of providers/v1/dvls/provider.go) before NewDVLSClient() is invoked. The validation checks for empty values, valid URL parsing, required scheme and host, whitelisted schemes (http/https), and enforces the insecure flag for HTTP. Adding duplicate validation in auth.go is redundant.

The library (go-dvls v0.14.0) is actively maintained by Devolutions with a recent v0.15.0 release (Jan 9, 2026). The API usage is correct and error handling is appropriate. No security issues were identified with the current version.

Likely an incorrect or invalid review comment.

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

🤖 Fix all issues with AI agents
In @config/crds/bases/external-secrets.io_secretstores.yaml:
- Around line 1958-2044: The v1beta1 CRD is missing the DVLS provider schema;
add the same "dvls" object from the v1 schema into the v1beta1 providers section
so DVLS support is parity restored (copy the "dvls" block including properties:
auth -> secretRef -> appId/appSecret, insecure, serverUrl, and required: [auth,
serverUrl]); in that block strengthen serverUrl by adding format: uri and
optionally add a CEL validation rule that enforces serverUrl starts with
"https://" unless insecure is true (use the dvls.insecure and dvls.serverUrl
fields in the CEL expression), ensuring field names like
dvls.auth.secretRef.appId, dvls.auth.secretRef.appSecret, dvls.insecure and
dvls.serverUrl match the v1 definitions exactly.
🧹 Nitpick comments (5)
providers/v1/dvls/client.go (2)

32-35: Consider using sentinel errors for better error handling.

The error constants are defined as strings but used with fmt.Errorf. For errNotImplemented, consider using a sentinel error for consistent error checking.

♻️ Optional improvement
 const (
-	errNotImplemented   = "not implemented"
 	errFailedToGetEntry = "failed to get entry: %w"
 )
+
+var errNotImplemented = errors.New("not implemented")

Then update line 124:

-	return nil, errors.New(errNotImplemented)
+	return nil, errNotImplemented

188-199: Validate could perform a lightweight connectivity check.

The current Validate only checks if the client is initialized but doesn't verify connectivity to DVLS. Consider adding a lightweight API call to confirm the client can reach DVLS.

Other providers often perform a simple API call during validation to ensure credentials are working. This would catch configuration issues earlier. However, the current implementation is acceptable for alpha stability.

providers/v1/dvls/provider_test.go (1)

29-38: Unused test constants.

testAppID and testAppSecret constants are defined but never used in the tests. Consider removing them to avoid dead code, or use them in place of string literals if they're intended for future use.

Suggested fix
 const (
 	testNamespace = "default"
 	testServerURL = "https://dvls.example.com"
 	secretName    = "dvls-secret"
 	appIDKey      = "app-id"
 	appSecretKey  = "app-secret"
 	otherNS       = "other"
-	testAppID     = "test-app-id"
-	testAppSecret = "test-app-secret"
 )
providers/v1/dvls/client_test.go (1)

136-150: Consider adding a positive validation test.

The Validate test only covers the nil client case. Consider adding a test case for successful validation with a properly initialized client to ensure complete coverage.

deploy/crds/bundle.yaml (1)

3913-3993: Well-structured DVLS provider schema.

The CRD schema for the DVLS provider is well-designed and follows Kubernetes conventions. The required fields are properly marked, validation patterns are appropriate, and the security warning on the insecure flag is clear.

Optional enhancements for better validation

Consider adding format validation to serverUrl to catch malformed URLs at admission time:

                        serverUrl:
                          description: ServerURL is the DVLS instance URL (e.g., https://dvls.example.com).
                          type: string
+                         format: uri

Optionally, you could also make the insecure default explicit for clarity (though Kubernetes defaults booleans to false):

                        insecure:
                          description: |-
                            Insecure allows connecting to DVLS over plain HTTP.
                            This is NOT RECOMMENDED for production use.
                            Set to true only if you understand the security implications.
                          type: boolean
+                         default: false
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e569456 and ef7b77e.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
🚧 Files skipped from review as they are similar to previous changes (9)
  • hack/api-docs/mkdocs.yml
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-external-secret.yaml
  • tests/snapshot/secretstore-v1.yaml
  • providers/v1/dvls/go.mod
  • tests/snapshot/clustersecretstore-v1.yaml
  • docs/snippets/dvls-secret-store.yaml
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧬 Code graph analysis (8)
apis/externalsecrets/v1/secretstore_types.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-38)
pkg/register/dvls.go (1)
providers/v1/dvls/provider.go (3)
  • NewProvider (134-136)
  • ProviderSpec (139-143)
  • MaintenanceStatus (146-148)
providers/v1/dvls/auth_test.go (2)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSProvider (24-38)
  • DVLSAuth (41-45)
  • DVLSAuthSecretRef (48-56)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-53)
providers/v1/dvls/client_test.go (1)
providers/v1/dvls/client.go (2)
  • Client (40-42)
  • NewClient (67-69)
providers/v1/dvls/auth.go (2)
providers/v1/dvls/client.go (2)
  • Client (40-42)
  • NewClient (67-69)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-38)
providers/v1/dvls/provider.go (4)
providers/v1/dvls/client.go (2)
  • NewClient (67-69)
  • Client (40-42)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-53)
apis/externalsecrets/v1/secretstore_dvls_types.go (2)
  • DVLSAuthSecretRef (48-56)
  • DVLSProvider (24-38)
apis/externalsecrets/v1/provider_schema_maintenance.go (1)
  • MaintenanceStatusMaintained (29-29)
providers/v1/dvls/client.go (3)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/keepersecurity/client.go (1)
  • Secret (105-111)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
apis/externalsecrets/v1/zz_generated.deepcopy.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSAuth (41-45)
  • DVLSAuthSecretRef (48-56)
  • DVLSProvider (24-38)
🪛 Gitleaks (8.30.0)
providers/v1/dvls/client_test.go

[high] 266-278: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🔇 Additional comments (33)
apis/externalsecrets/v1/secretstore_types.go (1)

203-205: LGTM! Clean provider integration.

The DVLS provider field follows the established pattern perfectly—proper documentation, kubebuilder markers, JSON tags, and alphabetical placement.

go.mod (3)

35-35: LGTM! Standard provider module integration.

The replace directive and require entry follow the exact pattern used for all other providers in the monorepo.

Also applies to: 149-149


101-101: golang.org/x/crypto update to v0.46.0 is valid.

The version exists and is available in the Go module registry.


198-198: go-dvls v0.15.0 is valid and no security vulnerabilities found.

v0.15.0 was released on January 9, 2026, with context support improvements to the client library. No known security vulnerabilities affect this version of the go-dvls package.

apis/externalsecrets/v1/zz_generated.deepcopy.go (2)

1263-1310: LGTM - Auto-generated deepcopy implementations follow established patterns.

The deepcopy methods for DVLSAuth, DVLSAuthSecretRef, and DVLSProvider correctly handle their respective fields:

  • Value types copied directly
  • Nested structs use DeepCopyInto appropriately

3556-3560: LGTM - DVLS field handling in SecretStoreProvider follows the standard pattern.

The nil-check and allocation pattern is consistent with other provider fields in this function.

providers/v1/dvls/client.go (4)

71-102: LGTM - GetSecret implementation with sensible defaults.

Good design choices:

  • Proper not-found error handling with esv1.NoSecretErr
  • Default to "password" property when none specified (consistent with 1Password provider as noted)
  • Clear error messages for missing properties

127-160: LGTM - PushSecret correctly requires pre-existing entry.

The implementation appropriately:

  • Validates the secret parameter
  • Requires the entry to exist before updating (documented behavior)
  • Only updates the credential secret field

This is a reasonable constraint for DVLS integration.


201-220: LGTM - Robust key parsing with clear error messages.

The parseSecretRef function:

  • Uses SplitN correctly to handle entry IDs containing /
  • Trims whitespace for user convenience
  • Provides clear error messages for invalid formats

237-256: LGTM - Thorough validation in extractPushValue.

Good defensive programming:

  • Checks for empty secretKey
  • Validates secret.Data is not nil
  • Ensures value is not empty
docs/introduction/stability-support.md (2)

100-100: LGTM - Provider stability entry is accurate.

Alpha stability is appropriate for a new provider. The maintainer matches the PR author.


142-142: LGTM - Feature support accurately reflects implementation.

The feature matrix correctly shows:

  • ✓ Store validation (implemented in Validate)
  • ✓ Push secret (implemented in PushSecret)
  • No find by name/tags (GetAllSecrets returns not implemented)
pkg/register/dvls.go (1)

1-29: LGTM - Provider registration follows established patterns.

The build tag, imports, and registration call are consistent with other providers in the codebase (e.g., barbican.go, ngrok.go).

docs/provider/devolutions-server.md (3)

1-6: LGTM - Clear and informative introduction.

Good context about what DVLS is and its capabilities.


53-70: LGTM - Helpful credential type mapping table.

The table clearly documents which properties are available for each credential type, which will help users configure their ExternalSecrets correctly.


96-101: LGTM - Limitations are clearly documented.

Good transparency about current limitations:

  • GetAllSecrets not supported
  • Custom CA certificates workaround
  • UUID-only references
  • Certificate entries not supported

This helps users understand what to expect.

providers/v1/dvls/auth_test.go (1)

111-122: Good handling of expected DVLS connection failure in tests.

The test correctly handles the case where Kubernetes secret access succeeds but the DVLS connection fails (expected with fake server). The assertions verify that the error is not from secret access, which is the behavior being tested.

providers/v1/dvls/provider_test.go (2)

40-372: LGTM!

The ValidateStore tests provide comprehensive coverage of validation scenarios including valid configurations, cluster store namespace requirements, HTTP/HTTPS URL handling, and auth secret reference validation. The assertions on specific error messages ensure validation logic is precise.


374-395: LGTM!

The utility function tests (Capabilities, NewProvider, ProviderSpec, MaintenanceStatus) appropriately verify the public API surface of the DVLS provider.

providers/v1/dvls/client_test.go (7)

34-48: LGTM!

The mock credential client is well-structured with configurable error injection and operation tracking (lastUpdated, lastDeleted), enabling thorough testing of client behavior under various conditions.


50-79: LGTM!

The mock methods correctly simulate DVLS API behavior, including proper 404 error responses for missing entries and tracking of mutation operations.


81-99: LGTM!

The stub types provide minimal but sufficient implementations for testing PushSecret, SecretExists, and DeleteSecret operations.


100-134: LGTM!

The parseSecretRef tests thoroughly cover the key format parsing including edge cases for invalid formats, empty components, and whitespace handling.


257-281: LGTM!

The PrivateKey credential type test uses placeholder/dummy key content for testing purposes. The static analysis warning about a private key is a false positive since "-----BEGIN RSA PRIVATE KEY-----\nMIIE..." is clearly truncated test data, not a real secret.


321-350: LGTM!

The GetSecret and GetSecretMap tests properly verify not-found error handling and successful secret retrieval with property extraction.


352-447: LGTM!

The SecretExists, DeleteSecret, and PushSecret tests provide solid coverage of the write operations, including error propagation and handling of unsupported credential subtypes.

providers/v1/dvls/provider.go (6)

32-35: LGTM!

The interface compliance check var _ esv1.Provider = &Provider{} ensures compile-time verification that Provider implements the required interface.


37-52: LGTM!

The NewClient function properly extracts provider configuration, resolves the store kind for secret resolution, and delegates to NewDVLSClient for authentication handling. Error messages provide appropriate context.


54-87: LGTM!

The ValidateStore function provides comprehensive validation:

  • Server URL presence and format verification
  • Scheme validation (http/https only)
  • Security-conscious requirement for explicit insecure: true when using HTTP
  • Proper delegation to validateAuthSecretRef for authentication configuration

94-122: LGTM!

The validateAuthSecretRef and requireSecretSelector functions provide layered validation: first ensuring required fields are present, then delegating to esutils.ValidateSecretSelector for store-level validation (e.g., namespace requirements for ClusterSecretStore).


124-131: LGTM!

The getDVLSProvider function properly validates the configuration chain (spec → provider → DVLS) and returns a clear error message when configuration is missing.


133-148: LGTM!

The registration functions (NewProvider, ProviderSpec, MaintenanceStatus) follow the standard External Secrets Operator provider registration pattern. The MaintenanceStatusMaintained status correctly indicates this is an actively supported provider.

deploy/crds/bundle.yaml (1)

15660-15740: Expected schema duplication in CRD bundle.

This hunk contains the same DVLS provider schema as the first hunk, which is expected for bundle.yaml (typically one definition for SecretStore and one for ClusterSecretStore). The schema structure and validation are identical and correct.

Signed-off-by: Richard Boisvert <rboisvert@devolutions.net>
@rbstp
Copy link
Copy Markdown
Contributor Author

rbstp commented Jan 9, 2026

for the nitpicks

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: 0

🧹 Nitpick comments (4)
docs/snippets/dvls-external-secret.yaml (2)

1-23: Clarify “Vault entry” wording to avoid confusion with HashiCorp Vault (and optionally document key format).
Right now “Vault entry” can be misread as the Vault provider rather than DVLS vault; this snippet is otherwise well-formed and appropriately uses external-secrets.io/v1. (Based on learnings, v1-only is correct.)

Proposed doc-only tweak
 ---
-# Fetch a single property from a credential entry
+# Fetch a single property from a DVLS credential entry (stored in a DVLS vault)
 apiVersion: external-secrets.io/v1
 kind: ExternalSecret
@@
   data:
     - secretKey: username
       remoteRef:
-        key: 'vault-uuid/entry-uuid'
+        key: 'dvls-vault-uuid/credential-entry-uuid'
         property: username
     - secretKey: password
       remoteRef:
-        key: 'vault-uuid/entry-uuid'
+        key: 'dvls-vault-uuid/credential-entry-uuid'
         property: password

24-40: dataFrom.extract example looks correct; consider aligning placeholder naming with DVLS terminology.

Optional placeholder alignment
 ---
-# Fetch all fields from a credential entry
+# Fetch all fields from a DVLS credential entry
 apiVersion: external-secrets.io/v1
 kind: ExternalSecret
@@
   dataFrom:
     - extract:
-        key: 'vault-uuid/entry-uuid'
+        key: 'dvls-vault-uuid/credential-entry-uuid'
config/crds/bases/external-secrets.io_secretstores.yaml (1)

2032-2037: Clarify insecure semantics (HTTP vs TLS verify).
The description says “plain HTTP”, but many users read insecure as “skip TLS verification”. Consider tightening the wording to explicitly say it permits http:// in serverUrl (or, if the implementation actually skips TLS verify, update the description accordingly).

providers/v1/dvls/client.go (1)

161-168: Consider idempotent delete behavior.

If the entry doesn't exist, the DVLS SDK error will propagate to the caller. Some providers handle this case by returning nil for idempotent delete semantics (i.e., deleting a non-existent secret is not an error).

Verify if PushSecret deletion reconciliation expects idempotent behavior. If so, consider handling not-found errors:

♻️ Optional: Handle not-found for idempotent delete
 func (c *Client) DeleteSecret(ctx context.Context, ref esv1.PushSecretRemoteRef) error {
 	vaultID, entryID, err := c.parseSecretRef(ref.GetRemoteKey())
 	if err != nil {
 		return err
 	}
-	return c.dvls.DeleteByID(ctx, vaultID, entryID)
+	err = c.dvls.DeleteByID(ctx, vaultID, entryID)
+	if isNotFoundError(err) {
+		return nil
+	}
+	return err
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef7b77e and 540da80.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • providers/v1/dvls/go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • apis/externalsecrets/v1/secretstore_types.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • docs/introduction/stability-support.md
  • docs/provider/devolutions-server.md
  • docs/snippets/dvls-external-secret.yaml
  • docs/snippets/dvls-push-secret.yaml
  • docs/snippets/dvls-secret-store.yaml
  • go.mod
  • hack/api-docs/mkdocs.yml
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • providers/v1/dvls/auth_test.go
  • providers/v1/dvls/client.go
  • providers/v1/dvls/client_test.go
  • providers/v1/dvls/go.mod
  • providers/v1/dvls/provider.go
  • providers/v1/dvls/provider_test.go
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
✅ Files skipped from review due to trivial changes (1)
  • docs/provider/devolutions-server.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • apis/externalsecrets/v1/secretstore_types.go
  • docs/introduction/stability-support.md
  • docs/snippets/dvls-push-secret.yaml
  • providers/v1/dvls/auth_test.go
  • tests/snapshot/clustersecretstore-v1.yaml
  • apis/externalsecrets/v1/secretstore_dvls_types.go
  • docs/snippets/dvls-secret-store.yaml
  • providers/v1/dvls/provider_test.go
  • hack/api-docs/mkdocs.yml
  • tests/snapshot/secretstore-v1.yaml
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, new providers are added to the GA v1 schema only; v1beta1 is maintained for backward compatibility and does not receive new features (e.g., DVLS should be v1-only).
📚 Learning: 2026-01-09T19:14:42.737Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, new providers are added to the GA v1 schema only; v1beta1 is maintained for backward compatibility and does not receive new features (e.g., DVLS should be v1-only).

Applied to files:

  • providers/v1/dvls/go.mod
  • pkg/register/dvls.go
  • providers/v1/dvls/auth.go
  • deploy/crds/bundle.yaml
  • providers/v1/dvls/provider.go
  • go.mod
  • providers/v1/dvls/client_test.go
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • docs/api/spec.md
  • docs/snippets/dvls-external-secret.yaml
  • providers/v1/dvls/client.go
📚 Learning: 2026-01-09T19:17:37.361Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:17:37.361Z
Learning: In this repo’s CRDs, avoid adding provider-specific schema validations (e.g., `format: uri` or CEL rules) when other providers don’t use them; prioritize consistency across providers.

Applied to files:

  • providers/v1/dvls/auth.go
  • deploy/crds/bundle.yaml
📚 Learning: 2026-01-09T19:14:42.737Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, add new providers only to the GA v1 schema. Do not introduce new features to v1beta1, which exists only for backward compatibility. Ensure that any provider-related changes (e.g., DVLS) are implemented in v1-only definitions to avoid breaking older installations. This guideline applies to YAML files under config/crds/bases that define CRD schemas for external-secrets providers.

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
📚 Learning: 2026-01-09T19:17:37.361Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:17:37.361Z
Learning: In CRD YAML files under config/crds, avoid provider-specific schema validations (e.g., format: uri or CEL rules) when other providers don’t use them. Aim for consistent, provider-agnostic validations across all CRDs to ensure uniform behavior and compatibility across providers. If a field requires provider-specific constraints, document and implement them in a separate, clearly scoped area or add conditional validation only where it’s universally applicable.

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
🧬 Code graph analysis (6)
pkg/register/dvls.go (1)
providers/v1/dvls/provider.go (3)
  • NewProvider (134-136)
  • ProviderSpec (139-143)
  • MaintenanceStatus (146-148)
providers/v1/dvls/auth.go (3)
providers/v1/dvls/client.go (2)
  • Client (39-41)
  • NewClient (66-68)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-38)
providers/v1/vault/util/vault.go (1)
  • Auth (31-33)
providers/v1/dvls/provider.go (4)
providers/v1/dvls/client.go (2)
  • NewClient (66-68)
  • Client (39-41)
providers/v1/dvls/auth.go (1)
  • NewDVLSClient (32-53)
apis/externalsecrets/v1/secretstore_dvls_types.go (2)
  • DVLSAuthSecretRef (48-56)
  • DVLSProvider (24-38)
apis/externalsecrets/v1/provider_schema_maintenance.go (1)
  • MaintenanceStatusMaintained (29-29)
providers/v1/dvls/client_test.go (3)
providers/v1/dvls/client.go (2)
  • Client (39-41)
  • NewClient (66-68)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
apis/externalsecrets/v1/zz_generated.deepcopy.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (3)
  • DVLSAuth (41-45)
  • DVLSAuthSecretRef (48-56)
  • DVLSProvider (24-38)
providers/v1/dvls/client.go (3)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/keepersecurity/client.go (1)
  • Secret (105-111)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
🔇 Additional comments (40)
docs/snippets/dvls-external-secret.yaml (1)

41-59: Verify the “Access Code type” → property: password mapping matches DVLS provider semantics.
If DVLS uses a different field name (e.g., accessCode), this example will silently mislead users.

providers/v1/dvls/go.mod (4)

67-67: LGTM: golang.org/x/crypto updated as requested.

The dependency includes golang.org/x/crypto v0.46.0 as requested in PR feedback.


5-14: All direct dependencies are properly aligned with the base module.

Verification confirms that the Kubernetes modules (k8s.io/api, k8s.io/apiextensions-apiserver, k8s.io/apimachinery) all use v0.34.1, controller-runtime uses v0.22.3, and Devolutions/go-dvls v0.15.0 is the correct version. No version conflicts or compatibility issues detected.


3-3: Go version 1.25.5 is intentional and properly aligned.

The provider module correctly inherits the base go.mod's Go version directive (1.25.5). Both are aligned with no mismatches or conflicts detected. No action needed.


91-94: No action required—replace directives in non-root modules will not escape to consumers.

Go module resolution explicitly ignores replace directives in non-root modules when that module is consumed as a dependency. Only the root module's replaces are applied. In this monorepo, the root go.mod already correctly maps all provider paths, including github.com/external-secrets/external-secrets/providers/v1/dvls => ./providers/v1/dvls. The dvls provider is not published independently and is only used internally through the root module. The replaces in providers/v1/dvls/go.mod are safe and follow the same pattern as all other providers in the repository—they won't cause downstream resolution issues.

config/crds/bases/external-secrets.io_clustersecretstores.yaml (2)

2032-2037: The dvls.insecure description is accurate and matches the implementation.

The insecure flag is used solely to permit plain HTTP URLs (as confirmed in providers/v1/dvls/provider.go:77). It is validated at store initialization and does not affect TLS certificate verification—the go-dvls client is initialized without any TLS configuration overrides regardless of the flag's value. The current description correctly reflects this behavior.

Regarding the optional default: false suggestion: Kubernetes boolean fields default to false implicitly, so adding an explicit default is not necessary but would serve as documentation.


1958-2044: No action needed—DVLS is correctly implemented as v1-only.

Verification confirms DVLS is properly added only to the v1 schema (line 1958) and does not appear in v1beta1 (which starts at line 6172). The schema follows project conventions: no provider-specific validators like format: uri are added, and the secret references use standard patterns consistent with other providers.

apis/externalsecrets/v1/zz_generated.deepcopy.go (2)

1263-1310: LGTM - Autogenerated deepcopy functions follow established patterns.

The DVLS type deepcopy implementations (DVLSAuth, DVLSAuthSecretRef, DVLSProvider) are correctly generated by controller-gen and follow the same patterns used by other providers in this file.


3556-3560: LGTM - DVLS field handling in SecretStoreProvider is consistent.

The DVLS nil-check and deepcopy pattern matches all other provider fields in SecretStoreProvider.DeepCopyInto.

pkg/register/dvls.go (1)

1-30: LGTM - Provider registration follows established patterns.

The build tag (dvls || all_providers), imports, and registration call in init() are consistent with other provider registration files in this codebase.

providers/v1/dvls/client_test.go (4)

34-79: LGTM - Well-structured mock with proper error simulation.

The mockCredentialClient provides good test isolation with configurable error injection and operation tracking (lastUpdated, lastDeleted). The 404 handling correctly simulates DVLS API behavior.


100-134: LGTM - Thorough parseSecretRef testing.

Good coverage of edge cases: valid format, missing separator, empty vault ID, empty entry ID, and whitespace handling.


159-326: LGTM - Comprehensive credential subtype testing.

Tests cover all supported DVLS credential subtypes (Default, AccessCode, ApiKey, AzureServicePrincipal, ConnectionString, PrivateKey), plus unsupported subtype error handling and partial data scenarios. The assertions verify both the presence of expected fields and the absence of empty fields.


387-454: LGTM - PushSecret tests cover key scenarios.

Tests validate updating Default and AccessCode subtypes, not-found handling, and unsupported subtype rejection. Good verification of the actual data written to the mock.

providers/v1/dvls/auth.go (2)

37-45: LGTM - Secret resolution follows established patterns.

The use of resolvers.SecretKeyRef for fetching appId and appSecret is consistent with how other providers resolve authentication credentials. Error wrapping provides good context for debugging.


31-53: The Insecure field is properly validated and being used; it does not need to be passed to dvls.NewClient.

The Insecure field is validated in provider.go's ValidateStore method (line 77–78), which enforces that HTTP URLs require insecure: true to be set explicitly. The dvls.NewClient call in auth.go correctly uses only the scheme from provider.ServerURL to determine the protocol (HTTP or HTTPS); the go-dvls library's NewClient signature accepts only username, password, and URL and has no parameter for insecure mode. The current implementation is correct.

go.mod (3)

35-35: LGTM - Replace directive follows established pattern.

The local replace directive for providers/v1/dvls is consistent with how other provider modules are structured in this monorepo.


101-101: LGTM - Crypto dependency update as requested.

The golang.org/x/crypto update to v0.46.0 addresses the reviewer request from Skarlso in the PR comments.


198-198: No action needed: go-dvls v0.15.0 is the latest stable version (released January 9, 2026).

config/crds/bases/external-secrets.io_secretstores.yaml (1)

1958-2044: DVLS provider schema addition looks correct and follows repo conventions.

DVLS is properly added only to the v1 schema (line 1958), keeping v1beta1 untouched for backward compatibility. The schema uses standard SecretKeySelector patterns without custom format validations (consistent with other providers like Chef), and the required fields (auth, serverUrl) align with typical provider configurations.

providers/v1/dvls/provider.go (5)

37-52: LGTM! Clean client instantiation flow.

The NewClient method correctly extracts the provider configuration, retrieves the store kind for cross-namespace validation, and properly delegates to NewDVLSClient and NewClient. Error handling and context propagation are appropriate.


54-87: Excellent validation with strong security posture.

The ValidateStore method provides comprehensive validation:

  • ServerURL presence and format checks
  • URL scheme/host validation
  • Security enforcement requiring explicit insecure: true for http URLs (lines 77-79) prevents accidental plaintext connections
  • Proper authentication reference validation

The validation logic is thorough and follows security best practices.


89-92: LGTM! Correct capability declaration.

SecretStoreReadWrite is appropriate for a provider that supports get, push, delete, and exists operations as described in the PR objectives.


94-122: Well-structured validation helpers.

The two-layer validation approach is well-designed:

  • requireSecretSelector provides clear, immediate feedback for missing required fields
  • esutils.ValidateSecretSelector handles store-specific validation rules (e.g., cross-namespace restrictions)

This pattern ensures both basic requirements and store-specific constraints are validated with appropriate error messages.


124-148: LGTM! Standard provider registration pattern.

The factory and helper functions follow the established patterns for provider registration:

  • getDVLSProvider performs proper nil checks at each level
  • NewProvider, ProviderSpec, and MaintenanceStatus align with external-secrets provider conventions
deploy/crds/bundle.yaml (2)

3913-3993: LGTM - DVLS provider schema is well-structured.

The schema follows existing provider patterns with proper required field declarations and clear descriptions. The insecure flag appropriately warns users about security implications. Based on learnings, this correctly avoids provider-specific validations (like format: uri on serverUrl) to maintain consistency with other providers.


15660-15740: LGTM - Schema is consistent with the SecretStore definition.

The ClusterSecretStore DVLS schema mirrors the SecretStore definition exactly, ensuring consistent behavior across namespaced and cluster-scoped stores.

providers/v1/dvls/client.go (13)

32-41: LGTM!

Good use of compile-time interface compliance check and clean struct design with dependency injection for the credential client.


49-63: LGTM!

Clean adapter pattern wrapping the DVLS SDK's EntryCredentialService, enabling testability through the credentialClient interface.


70-101: LGTM!

The implementation correctly handles the not-found case by returning esv1.NoSecretErr, and the default property fallback to "password" is well-documented.


103-119: LGTM!

Consistent implementation pattern with GetSecret, properly handling not-found errors.


121-124: LGTM!

Returning errNotImplemented is acceptable when the provider doesn't support find/list operations.


126-159: LGTM!

Good implementation with clear requirements: entry must exist before pushing, and only the credential/password field is updated. Error messages are informative.


170-185: LGTM!

Clean existence check implementation with proper not-found handling.


187-193: LGTM!

Basic nil-check validation is acceptable. Connectivity will be verified on first operation.


195-198: LGTM!

No-op close is appropriate for HTTP-based clients that don't hold persistent connections.


200-219: LGTM!

Good parsing logic with SplitN(key, "/", 2) allowing entry IDs to contain "/" while vault IDs cannot. Proper whitespace trimming and empty validation.


221-234: LGTM!

Efficient conversion with pre-allocated map capacity.


236-255: LGTM!

Comprehensive validation with clear, contextual error messages.


257-268: LGTM!

Robust not-found detection with dual-layer check: SDK-specific IsNotFound and HTTP 404 status fallback.

@rbstp
Copy link
Copy Markdown
Contributor Author

rbstp commented Jan 9, 2026

I have a couple of questions here and there. :) Nothing big all in all. Well done.

Thanks @Skarlso! I should have addressed all the points and the new ones from code rabbit.

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Jan 10, 2026

/ok-to-test sha=a6dc256ca0c60ceba96b96ed4daa875d2732e451

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: 0

🧹 Nitpick comments (1)
config/crds/bases/external-secrets.io_secretstores.yaml (1)

1958-2044: DVLS provider schema is sound; optional doc clarification for insecure field aligns with patterns in other providers

The DVLS schema correctly follows existing provider conventions (SecretKeySelector-style refs, required auth + serverUrl), avoids provider-specific validations, and is properly added to v1 only without appearing in v1beta1. No duplicate entries exist.

The suggested clarification for insecure is valid and consistent with how other providers document conditional HTTPS behavior. Adding "If serverUrl uses HTTPS, this field has no effect." would help users understand when this setting applies.

Optional doc-only tweak
                   insecure:
                     description: |-
                       Insecure allows connecting to DVLS over plain HTTP.
+                      If serverUrl uses HTTPS, this field has no effect.
                       This is NOT RECOMMENDED for production use.
                       Set to true only if you understand the security implications.
                     type: boolean
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 540da80 and a6dc256.

📒 Files selected for processing (6)
  • apis/externalsecrets/v1/secretstore_types.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • hack/api-docs/mkdocs.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • hack/api-docs/mkdocs.yml
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, new providers are added to the GA v1 schema only; v1beta1 is maintained for backward compatibility and does not receive new features (e.g., DVLS should be v1-only).
📚 Learning: 2026-01-09T19:14:42.737Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, new providers are added to the GA v1 schema only; v1beta1 is maintained for backward compatibility and does not receive new features (e.g., DVLS should be v1-only).

Applied to files:

  • apis/externalsecrets/v1/secretstore_types.go
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
📚 Learning: 2026-01-09T19:17:37.361Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:17:37.361Z
Learning: In this repo’s CRDs, avoid adding provider-specific schema validations (e.g., `format: uri` or CEL rules) when other providers don’t use them; prioritize consistency across providers.

Applied to files:

  • deploy/crds/bundle.yaml
📚 Learning: 2026-01-10T10:58:07.223Z
Learnt from: Skarlso
Repo: external-secrets/external-secrets PR: 5811
File: deploy/crds/bundle.yaml:5686-5707
Timestamp: 2026-01-10T10:58:07.223Z
Learning: In Kubernetes CRD YAML (e.g., deploy/crds/bundle.yaml), do not apply minLength constraints to duration fields (strings like "5m", "1h", "30s"). Duration values are semantic time representations; rely on proper duration patterns or server-side validation (e.g., OpenAPI schema with a proper format or a custom validator) rather than generic string length constraints.

Applied to files:

  • deploy/crds/bundle.yaml
📚 Learning: 2026-01-09T19:14:42.737Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, add new providers only to the GA v1 schema. Do not introduce new features to v1beta1, which exists only for backward compatibility. Ensure that any provider-related changes (e.g., DVLS) are implemented in v1-only definitions to avoid breaking older installations. This guideline applies to YAML files under config/crds/bases that define CRD schemas for external-secrets providers.

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
📚 Learning: 2026-01-09T19:17:37.361Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:17:37.361Z
Learning: In CRD YAML files under config/crds, avoid provider-specific schema validations (e.g., format: uri or CEL rules) when other providers don’t use them. Aim for consistent, provider-agnostic validations across all CRDs to ensure uniform behavior and compatibility across providers. If a field requires provider-specific constraints, document and implement them in a separate, clearly scoped area or add conditional validation only where it’s universally applicable.

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
📚 Learning: 2026-01-10T10:41:30.240Z
Learnt from: Skarlso
Repo: external-secrets/external-secrets PR: 5811
File: config/crds/bases/external-secrets.io_clustersecretstores.yaml:3868-3887
Timestamp: 2026-01-10T10:41:30.240Z
Learning: In config/crds/bases/external-secrets.io_clustersecretstores.yaml: Make the CacheConfig block optional; allow cache: {} to enable caching with defaults. Do not require ttl. Use +kubebuilder:validation:MinLength=1 for ttl when provided and +kubebuilder:default="5m". For maxSize, use +kubebuilder:validation:Minimum=1 and default 100. Update docs wording to: "If omitted, caching is disabled (default)."

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
🧬 Code graph analysis (1)
apis/externalsecrets/v1/secretstore_types.go (1)
apis/externalsecrets/v1/secretstore_dvls_types.go (1)
  • DVLSProvider (24-38)
🔇 Additional comments (4)
apis/externalsecrets/v1/secretstore_types.go (1)

203-205: LGTM! Provider addition follows established patterns.

The DVLS provider field is correctly integrated into the SecretStoreProvider struct:

  • Properly placed in alphabetical order between Device42 and Infisical
  • Follows the consistent pattern used by all other providers (pointer type, json tag with omitempty, +optional marker)
  • Comment clearly describes the provider's purpose
config/crds/bases/external-secrets.io_clustersecretstores.yaml (1)

1958-2044: No changes required. DVLS is correctly scoped to the GA v1 schema only and does not appear in the deprecated v1beta1 schema, following repo conventions. The insecure field description accurately reflects its implementation: it controls whether plain HTTP connections are allowed to the DVLS instance.

deploy/crds/bundle.yaml (2)

3913-3993: LGTM! Well-structured DVLS provider schema.

The schema follows existing provider patterns in this repository:

  • Proper validation constraints on secret references (key, name, namespace) using standard Kubernetes patterns
  • Required fields are correctly marked at each nesting level
  • The insecure field includes an appropriate security warning
  • No provider-specific validations like format: uri on serverUrl, which is consistent with how other providers handle URLs

Based on learnings, this v1-only addition aligns with the project convention that new providers are added to the GA v1 schema only.


15660-15740: LGTM! ClusterSecretStore schema matches SecretStore.

This DVLS provider schema is identical to the SecretStore definition, which is the expected pattern for maintaining consistency between SecretStore and ClusterSecretStore CRDs.

@eso-service-account-app
Copy link
Copy Markdown
Contributor

@sonarqubecloud
Copy link
Copy Markdown

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

🤖 Fix all issues with AI agents
In @tests/__snapshot__/clustersecretstore-v1.yaml:
- Around line 300-312: The snapshot YAML sets dvls.insecure: true which disables
TLS verification; change the example/default to insecure: false to make the
configuration secure-by-default and align with other providers (e.g.,
senhasegura.ignoreSslCertificate: false); update the dvls → auth → secretRef
block in the cluster secret store snapshot so the insecure field is false and
regenerate the snapshot.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6dc256 and 4640543.

📒 Files selected for processing (7)
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
  • config/crds/bases/external-secrets.io_secretstores.yaml
  • deploy/crds/bundle.yaml
  • docs/api/spec.md
  • tests/__snapshot__/clustersecretstore-v1.yaml
  • tests/__snapshot__/secretstore-v1.yaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/snapshot/secretstore-v1.yaml
  • apis/externalsecrets/v1/zz_generated.deepcopy.go
  • config/crds/bases/external-secrets.io_secretstores.yaml
👮 Files not reviewed due to content moderation or server errors (1)
  • docs/api/spec.md
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, new providers are added to the GA v1 schema only; v1beta1 is maintained for backward compatibility and does not receive new features (e.g., DVLS should be v1-only).
📚 Learning: 2026-01-09T19:14:42.737Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, new providers are added to the GA v1 schema only; v1beta1 is maintained for backward compatibility and does not receive new features (e.g., DVLS should be v1-only).

Applied to files:

  • tests/__snapshot__/clustersecretstore-v1.yaml
  • docs/api/spec.md
  • deploy/crds/bundle.yaml
📚 Learning: 2026-01-09T19:17:37.361Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:17:37.361Z
Learning: In this repo’s CRDs, avoid adding provider-specific schema validations (e.g., `format: uri` or CEL rules) when other providers don’t use them; prioritize consistency across providers.

Applied to files:

  • deploy/crds/bundle.yaml
📚 Learning: 2026-01-10T10:58:07.223Z
Learnt from: Skarlso
Repo: external-secrets/external-secrets PR: 5811
File: deploy/crds/bundle.yaml:5686-5707
Timestamp: 2026-01-10T10:58:07.223Z
Learning: In Kubernetes CRD YAML (e.g., deploy/crds/bundle.yaml), do not apply minLength constraints to duration fields (strings like "5m", "1h", "30s"). Duration values are semantic time representations; rely on proper duration patterns or server-side validation (e.g., OpenAPI schema with a proper format or a custom validator) rather than generic string length constraints.

Applied to files:

  • deploy/crds/bundle.yaml
📚 Learning: 2026-01-09T19:14:42.737Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, add new providers only to the GA v1 schema. Do not introduce new features to v1beta1, which exists only for backward compatibility. Ensure that any provider-related changes (e.g., DVLS) are implemented in v1-only definitions to avoid breaking older installations. This guideline applies to YAML files under config/crds/bases that define CRD schemas for external-secrets providers.

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
📚 Learning: 2026-01-09T19:17:37.361Z
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:17:37.361Z
Learning: In CRD YAML files under config/crds, avoid provider-specific schema validations (e.g., format: uri or CEL rules) when other providers don’t use them. Aim for consistent, provider-agnostic validations across all CRDs to ensure uniform behavior and compatibility across providers. If a field requires provider-specific constraints, document and implement them in a separate, clearly scoped area or add conditional validation only where it’s universally applicable.

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
📚 Learning: 2026-01-10T10:41:30.240Z
Learnt from: Skarlso
Repo: external-secrets/external-secrets PR: 5811
File: config/crds/bases/external-secrets.io_clustersecretstores.yaml:3868-3887
Timestamp: 2026-01-10T10:41:30.240Z
Learning: In config/crds/bases/external-secrets.io_clustersecretstores.yaml: Make the CacheConfig block optional; allow cache: {} to enable caching with defaults. Do not require ttl. Use +kubebuilder:validation:MinLength=1 for ttl when provided and +kubebuilder:default="5m". For maxSize, use +kubebuilder:validation:Minimum=1 and default 100. Update docs wording to: "If omitted, caching is disabled (default)."

Applied to files:

  • config/crds/bases/external-secrets.io_clustersecretstores.yaml
🔇 Additional comments (4)
config/crds/bases/external-secrets.io_clustersecretstores.yaml (2)

1958-2044: No issues found. DVLS provider is correctly added only to the GA v1 schema and does not appear in the deprecated v1beta1 schema (which begins at line 6195). The schema follows repo conventions by avoiding provider-specific validations and maintaining consistency with other providers.


2032-2041: Remove - field semantics are unambiguous.

The Insecure field is specifically for allowing plain HTTP scheme URLs (http:// protocol), not for skipping TLS verification. The implementation validates the URL scheme in provider.go with the check if parsedURL.Scheme == "http" && !dvlsProvider.Insecure, and the error message confirms: "http URLs require 'insecure: true'". The description "connecting to DVLS over plain HTTP" accurately reflects this behavior. No TLS verification skip logic is present in the DVLS provider code.

Likely an incorrect or invalid review comment.

deploy/crds/bundle.yaml (2)

3913-3993: LGTM: DVLS provider schema follows Kubernetes conventions.

The DVLS provider schema is well-structured with:

  • Appropriate required fields (auth, serverUrl)
  • Standard Kubernetes secret reference patterns for authentication
  • Secure defaults (insecure boolean defaults to false)
  • Clear security warnings for the insecure field

Also applies to: 15682-15762


3913-3993: DVLS is correctly scoped to v1 schemas only.

Verification confirms that DVLS is added to v1 schemas only (lines 35–6194 in both external-secrets.io_secretstores.yaml and external-secrets.io_clustersecretstores.yaml), with no presence in v1beta1 schemas (lines 6195+), which aligns with the requirement that new providers are GA v1-only for backward compatibility.

Comment on lines +300 to +312
dvls:
auth:
secretRef:
appId:
key: string
name: string
namespace: string
appSecret:
key: string
name: string
namespace: string
insecure: true
serverUrl: string
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Jan 10, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Set insecure to false to promote secure-by-default configuration.

The snapshot shows insecure: true (Line 311), which disables TLS certificate verification. Since snapshot files often serve as templates or references for users, this example could lead to insecure deployments.

Compare with similar fields in other providers: senhasegura.ignoreSslCertificate: false (Line 709).

🔒 Proposed fix
     dvls:
       auth:
         secretRef:
           appId:
             key: string
             name: string
             namespace: string
           appSecret:
             key: string
             name: string
             namespace: string
-      insecure: true
+      insecure: false
       serverUrl: string
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dvls:
auth:
secretRef:
appId:
key: string
name: string
namespace: string
appSecret:
key: string
name: string
namespace: string
insecure: true
serverUrl: string
dvls:
auth:
secretRef:
appId:
key: string
name: string
namespace: string
appSecret:
key: string
name: string
namespace: string
insecure: false
serverUrl: string
🤖 Prompt for AI Agents
In @tests/__snapshot__/clustersecretstore-v1.yaml around lines 300 - 312, The
snapshot YAML sets dvls.insecure: true which disables TLS verification; change
the example/default to insecure: false to make the configuration
secure-by-default and align with other providers (e.g.,
senhasegura.ignoreSslCertificate: false); update the dvls → auth → secretRef
block in the cluster secret store snapshot so the insecure field is false and
regenerate the snapshot.

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.

That's not what that field is for buddy. It's just an extra measure if people define http making sure they don't leave the 's' off by accident.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Skarlso, thank you for the clarification! I misunderstood the purpose of the insecure field. You're absolutely right—it's a safety mechanism that requires explicit acknowledgment when using HTTP instead of HTTPS, preventing accidental omission of the 's'. I appreciate you explaining the design intent.


✏️ Learnings added
Learnt from: Skarlso
Repo: external-secrets/external-secrets PR: 5712
File: tests/__snapshot__/clustersecretstore-v1.yaml:300-312
Timestamp: 2026-01-10T16:35:03.175Z
Learning: In the external-secrets DVLS provider (providers/v1/dvls), the `insecure` field is a safety mechanism that requires users to explicitly set `insecure: true` when using HTTP URLs (without TLS). This prevents accidentally using an insecure connection when HTTPS was intended. The validation enforces that HTTP URLs must have the insecure flag set to true; otherwise, an error is returned.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: rbstp
Repo: external-secrets/external-secrets PR: 5712
File: config/crds/bases/external-secrets.io_secretstores.yaml:1958-2044
Timestamp: 2026-01-09T19:14:42.737Z
Learning: In external-secrets CRDs, new providers are added to the GA v1 schema only; v1beta1 is maintained for backward compatibility and does not receive new features (e.g., DVLS should be v1-only).

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Jan 10, 2026

I did not modify the CODEOWNERS.md file but I am uncertain if it is needed. It was modified in the ngrok PR, and not in the Barbican one.

Oh sorry, I haven't seen this question. That's interesting. :D I think in case of Barbican it was missed? Normally we should modify it, but we also need to create the team for it in GitHub. :D So I guess we'll create a followup PR to add everyone who's missing. :)

@Skarlso Skarlso merged commit bdfa94e into external-secrets:main Jan 10, 2026
35 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 4, 2026
5 tasks
dsp0x4 pushed a commit to dsp0x4/external-secrets that referenced this pull request Mar 22, 2026
…rets#5712)

Co-authored-by: Gergely Bräutigam <skarlso777@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/provider kind/dependency dependabot and upgrades kind/documentation Categorizes issue or PR as related to documentation. kind/feature Categorizes issue or PR as related to a new feature. size/l

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Devolutions Server Provider

2 participants