Repository: snowflakedb/gosnowflake
Pull request #1146 (2024-05-27 14:08:39)

SNOW-1432112 verify / sanitize account name before creating JWT
Attempt to fix the issue caused by certain applications putting
* regioned (e.g. myaccount.eu-central-1)
* privatelink (e.g. myorg-myaccount.privatelink)

values in the `Account` field of the configuration, which could result in unsuccessful keypair authentication, as this value was relayed into the JWT unchanged and led to an invalid JWT

The change has been also manually tested by actually generating JWT and actually logging into live Snowflake account, whose names conform the pattern seen in the newly added part in auth_test.go

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/snowflakedb/gosnowflake/pull/1146?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=snowflakedb) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 75.44%. Comparing base [(`a0145ec`)](https://app.codecov.io/gh/snowflakedb/gosnowflake/commit/a0145ec5c912210edfe96257efc83d3929769fe2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=snowflakedb) to head [(`e567bf3`)](https://app.codecov.io/gh/snowflakedb/gosnowflake/commit/e567bf39aa98ad78bfae02eb07937884a20ca042?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=snowflakedb).
> Report is 1 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #1146   +/-   ##
=======================================
  Coverage   75.44%   75.44%           
=======================================
  Files          53       53           
  Lines       11272    11291   +19     
=======================================
+ Hits         8504     8519   +15     
- Misses       2768     2772    +4     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/snowflakedb/gosnowflake/pull/1146?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=snowflakedb).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=snowflakedb).


Patch:
diff --git a/auth.go b/auth.go
index ebd3d23..832eeb6 100644
--- a/auth.go
+++ b/auth.go
@@ -480,7 +480,7 @@ func prepareJWTToken(config *Config) (string, error) {
 	}
 	hash := sha256.Sum256(pubBytes)
 
-	accountName := strings.ToUpper(config.Account)
+	accountName := extractAccountName(config.Account)
 	userName := strings.ToUpper(config.User)
 
 	issueAtTime := time.Now().UTC()
diff --git a/dsn.go b/dsn.go
index aefcee2..5f61b5b 100644
--- a/dsn.go
+++ b/dsn.go
@@ -907,3 +907,11 @@ func parsePrivateKeyFromFile(path string) (*rsa.PrivateKey, error) {
 	}
 	return pk, nil
 }
+
+func extractAccountName(rawAccount string) string {
+	posDot := strings.Index(rawAccount, ".")
+	if posDot > 0 {
+		return strings.ToUpper(rawAccount[:posDot])
+	}
+	return strings.ToUpper(rawAccount)
+}


Test patch:
diff --git a/dsn_test.go b/dsn_test.go
index c9b032a..c3d2c40 100644
--- a/dsn_test.go
+++ b/dsn_test.go
@@ -1616,3 +1616,26 @@ func TestConfigValidateTmpDirPath(t *testing.T) {
 		t.Fatalf("Should fail on not existing TmpDirPath")
 	}
 }
+
+func TestExtractAccountName(t *testing.T) {
+	testcases := map[string]string{
+		"myaccount":                          "MYACCOUNT",
+		"myaccount.eu-central-1":             "MYACCOUNT",
+		"myaccount.eu-central-1.privatelink": "MYACCOUNT",
+		"myorg-myaccount":                    "MYORG-MYACCOUNT",
+		"myorg-myaccount.privatelink":        "MYORG-MYACCOUNT",
+		"myorg-my-account":                   "MYORG-MY-ACCOUNT",
+		"myorg-my-account.privatelink":       "MYORG-MY-ACCOUNT",
+		"myorg-my_account":                   "MYORG-MY_ACCOUNT",
+		"myorg-my_account.privatelink":       "MYORG-MY_ACCOUNT",
+	}
+
+	for account, expected := range testcases {
+		t.Run(account, func(t *testing.T) {
+			accountPart := extractAccountName(account)
+			if accountPart != expected {
+				t.Fatalf("extractAccountName returned unexpected response (%v), should be %v", accountPart, expected)
+			}
+		})
+	}
+}