package jdbc import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) func TestParseSqlServerMissingCredentials(t *testing.T) { tests := []struct { name string subname string shouldBeNil bool reason string }{ { name: "no password - should return nil (nothing to verify)", subname: "//examplehost.net;databaseName=QRG1;sendStringParametersAsUnicode=false", shouldBeNil: true, reason: "no password present", }, { name: "no host - should return nil (invalid connection)", subname: "//;password=secret123", shouldBeNil: true, reason: "no host present", }, { name: "no host and no password - should return nil", subname: "//;databaseName=QRG1", shouldBeNil: true, reason: "no host or password present", }, { name: "csm-1584-example", subname: "//examplehost.net;databaseName=QRG1;sendStringParametersAsUnicode=false;loginTimeout=4;applicationName=pdal-cat-hierarchy-loader-v1;", shouldBeNil: true, reason: "no password present", }, { name: "valid with both host and password - should succeed", subname: "//examplehost.net;password=secret123", shouldBeNil: false, }, { name: "valid with host:port and password - should succeed", subname: "//examplehost.net:1433;password=secret123", shouldBeNil: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := logContext.AddLogger(context.Background()) j, err := parseSqlServer(ctx, tt.subname) if tt.shouldBeNil { if j != nil { t.Errorf("parseSqlServer() expected nil (%s), but got connection: %v", tt.reason, j) } } else { if j == nil { t.Errorf("parseSqlServer() returned nil, expected valid connection. err = %v", err) } if err != nil { t.Errorf("parseSqlServer() unexpected error = %v", err) } } }) } } // This test demonstrates the username is ignored when parsing the JDBC URL. Instead the default username "sa" is always used. func TestParseSqlServerUserIgnoredBug2(t *testing.T) { tests := []struct { name string subname string // the part after "jdbc:sqlserver:" wantUsername string }{ { name: "user parameter specified", subname: "//localhost:1433;user=myuser;password=mypass", wantUsername: "myuser", }, { name: "user id parameter specified", subname: "//localhost:1433;user id=admin;password=secret", wantUsername: "admin", }, { name: "no user specified - should default to sa", subname: "//localhost:1433;password=mypass", wantUsername: "sa", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := logContext.AddLogger(context.Background()) j, err := parseSqlServer(ctx, tt.subname) if err != nil { t.Fatalf("parseSqlServer() error = %v", err) } sqlServerConn := j.(*SqlServerJDBC) if sqlServerConn.User != tt.wantUsername { t.Errorf("Connection string does not contain expected username '%s'\nGot: %s\nExpected to contain: %s", tt.wantUsername, sqlServerConn.User, tt.wantUsername) } }) } } func TestParseSqlServerWithJdbcAndOdbcBridgeString(t *testing.T) { subname := "//odbc:server=localhost;port=1433;database=testdb;password=testpassword" wantHost := "localhost" wantPort := "1433" wantPassword := "testpassword" wantDatabase := "testdb" ctx := logContext.AddLogger(context.Background()) j, err := parseSqlServer(ctx, subname) if err != nil { t.Fatalf("parseSqlServer() error = %v", err) } if j == nil { t.Fatalf("parseSqlServer() returned nil, expected valid connection.") } sqlServerConn, ok := j.(*SqlServerJDBC) if !ok { t.Fatalf("parseSqlServer() returned unexpected type %T, expected *SqlServerJDBC", j) } if sqlServerConn.Host != wantHost+":"+wantPort { t.Errorf("Host mismatch. Got: %s, Want: %s", sqlServerConn.Host, wantHost+":"+wantPort) } if sqlServerConn.Password != wantPassword { t.Errorf("Password mismatch. Got: %s, Want: %s", sqlServerConn.Password, wantPassword) } if sqlServerConn.Database != wantDatabase { t.Errorf("Database mismatch. Got: %s, Want: %s", sqlServerConn.Database, wantDatabase) } } func TestSQLServerHandler_ParseJDBCURL(t *testing.T) { tests := []struct { name string jdbcURL string wantHost string wantDB string wantUser string wantPass string wantErr bool }{ { name: "basic URL with semicolon params", jdbcURL: "jdbc:sqlserver://localhost:1433;database=testdb;user=sa;password=Pass123", wantHost: "localhost:1433", wantDB: "testdb", wantUser: "sa", wantPass: "Pass123", }, { name: "URL with default port and database", jdbcURL: "jdbc:sqlserver://dbhost;user=testuser;password=secret", wantHost: "dbhost:1433", wantDB: "master", wantUser: "testuser", wantPass: "secret", }, { name: "URL with port in host", jdbcURL: "jdbc:sqlserver://server.example.com:1434;databaseName=mydb;userId=admin;pwd=admin123", wantHost: "server.example.com:1434", wantDB: "mydb", wantUser: "admin", wantPass: "admin123", }, { name: "invalid URL - missing jdbc:sqlserver prefix", jdbcURL: "jdbc:mysql://localhost/db", wantErr: true, }, { name: "invalid URL - missing //", jdbcURL: "jdbc:sqlserver:localhost;database=test", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { jdbc, err := NewJDBC(logContext.Background(), tt.jdbcURL) if tt.wantErr { assert.Error(t, err) return } require.NoError(t, err) info := jdbc.GetConnectionInfo() assert.Equal(t, tt.wantHost, info.Host) assert.Equal(t, tt.wantDB, info.Database) assert.Equal(t, tt.wantUser, info.User) assert.Equal(t, tt.wantPass, info.Password) }) } } func TestSQLServerHandler_BuildNativeConnectionString(t *testing.T) { tests := []struct { name string info *ConnectionInfo wantUser string wantPass string wantHost string wantDB string }{ { name: "basic connection", info: &ConnectionInfo{ Host: "localhost", Database: "testdb", User: "sa", Password: "Pass123", }, wantUser: "sa", wantPass: "Pass123", wantHost: "localhost", wantDB: "testdb", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { jdbc := &SqlServerJDBC{ ConnectionInfo: *tt.info, } connStr := jdbc.BuildConnectionString() // SQL Server format: sqlserver://user:password@host:port?database=db&connection+timeout=10 assert.Contains(t, connStr, tt.wantUser) assert.Contains(t, connStr, tt.wantPass) assert.Contains(t, connStr, tt.wantHost) assert.Contains(t, connStr, "database="+tt.wantDB) }) } }