From 8e06a029aa9b7f7830a72374b7a4151d8b9241fa Mon Sep 17 00:00:00 2001 From: t3pm14r3 Date: Fri, 30 Aug 2024 22:47:24 +0300 Subject: [PATCH 1/6] fix an int parsing bug in godotenv.Marshal --- godotenv.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/godotenv.go b/godotenv.go index 61b0ebb..e60d55b 100644 --- a/godotenv.go +++ b/godotenv.go @@ -19,8 +19,8 @@ import ( "io" "os" "os/exec" + "regexp" "sort" - "strconv" "strings" ) @@ -163,9 +163,10 @@ func Write(envMap map[string]string, filename string) error { // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. func Marshal(envMap map[string]string) (string, error) { lines := make([]string, 0, len(envMap)) + var isInt = regexp.MustCompile(`^[0-9]+$`).MatchString for k, v := range envMap { - if d, err := strconv.Atoi(v); err == nil { - lines = append(lines, fmt.Sprintf(`%s=%d`, k, d)) + if isInt(v) { + lines = append(lines, fmt.Sprintf(`%s=%s`, k, v)) } else { lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v))) } From de05e2d993e7740349d9b924b532f837c47f14ac Mon Sep 17 00:00:00 2001 From: t3pm14r3 Date: Mon, 2 Sep 2024 10:42:46 +0300 Subject: [PATCH 2/6] got rid of regex, added a check for negative numbers, added a test --- godotenv.go | 19 +++++++++++++++++-- godotenv_test.go | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/godotenv.go b/godotenv.go index e60d55b..c560be5 100644 --- a/godotenv.go +++ b/godotenv.go @@ -19,9 +19,9 @@ import ( "io" "os" "os/exec" - "regexp" "sort" "strings" + "unicode" ) const doubleQuoteSpecialChars = "\\\n\r\"!$`" @@ -159,11 +159,26 @@ func Write(envMap map[string]string, filename string) error { return file.Sync() } +func isInt(s string) bool { + if len(s) == 0 { + return false + } else if s[0] == '-' { + s = s[1:] + } + + for _, r := range s { + if !unicode.IsDigit(r) { + return false + } + } + + return true +} + // Marshal outputs the given environment as a dotenv-formatted environment file. // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. func Marshal(envMap map[string]string) (string, error) { lines := make([]string, 0, len(envMap)) - var isInt = regexp.MustCompile(`^[0-9]+$`).MatchString for k, v := range envMap { if isInt(v) { lines = append(lines, fmt.Sprintf(`%s=%s`, k, v)) diff --git a/godotenv_test.go b/godotenv_test.go index c6d7e54..f489615 100644 --- a/godotenv_test.go +++ b/godotenv_test.go @@ -479,6 +479,26 @@ func TestComments(t *testing.T) { loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) } +func TestIsInt(t *testing.T) { + checkAndCompare := func(s string, expected bool) { + if isInt(s) != expected { + t.Fail() + } + } + + // invalid values + checkAndCompare("", false) + checkAndCompare("+123", false) + checkAndCompare("+12a3", false) + checkAndCompare("12a3", false) + checkAndCompare("abc", false) + checkAndCompare("12 3", false) + + // valid values + checkAndCompare("-123", true) + checkAndCompare("123", true) +} + func TestWrite(t *testing.T) { writeAndCompare := func(env string, expected string) { envMap, _ := Unmarshal(env) From 2f570db8532974ab8b6e523fd1f2a014312d7f8f Mon Sep 17 00:00:00 2001 From: t3pm14r3 Date: Mon, 2 Sep 2024 10:54:36 +0300 Subject: [PATCH 3/6] fix '-' character trimming and expand the test --- godotenv.go | 4 ++-- godotenv_test.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/godotenv.go b/godotenv.go index c560be5..54eebf4 100644 --- a/godotenv.go +++ b/godotenv.go @@ -160,10 +160,10 @@ func Write(envMap map[string]string, filename string) error { } func isInt(s string) bool { + s = strings.TrimPrefix(s, "-") + if len(s) == 0 { return false - } else if s[0] == '-' { - s = s[1:] } for _, r := range s { diff --git a/godotenv_test.go b/godotenv_test.go index f489615..c79912b 100644 --- a/godotenv_test.go +++ b/godotenv_test.go @@ -493,6 +493,8 @@ func TestIsInt(t *testing.T) { checkAndCompare("12a3", false) checkAndCompare("abc", false) checkAndCompare("12 3", false) + checkAndCompare("-", false) + checkAndCompare(" ", false) // valid values checkAndCompare("-123", true) From 209910751f4acba7635cea24647f268200f30694 Mon Sep 17 00:00:00 2001 From: t3pm14r3 Date: Mon, 2 Sep 2024 13:56:44 +0300 Subject: [PATCH 4/6] replace unicode.IsDigit for number checking --- godotenv.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/godotenv.go b/godotenv.go index 54eebf4..0e64fb3 100644 --- a/godotenv.go +++ b/godotenv.go @@ -21,7 +21,6 @@ import ( "os/exec" "sort" "strings" - "unicode" ) const doubleQuoteSpecialChars = "\\\n\r\"!$`" @@ -167,9 +166,10 @@ func isInt(s string) bool { } for _, r := range s { - if !unicode.IsDigit(r) { - return false - } + if '0' <= r && r <= '9' { + continue + } + return false } return true From bfb9a566c92c2bae5c130b6baac4e826ac8082cf Mon Sep 17 00:00:00 2001 From: t3pm14r3 Date: Tue, 3 Sep 2024 00:23:52 +0300 Subject: [PATCH 5/6] add the comment to isInt, run go fmt --- godotenv.go | 15 ++++++++------ godotenv_test.go | 54 ++++++++++++++++++++++++------------------------ 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/godotenv.go b/godotenv.go index 0e64fb3..8e9fe3f 100644 --- a/godotenv.go +++ b/godotenv.go @@ -158,18 +158,21 @@ func Write(envMap map[string]string, filename string) error { return file.Sync() } +// isInt checks if the string may be serialized as a number value, leading +// "-" symbol is allowed for negative numbers, leading "+" sign is not. The +// length of the value is not limited. func isInt(s string) bool { - s = strings.TrimPrefix(s, "-") + s = strings.TrimPrefix(s, "-") - if len(s) == 0 { + if len(s) == 0 { return false } for _, r := range s { - if '0' <= r && r <= '9' { - continue - } - return false + if '0' <= r && r <= '9' { + continue + } + return false } return true diff --git a/godotenv_test.go b/godotenv_test.go index c79912b..27413dc 100644 --- a/godotenv_test.go +++ b/godotenv_test.go @@ -480,25 +480,25 @@ func TestComments(t *testing.T) { } func TestIsInt(t *testing.T) { - checkAndCompare := func(s string, expected bool) { - if isInt(s) != expected { - t.Fail() - } - } - - // invalid values - checkAndCompare("", false) - checkAndCompare("+123", false) - checkAndCompare("+12a3", false) - checkAndCompare("12a3", false) - checkAndCompare("abc", false) - checkAndCompare("12 3", false) - checkAndCompare("-", false) - checkAndCompare(" ", false) - - // valid values - checkAndCompare("-123", true) - checkAndCompare("123", true) + checkAndCompare := func(s string, expected bool) { + if isInt(s) != expected { + t.Fail() + } + } + + // invalid values + checkAndCompare("", false) + checkAndCompare("+123", false) + checkAndCompare("+12a3", false) + checkAndCompare("12a3", false) + checkAndCompare("abc", false) + checkAndCompare("12 3", false) + checkAndCompare("-", false) + checkAndCompare(" ", false) + + // valid values + checkAndCompare("-123", true) + checkAndCompare("123", true) } func TestWrite(t *testing.T) { @@ -604,42 +604,42 @@ func TestWhitespace(t *testing.T) { }{ "Leading whitespace": { input: " A=a\n", - key: "A", + key: "A", value: "a", }, "Leading tab": { input: "\tA=a\n", - key: "A", + key: "A", value: "a", }, "Leading mixed whitespace": { input: " \t \t\n\t \t A=a\n", - key: "A", + key: "A", value: "a", }, "Leading whitespace before export": { input: " \t\t export A=a\n", - key: "A", + key: "A", value: "a", }, "Trailing whitespace": { input: "A=a \t \t\n", - key: "A", + key: "A", value: "a", }, "Trailing whitespace with export": { input: "export A=a\t \t \n", - key: "A", + key: "A", value: "a", }, "No EOL": { input: "A=a", - key: "A", + key: "A", value: "a", }, "Trailing whitespace with no EOL": { input: "A=a ", - key: "A", + key: "A", value: "a", }, } From f7b3a6b5178d0b55731119194bbf2f8b464d05ee Mon Sep 17 00:00:00 2001 From: t3pm14r3 Date: Tue, 3 Sep 2024 08:48:01 +0300 Subject: [PATCH 6/6] add really big numbers isInt tests --- godotenv_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/godotenv_test.go b/godotenv_test.go index 27413dc..db9f7e0 100644 --- a/godotenv_test.go +++ b/godotenv_test.go @@ -499,6 +499,8 @@ func TestIsInt(t *testing.T) { // valid values checkAndCompare("-123", true) checkAndCompare("123", true) + checkAndCompare("-922337203685477580868712", true) + checkAndCompare("922337203685477580837281", true) } func TestWrite(t *testing.T) {