From 8461abdb8528d2a3b210838eb3d7d2903440dbb0 Mon Sep 17 00:00:00 2001 From: David Levy Date: Thu, 30 Apr 2026 17:17:17 -0500 Subject: [PATCH] fix: reject non-YAML extensions for --sqlconfig flag --- README.md | 9 +++++++++ internal/config/config.go | 14 ++++++++++--- internal/config/config_test.go | 37 ++++++++++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 576439da..1628a1d8 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,15 @@ sqlcmd config connection-strings sqlcmd config view ``` +#### Custom configuration files + +The `--sqlconfig` flag specifies a custom configuration file. The file must be YAML. Extensionless filenames are allowed; if the file has an extension, it must be `.yaml` or `.yml`: + +``` +sqlcmd config --sqlconfig ./myproject.yaml add-endpoint --name ep1434 --address localhost --port 1434 +sqlcmd config --sqlconfig ./myproject.yaml view +``` + ### Versions To see all version tags to choose from (2017, 2019, 2022 etc.), and install a specific version, run: diff --git a/internal/config/config.go b/internal/config/config.go index 1b24695c..56b20f8f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,13 +4,16 @@ package config import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + . "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig" "github.com/microsoft/go-sqlcmd/internal/io/file" "github.com/microsoft/go-sqlcmd/internal/io/folder" "github.com/microsoft/go-sqlcmd/internal/pal" - "os" - "path/filepath" - "testing" ) var config Sqlconfig @@ -24,6 +27,11 @@ func SetFileName(name string) { panic("name is empty") } + if ext := strings.ToLower(filepath.Ext(name)); ext != "" && ext != ".yaml" && ext != ".yml" { + checkErr(fmt.Errorf("configuration file %q has unsupported extension %q (must be .yaml or .yml)", name, ext)) + return + } + filename = name file.CreateEmptyIfNotExists(filename) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 7e2540c2..11955a63 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -4,15 +4,18 @@ package config import ( + "os" + "path/filepath" + "reflect" + "strings" + "testing" + . "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig" "github.com/microsoft/go-sqlcmd/internal/output" "github.com/microsoft/go-sqlcmd/internal/pal" "github.com/microsoft/go-sqlcmd/internal/secret" "github.com/stretchr/testify/assert" - "os" - "reflect" - "strings" - "testing" + "github.com/stretchr/testify/require" ) func TestConfig(t *testing.T) { @@ -380,6 +383,32 @@ func TestConfig_GetCurrentContextEndPointNotFoundPanic(t *testing.T) { }) } +func TestSetFileName_RejectsNonYAMLExtension(t *testing.T) { + var gotErr error + originalCallback := errorCallback + errorCallback = func(err error) { gotErr = err } + t.Cleanup(func() { errorCallback = originalCallback }) + + SetFileName("config.json") + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), ".json") + + gotErr = nil + SetFileName("config.toml") + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), ".toml") + + tempDir := t.TempDir() + + gotErr = nil + SetFileName(filepath.Join(tempDir, t.Name()+".yaml")) + assert.NoError(t, gotErr) + + gotErr = nil + SetFileName(filepath.Join(tempDir, t.Name()+".yml")) + assert.NoError(t, gotErr) +} + func TestConfig_DeleteContextThatDoesNotExist(t *testing.T) { assert.Panics(t, func() { contextOrdinal("does-not-exist")