From 1ec169627777bd92330fd59de2d97c6f9119640a Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Mon, 10 Aug 2026 13:21:59 +0200 Subject: [PATCH 1/4] fix: Hard fail if the secret is not a string --- bundle/config/mutator/validate_secret_value_is_variable.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bundle/config/mutator/validate_secret_value_is_variable.go b/bundle/config/mutator/validate_secret_value_is_variable.go index b077d9430e5..cd1f8a0ab3e 100644 --- a/bundle/config/mutator/validate_secret_value_is_variable.go +++ b/bundle/config/mutator/validate_secret_value_is_variable.go @@ -36,6 +36,13 @@ func (v *validateSecretValueIsVariable) Apply(ctx context.Context, b *bundle.Bun valueStr, ok := val.AsString() if !ok { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Secret value must be a string", + Detail: fmt.Sprintf(`The secret value for "%s" must be a string.`, key), + Locations: val.Locations(), + Paths: []dyn.Path{p}, + }) continue } From 8e099dabfc1fb9777bfdf21831efd14412f5cedb Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Mon, 10 Aug 2026 13:54:27 +0200 Subject: [PATCH 2/4] fix + test --- .../validate-secret-is-string/databricks.yml | 13 +++++++++++++ .../validate-secret-is-string/out.test.toml | 2 ++ .../secrets/validate-secret-is-string/output.txt | 14 ++++++++++++++ .../secrets/validate-secret-is-string/script | 2 ++ .../secrets/validate-secret-is-string/test.toml | 7 +++++++ .../mutator/validate_secret_value_is_variable.go | 15 +++++++++++---- 6 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 acceptance/bundle/resources/secrets/validate-secret-is-string/databricks.yml create mode 100644 acceptance/bundle/resources/secrets/validate-secret-is-string/out.test.toml create mode 100644 acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt create mode 100755 acceptance/bundle/resources/secrets/validate-secret-is-string/script create mode 100644 acceptance/bundle/resources/secrets/validate-secret-is-string/test.toml diff --git a/acceptance/bundle/resources/secrets/validate-secret-is-string/databricks.yml b/acceptance/bundle/resources/secrets/validate-secret-is-string/databricks.yml new file mode 100644 index 00000000000..c312e959fbc --- /dev/null +++ b/acceptance/bundle/resources/secrets/validate-secret-is-string/databricks.yml @@ -0,0 +1,13 @@ +bundle: + name: test-bundle + +resources: + secrets: + secret1: + catalog_name: main + schema_name: default + name: test_secret + value: + - hello + - world + comment: "This should fail validation" diff --git a/acceptance/bundle/resources/secrets/validate-secret-is-string/out.test.toml b/acceptance/bundle/resources/secrets/validate-secret-is-string/out.test.toml new file mode 100644 index 00000000000..0938e678987 --- /dev/null +++ b/acceptance/bundle/resources/secrets/validate-secret-is-string/out.test.toml @@ -0,0 +1,2 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt b/acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt new file mode 100644 index 00000000000..1366ee9c85d --- /dev/null +++ b/acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt @@ -0,0 +1,14 @@ + +=== Deploy should fail due to plain text secret value +>>> [CLI] bundle deploy +Warning: expected string, found sequence + at resources.secrets.secret1.value + in databricks.yml:11:9 + +Error: Secret value must be a non-empty string + at resources.secrets.secret1.value + +The secret value for "secret1" must be a non-empty string. + + +Exit code: 1 diff --git a/acceptance/bundle/resources/secrets/validate-secret-is-string/script b/acceptance/bundle/resources/secrets/validate-secret-is-string/script new file mode 100755 index 00000000000..cc724cd5692 --- /dev/null +++ b/acceptance/bundle/resources/secrets/validate-secret-is-string/script @@ -0,0 +1,2 @@ +title "Deploy should fail due to plain text secret value" +trace $CLI bundle deploy diff --git a/acceptance/bundle/resources/secrets/validate-secret-is-string/test.toml b/acceptance/bundle/resources/secrets/validate-secret-is-string/test.toml new file mode 100644 index 00000000000..dcbf079204f --- /dev/null +++ b/acceptance/bundle/resources/secrets/validate-secret-is-string/test.toml @@ -0,0 +1,7 @@ +Cloud = false +RecordRequests = false + +Ignore = [".databricks"] + +[EnvMatrix] +DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/bundle/config/mutator/validate_secret_value_is_variable.go b/bundle/config/mutator/validate_secret_value_is_variable.go index cd1f8a0ab3e..31bac5f14df 100644 --- a/bundle/config/mutator/validate_secret_value_is_variable.go +++ b/bundle/config/mutator/validate_secret_value_is_variable.go @@ -28,18 +28,25 @@ func (v *validateSecretValueIsVariable) Apply(ctx context.Context, b *bundle.Bun p := dyn.NewPath(dyn.Key("resources"), dyn.Key("secrets"), dyn.Key(key), dyn.Key("value")) val, err := dyn.GetByPath(b.Config.Value(), p) if dyn.IsNoSuchKeyError(err) { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Secret value must be a non-empty string", + Detail: fmt.Sprintf(`The secret value for "%s" must be a non-empty string.`, key), + Locations: val.Locations(), + Paths: []dyn.Path{p}, + }) continue } if err != nil { return diag.FromErr(err) } - valueStr, ok := val.AsString() - if !ok { + valueStr := val.MustString() + if valueStr == "" { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, - Summary: "Secret value must be a string", - Detail: fmt.Sprintf(`The secret value for "%s" must be a string.`, key), + Summary: "Secret value must be a non-empty string", + Detail: fmt.Sprintf(`The secret value for "%s" must be a non-empty string.`, key), Locations: val.Locations(), Paths: []dyn.Path{p}, }) From ac36b76ab9b48e7851a7c2ee194c9d1bba1fbb58 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Mon, 10 Aug 2026 14:02:13 +0200 Subject: [PATCH 3/4] fix --- .../secrets/validate-secret-is-string/output.txt | 4 ++-- .../mutator/validate_secret_value_is_variable.go | 14 ++------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt b/acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt index 1366ee9c85d..07cfd2978b5 100644 --- a/acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt +++ b/acceptance/bundle/resources/secrets/validate-secret-is-string/output.txt @@ -5,10 +5,10 @@ Warning: expected string, found sequence at resources.secrets.secret1.value in databricks.yml:11:9 -Error: Secret value must be a non-empty string +Error: Secret value must be a string at resources.secrets.secret1.value -The secret value for "secret1" must be a non-empty string. +The secret value for "secret1" must be a string. Exit code: 1 diff --git a/bundle/config/mutator/validate_secret_value_is_variable.go b/bundle/config/mutator/validate_secret_value_is_variable.go index 31bac5f14df..7c069ffad3f 100644 --- a/bundle/config/mutator/validate_secret_value_is_variable.go +++ b/bundle/config/mutator/validate_secret_value_is_variable.go @@ -30,8 +30,8 @@ func (v *validateSecretValueIsVariable) Apply(ctx context.Context, b *bundle.Bun if dyn.IsNoSuchKeyError(err) { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, - Summary: "Secret value must be a non-empty string", - Detail: fmt.Sprintf(`The secret value for "%s" must be a non-empty string.`, key), + Summary: "Secret value must be a string", + Detail: fmt.Sprintf(`The secret value for "%s" must be a string.`, key), Locations: val.Locations(), Paths: []dyn.Path{p}, }) @@ -42,16 +42,6 @@ func (v *validateSecretValueIsVariable) Apply(ctx context.Context, b *bundle.Bun } valueStr := val.MustString() - if valueStr == "" { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Secret value must be a non-empty string", - Detail: fmt.Sprintf(`The secret value for "%s" must be a non-empty string.`, key), - Locations: val.Locations(), - Paths: []dyn.Path{p}, - }) - continue - } // Value must be a variable reference to prevent leaking secrets in config files if !dynvar.IsPureVariableReference(valueStr) { From 2057cac210395af19ad71bb9e1fa55d46541d259 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Wed, 12 Aug 2026 16:22:33 +0200 Subject: [PATCH 4/4] use as string --- .../mutator/validate_secret_value_is_variable.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bundle/config/mutator/validate_secret_value_is_variable.go b/bundle/config/mutator/validate_secret_value_is_variable.go index 7c069ffad3f..39b7822168a 100644 --- a/bundle/config/mutator/validate_secret_value_is_variable.go +++ b/bundle/config/mutator/validate_secret_value_is_variable.go @@ -41,7 +41,17 @@ func (v *validateSecretValueIsVariable) Apply(ctx context.Context, b *bundle.Bun return diag.FromErr(err) } - valueStr := val.MustString() + valueStr, ok := val.AsString() + if !ok { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Secret value must be a string", + Detail: fmt.Sprintf(`The secret value for "%s" must be a string.`, key), + Locations: val.Locations(), + Paths: []dyn.Path{p}, + }) + continue + } // Value must be a variable reference to prevent leaking secrets in config files if !dynvar.IsPureVariableReference(valueStr) {