From 65a6fa7bc37eff9588968ba2c064c589c52b165e Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 12 Aug 2024 17:10:38 -0400 Subject: [PATCH 01/17] Go Environment variable (parsing) models and tests --- ...github.com.hashicorp.go-envparse.model.yml | 6 ++ .../ext/github.com.joho.godotenv.model.yml | 9 +++ ...ub.com.kelseyhightower.envconfig.model.yml | 11 +++ go/ql/lib/ext/os.model.yml | 5 +- .../flowsources/local/environment/go.mod | 9 +++ .../local/environment/test.expected | 6 ++ .../local/environment/test.ext.yml | 6 ++ .../flowsources/local/environment/test.go | 67 +++++++++++++++++++ .../flowsources/local/environment/test.ql | 5 ++ .../github.com/hashicorp/go-envparse/stub.go | 7 ++ .../vendor/github.com/joho/godotenv/stub.go | 37 ++++++++++ .../kelseyhightower/envconfig/stub.go | 30 +++++++++ .../local/environment/vendor/modules.txt | 9 +++ 13 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 go/ql/lib/ext/github.com.hashicorp.go-envparse.model.yml create mode 100644 go/ql/lib/ext/github.com.joho.godotenv.model.yml create mode 100644 go/ql/lib/ext/github.com.kelseyhightower.envconfig.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/go.mod create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ext.yml create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/hashicorp/go-envparse/stub.go create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/kelseyhightower/envconfig/stub.go create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/modules.txt diff --git a/go/ql/lib/ext/github.com.hashicorp.go-envparse.model.yml b/go/ql/lib/ext/github.com.hashicorp.go-envparse.model.yml new file mode 100644 index 000000000000..73a178fbdcc8 --- /dev/null +++ b/go/ql/lib/ext/github.com.hashicorp.go-envparse.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["github.com/hashicorp/go-envparse", "", False, "Parse", "", "", "ReturnValue", "environment", "manual"] diff --git a/go/ql/lib/ext/github.com.joho.godotenv.model.yml b/go/ql/lib/ext/github.com.joho.godotenv.model.yml new file mode 100644 index 000000000000..8bd62c5dd0b1 --- /dev/null +++ b/go/ql/lib/ext/github.com.joho.godotenv.model.yml @@ -0,0 +1,9 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["github.com/joho/godotenv", "", False, "Parse", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/joho/godotenv", "", False, "Read", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/joho/godotenv", "", False, "Unmarshal", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/joho/godotenv", "", False, "UnmarshalBytes", "", "", "ReturnValue", "environment", "manual"] diff --git a/go/ql/lib/ext/github.com.kelseyhightower.envconfig.model.yml b/go/ql/lib/ext/github.com.kelseyhightower.envconfig.model.yml new file mode 100644 index 000000000000..71d032a18e1b --- /dev/null +++ b/go/ql/lib/ext/github.com.kelseyhightower.envconfig.model.yml @@ -0,0 +1,11 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["github.com/kelseyhightower/envconfig", "", False, "CheckDisallowed", "", "", "Argument[1]", "environment", "manual"] + - ["github.com/kelseyhightower/envconfig", "", False, "MustProcess", "", "", "Argument[1]", "environment", "manual"] + - ["github.com/kelseyhightower/envconfig", "", False, "Process", "", "", "Argument[1]", "environment", "manual"] + - ["github.com/kelseyhightower/envconfig", "", False, "Usage", "", "", "Argument[1]", "environment", "manual"] + - ["github.com/kelseyhightower/envconfig", "", False, "Usagef", "", "", "Argument[1]", "environment", "manual"] + - ["github.com/kelseyhightower/envconfig", "", False, "Usaget", "", "", "Argument[1]", "environment", "manual"] \ No newline at end of file diff --git a/go/ql/lib/ext/os.model.yml b/go/ql/lib/ext/os.model.yml index 31034541b6df..48c9481c5879 100644 --- a/go/ql/lib/ext/os.model.yml +++ b/go/ql/lib/ext/os.model.yml @@ -46,6 +46,9 @@ extensions: pack: codeql/go-all extensible: sourceModel data: + - ["os", "", False, "Environ", "", "", "ReturnValue", "environment", "manual"] + - ["os", "", False, "Getenv", "", "", "ReturnValue", "environment", "manual"] + - ["os", "", False, "LookupEnv", "", "", "ReturnValue[0]", "environment", "manual"] - ["os", "", False, "Open", "", "", "ReturnValue[0]", "file", "manual"] - ["os", "", False, "OpenFile", "", "", "ReturnValue[0]", "file", "manual"] - - ["os", "", False, "ReadFile", "", "", "ReturnValue[0]", "file", "manual"] \ No newline at end of file + - ["os", "", False, "ReadFile", "", "", "ReturnValue[0]", "file", "manual"] diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/go.mod b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/go.mod new file mode 100644 index 000000000000..90c1c9f56600 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/go.mod @@ -0,0 +1,9 @@ +module test + +go 1.22.5 + +require ( + github.com/hashicorp/go-envparse v0.1.0 + github.com/joho/godotenv v1.5.1 + github.com/kelseyhightower/envconfig v1.4.0 +) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected new file mode 100644 index 000000000000..555ce04adeda --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected @@ -0,0 +1,6 @@ +| test.go:12:10:12:26 | call to Getenv | +| test.go:14:2:14:33 | ... := ...[0] | +| test.go:19:20:19:31 | call to Environ | +| test.go:34:29:34:32 | &... | +| test.go:41:2:41:40 | ... := ...[0] | +| test.go:48:2:48:52 | ... := ...[0] | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ext.yml new file mode 100644 index 000000000000..b7075d8e7ae9 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ext.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/threat-models + extensible: threatModelConfiguration + data: + - ["environment", true, 0] \ No newline at end of file diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go new file mode 100644 index 000000000000..fe93d21adb68 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -0,0 +1,67 @@ +package test + +import ( + "fmt" + "github.com/hashicorp/go-envparse" + "github.com/joho/godotenv" + "github.com/kelseyhightower/envconfig" + "os" +) + +func osEnvironmentVariables() { + home := os.Getenv("HOME") + + port, ok := os.LookupEnv("PORT") + if !ok { + port = "3000" + } + + for _, e := range os.Environ() { + _ = e + } + + fmt.Printf("HOME: %s\n", home) + fmt.Printf("PORT: %s\n", port) +} + +type ServerConfig struct { + Port int `envconfig:"PORT"` + Host string `envconfig:"HOST"` +} + +func envconfigEnvironmentVariables() { + var cfg ServerConfig + envconfig.Process("myapp", &cfg) +} + +func godotenvEnvironmentVariables() { + var err error + var username, greeting string + + users, err := godotenv.Read("user.env") + if err != nil { + return + } + + username := users["USERNAME"] + + greetings, err := godotenv.Unmarshal("HELLO=hello") + if err != nil { + return + } + + greeting := greetings["HELLO"] + + fmt.Printf("%s, %s!\n", greeting, username) +} + +func envparseEnvironmentVariables() { + f := os.Open("file.txt") + envVars, ok := envparse.Parse(f) + + if !ok { + return + } + + fmt.Printf("HOME: %s\n", envVars["HOME"]) +} diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql new file mode 100644 index 000000000000..fff264793999 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql @@ -0,0 +1,5 @@ +import go + +from DataFlow::Node source +where source instanceof ThreatModelFlowSource +select source diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/hashicorp/go-envparse/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/hashicorp/go-envparse/stub.go new file mode 100644 index 000000000000..0ce38bd01735 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/hashicorp/go-envparse/stub.go @@ -0,0 +1,7 @@ +package envparse + +import "io" + +func Parse(r io.Reader) (map[string]string, error) { + return nil, nil +} diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go new file mode 100644 index 000000000000..bd8d60dd1926 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go @@ -0,0 +1,37 @@ +package godotenv + +func Exec(filenames []string, cmd string, cmdArgs []string, overload bool) error { + return nil +} + +func Load(filenames ...string) (err error) { + return nil +} + +func Marshal(envMap map[string]string) (string, error) { + return "", nil +} + +func Overload(filenames ...string) (err error) { + return nil +} + +func Parse(r io.Reader) (map[string]string, error) { + return nil, nil +} + +func Read(filenames ...string) (envMap map[string]string, err error) { + return nil, nil +} + +func Unmarshal(str string) (envMap map[string]string, err error) { + return nil, nil +} + +func UnmarshalBytes(src []byte) (map[string]string, error) { + return nil, nil +} + +func Write(envMap map[string]string, filename string) error { + return nil +} diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/kelseyhightower/envconfig/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/kelseyhightower/envconfig/stub.go new file mode 100644 index 000000000000..856b8ebb6ef0 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/kelseyhightower/envconfig/stub.go @@ -0,0 +1,30 @@ +package envconfig + +import ( + "io" + "text/template" +) + +func CheckDisallowed(prefix string, cfg interface{}) error { + return nil +} + +func MustProcess(prefix string, cfg interface{}) { + +} + +func Process(prefix string, cfg interface{}) error { + return nil +} + +func Usage(prefix string, spec interface{}) error { + return nil +} + +func Usagef(prefix string, spec interface{}, out io.Writer, format string) error { + return nil +} + +func Usaget(prefix string, spec interface{}, out io.Writer, tmpl *template.Template) error { + return nil +} diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/modules.txt b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/modules.txt new file mode 100644 index 000000000000..0b7968eaec90 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/modules.txt @@ -0,0 +1,9 @@ +# github.com/hashicorp/go-envparse v0.1.0 +## explicit +github.com/hashicorp/go-envparse +# github.com/joho/godotenv v1.5.1 +## explicit +github.com/joho/godotenv +# github.com/kelseyhightower/envconfig v1.4.0 +## explicit +github.com/kelseyhightower/envconfig From 69f02293f55de9e7b822e8d7e624d2b8ce95ad32 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 12 Aug 2024 17:23:31 -0400 Subject: [PATCH 02/17] Add change note --- .../lib/change-notes/2024-08-12-add-environment-models.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 go/ql/lib/change-notes/2024-08-12-add-environment-models.md diff --git a/go/ql/lib/change-notes/2024-08-12-add-environment-models.md b/go/ql/lib/change-notes/2024-08-12-add-environment-models.md new file mode 100644 index 000000000000..cc88532a5069 --- /dev/null +++ b/go/ql/lib/change-notes/2024-08-12-add-environment-models.md @@ -0,0 +1,8 @@ +--- +category: minorAnalysis +--- +* Local source models for reading and parsing environment variables have been added. The effect libraries include + - "os" + - github.com/hashicorp/go-envparse + - github.com/joho/godotenv + - github.com/kelseyhightower/envconfig From 69679dec1d07341a66cc791959f312f36c24040a Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 12 Aug 2024 20:10:44 -0400 Subject: [PATCH 03/17] Add defer statement --- .../go/dataflow/flowsources/local/environment/test.expected | 1 + .../semmle/go/dataflow/flowsources/local/environment/test.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected index 555ce04adeda..9d356855b27f 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected @@ -4,3 +4,4 @@ | test.go:34:29:34:32 | &... | | test.go:41:2:41:40 | ... := ...[0] | | test.go:48:2:48:52 | ... := ...[0] | +| test.go:61:2:61:33 | ... := ...[0] | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index fe93d21adb68..08d2012a6c52 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -56,7 +56,8 @@ func godotenvEnvironmentVariables() { } func envparseEnvironmentVariables() { - f := os.Open("file.txt") + f := os.OpenRead("file.txt") + defer f.Close() envVars, ok := envparse.Parse(f) if !ok { From ed36aaa5708f22f0c18faaa8947ed9d079857dfc Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Tue, 13 Aug 2024 10:38:15 -0400 Subject: [PATCH 04/17] Fix some minor issues --- .../go/dataflow/flowsources/local/environment/test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index 08d2012a6c52..dfb044975220 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -43,7 +43,7 @@ func godotenvEnvironmentVariables() { return } - username := users["USERNAME"] + username = users["USERNAME"] greetings, err := godotenv.Unmarshal("HELLO=hello") if err != nil { @@ -56,11 +56,14 @@ func godotenvEnvironmentVariables() { } func envparseEnvironmentVariables() { - f := os.OpenRead("file.txt") + f, err := os.Open("file.txt") + if err != nil { + return + } defer f.Close() - envVars, ok := envparse.Parse(f) + envVars, err := envparse.Parse(f) - if !ok { + if err != nil { return } From 47974914a566b8c456e3ead1a380e7b7d2f5fdd2 Mon Sep 17 00:00:00 2001 From: Edward Minnix III Date: Mon, 19 Aug 2024 17:27:10 -0400 Subject: [PATCH 05/17] Apply suggestions from code review Co-authored-by: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com> --- .../lib/change-notes/2024-08-12-add-environment-models.md | 4 ++-- go/ql/lib/ext/os.model.yml | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/go/ql/lib/change-notes/2024-08-12-add-environment-models.md b/go/ql/lib/change-notes/2024-08-12-add-environment-models.md index cc88532a5069..0b0317da8390 100644 --- a/go/ql/lib/change-notes/2024-08-12-add-environment-models.md +++ b/go/ql/lib/change-notes/2024-08-12-add-environment-models.md @@ -1,8 +1,8 @@ --- category: minorAnalysis --- -* Local source models for reading and parsing environment variables have been added. The effect libraries include - - "os" +* Local source models for reading and parsing environment variables have been added for the following libraries: + - os - github.com/hashicorp/go-envparse - github.com/joho/godotenv - github.com/kelseyhightower/envconfig diff --git a/go/ql/lib/ext/os.model.yml b/go/ql/lib/ext/os.model.yml index 48c9481c5879..592b55377f50 100644 --- a/go/ql/lib/ext/os.model.yml +++ b/go/ql/lib/ext/os.model.yml @@ -46,9 +46,14 @@ extensions: pack: codeql/go-all extensible: sourceModel data: - - ["os", "", False, "Environ", "", "", "ReturnValue", "environment", "manual"] + - ["os", "", False, "Environ", "", "", "ReturnValue", "environment", "manual"] # TODO: when sources can have access paths, use .ArrayElement + - ["os", "", False, "ExpandEnv", "", "", "ReturnValue", "environment", "manual"] - ["os", "", False, "Getenv", "", "", "ReturnValue", "environment", "manual"] - ["os", "", False, "LookupEnv", "", "", "ReturnValue[0]", "environment", "manual"] - ["os", "", False, "Open", "", "", "ReturnValue[0]", "file", "manual"] - ["os", "", False, "OpenFile", "", "", "ReturnValue[0]", "file", "manual"] - ["os", "", False, "ReadFile", "", "", "ReturnValue[0]", "file", "manual"] + - ["os", "", False, "UserCacheDir", "", "", "ReturnValue[0]", "environment", "manual"] + - ["os", "", False, "UserConfigDir", "", "", "ReturnValue[0]", "environment", "manual"] + - ["os", "", False, "UserHomeDir", "", "", "ReturnValue[0]", "environment", "manual"] + - ["os", "ProcAttr", False, "Env", "", "", "", "environment", "manual"] # TODO: when sources can have access paths, use .ArrayElement From 257436a49d5f5bd040ce199812dcd488432e25eb Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 19 Aug 2024 17:46:22 -0400 Subject: [PATCH 06/17] Convert test to inline expectation test --- .../local/environment/test.expected | 10 +++------- .../flowsources/local/environment/test.go | 14 ++++++------- .../flowsources/local/environment/test.ql | 20 ++++++++++++++++--- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected index 9d356855b27f..db33d6d2504a 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.expected @@ -1,7 +1,3 @@ -| test.go:12:10:12:26 | call to Getenv | -| test.go:14:2:14:33 | ... := ...[0] | -| test.go:19:20:19:31 | call to Environ | -| test.go:34:29:34:32 | &... | -| test.go:41:2:41:40 | ... := ...[0] | -| test.go:48:2:48:52 | ... := ...[0] | -| test.go:61:2:61:33 | ... := ...[0] | +testFailures +invalidModelRow +failures diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index dfb044975220..64307ac0d3fa 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -9,14 +9,14 @@ import ( ) func osEnvironmentVariables() { - home := os.Getenv("HOME") + home := os.Getenv("HOME") // $ source - port, ok := os.LookupEnv("PORT") + port, ok := os.LookupEnv("PORT") // $ source if !ok { port = "3000" } - for _, e := range os.Environ() { + for _, e := range os.Environ() { // $ source _ = e } @@ -31,21 +31,21 @@ type ServerConfig struct { func envconfigEnvironmentVariables() { var cfg ServerConfig - envconfig.Process("myapp", &cfg) + envconfig.Process("myapp", &cfg) // $ source } func godotenvEnvironmentVariables() { var err error var username, greeting string - users, err := godotenv.Read("user.env") + users, err := godotenv.Read("user.env") // $ source if err != nil { return } username = users["USERNAME"] - greetings, err := godotenv.Unmarshal("HELLO=hello") + greetings, err := godotenv.Unmarshal("HELLO=hello") // $ source if err != nil { return } @@ -61,7 +61,7 @@ func envparseEnvironmentVariables() { return } defer f.Close() - envVars, err := envparse.Parse(f) + envVars, err := envparse.Parse(f) // $ source if err != nil { return diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql index fff264793999..db6bbb1a2d16 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.ql @@ -1,5 +1,19 @@ import go +import ModelValidation +import TestUtilities.InlineExpectationsTest -from DataFlow::Node source -where source instanceof ThreatModelFlowSource -select source +module SourceTest implements TestSig { + string getARelevantTag() { result = "source" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(ThreatModelFlowSource s | + s.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = s.toString() and + value = "" and + tag = "source" + ) + } +} + +import MakeTest From f0f535b0e434b2b5a1a8730a0a62ae5f90319bd5 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 19 Aug 2024 18:36:10 -0400 Subject: [PATCH 07/17] Fix frontend errors --- .../semmle/go/dataflow/flowsources/local/environment/test.go | 2 +- .../local/environment/vendor/github.com/joho/godotenv/stub.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index 64307ac0d3fa..20042d6a40c7 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -50,7 +50,7 @@ func godotenvEnvironmentVariables() { return } - greeting := greetings["HELLO"] + greeting = greetings["HELLO"] fmt.Printf("%s, %s!\n", greeting, username) } diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go index bd8d60dd1926..eba88b5050c7 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/joho/godotenv/stub.go @@ -1,5 +1,7 @@ package godotenv +import "io" + func Exec(filenames []string, cmd string, cmdArgs []string, overload bool) error { return nil } From 8a7e378b401cb8d7bc7f30a8cc02f30c455c6067 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Mon, 19 Aug 2024 18:59:59 -0400 Subject: [PATCH 08/17] caarlos0/env --- .../lib/ext/github.com.caarlos0.env.model.yml | 15 ++++++++++ .../flowsources/local/environment/test.go | 17 +++++++++++ .../vendor/github.com/caarlos0/env/stub.go | 28 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 go/ql/lib/ext/github.com.caarlos0.env.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go diff --git a/go/ql/lib/ext/github.com.caarlos0.env.model.yml b/go/ql/lib/ext/github.com.caarlos0.env.model.yml new file mode 100644 index 000000000000..58576632ea50 --- /dev/null +++ b/go/ql/lib/ext/github.com.caarlos0.env.model.yml @@ -0,0 +1,15 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["github.com/caarlos0/env", "", False, "Parse", "", "", "Argument[0]", "environment", "manual"] + - ["github.com/caarlos0/env", "", False, "ParseAs", "", "", "ReturnValue[0]", "environment", "manual"] + - ["github.com/caarlos0/env", "", False, "ParseAsWithOptions", "", "", "ReturnValue[0]", "environment", "manual"] + - ["github.com/caarlos0/env", "", False, "ParseWithOptions", "", "", "Argument[0]", "environment", "manual"] + - addsTo: + pack: codeql/go-all + extensible: summaryModel + data: + - ["github.com/caarlos0/env", "", False, "Must", "", "", "Argument[0]", "ReturnValue", "value", "manual"] + - ["github.com/caarlos0/env", "", False, "ToMap", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index 20042d6a40c7..a4d8a66deafd 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -2,6 +2,7 @@ package test import ( "fmt" + "github.com/caarlos0/env" "github.com/hashicorp/go-envparse" "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" @@ -69,3 +70,19 @@ func envparseEnvironmentVariables() { fmt.Printf("HOME: %s\n", envVars["HOME"]) } + +func caarlos0EnvironmentVariables() { + type config struct { + Home string `env:"HOME"` + Port int `env:"PORT"` + } + + cfg := config{} + env.Parse(&cfg) // $ source + + fmt.Printf("HOME: %s\n", cfg.Home) + + cfg = env.ParseAs[config]() // $ source + + fmt.Printf("HOME: %s\n", cfg.Home) +} \ No newline at end of file diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go new file mode 100644 index 000000000000..fa042b6a43de --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go @@ -0,0 +1,28 @@ +type Options struct {} + +func Must[T any](t T, err error) T { + if err != nil { + panic(err) + } + return t +} + +func Parse(v interface{}) error { + return nil +} + +func ParseAs[T any]() (T, error) { + return nil, nil +} + +func ParseAsWithOptions[T any](opts Options) (T, error) { + return nil, nil +} + +func ParseWithOptions(v interface{}, opts Options) error { + return nil +} + +func ToMap(env []string) map[string]string { + return nil +} \ No newline at end of file From cf3b3d75d0206afee81503414c10e3b2db1a4833 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Wed, 21 Aug 2024 00:29:17 -0400 Subject: [PATCH 09/17] Fix caarlos0 test --- .../go/dataflow/flowsources/local/environment/test.go | 6 +++--- .../environment/vendor/github.com/caarlos0/env/stub.go | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index a4d8a66deafd..b34e34c43188 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -78,11 +78,11 @@ func caarlos0EnvironmentVariables() { } cfg := config{} - env.Parse(&cfg) // $ source + err := env.Parse(&cfg) // $ source fmt.Printf("HOME: %s\n", cfg.Home) - cfg = env.ParseAs[config]() // $ source + cfg, err = env.ParseAs[config]() // $ source fmt.Printf("HOME: %s\n", cfg.Home) -} \ No newline at end of file +} diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go index fa042b6a43de..c504b1b17b9e 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go @@ -1,3 +1,5 @@ +package env + type Options struct {} func Must[T any](t T, err error) T { From 9f00a0060dec7157efbea4c4b5a1fdfacf241d88 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Wed, 21 Aug 2024 00:30:36 -0400 Subject: [PATCH 10/17] gobuffalo/envy --- .../ext/github.com.gobuffalo.envy.model.yml | 12 ++++++++ .../flowsources/local/environment/test.go | 11 +++++++ .../vendor/github.com/gobuffalo/envy/stub.go | 29 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 go/ql/lib/ext/github.com.gobuffalo.envy.model.yml create mode 100644 go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/gobuffalo/envy/stub.go diff --git a/go/ql/lib/ext/github.com.gobuffalo.envy.model.yml b/go/ql/lib/ext/github.com.gobuffalo.envy.model.yml new file mode 100644 index 000000000000..aaedbb254c90 --- /dev/null +++ b/go/ql/lib/ext/github.com.gobuffalo.envy.model.yml @@ -0,0 +1,12 @@ +extensions: + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["github.com/gobuffalo/envy", "", False, "Environ", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/gobuffalo/envy", "", False, "Get", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/gobuffalo/envy", "", False, "GoPath", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/gobuffalo/envy", "", False, "GoPaths", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/gobuffalo/envy", "", False, "GoBin", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/gobuffalo/envy", "", False, "Map", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/gobuffalo/envy", "", False, "MustGet", "", "", "ReturnValue[0]", "environment", "manual"] diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index b34e34c43188..b25386ff6f63 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -3,6 +3,7 @@ package test import ( "fmt" "github.com/caarlos0/env" + "github.com/gobuffalo/envy" "github.com/hashicorp/go-envparse" "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" @@ -86,3 +87,13 @@ func caarlos0EnvironmentVariables() { fmt.Printf("HOME: %s\n", cfg.Home) } + +func envyEnvironmentVariables() { + goPath := envy.GoPath() // $ source + + fmt.Printf("GOPATH: %s\n", goPath) + + homeDir := envy.MustGet("HOME") // $ source + + fmt.Printf("HOME: %s\n", homeDir) +} diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/gobuffalo/envy/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/gobuffalo/envy/stub.go new file mode 100644 index 000000000000..895b82e9a10c --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/gobuffalo/envy/stub.go @@ -0,0 +1,29 @@ +package envy + +func Environ() []string { + return nil +} + +func Get(key string) string { + return "" +} + +func GoPath() string { + return "" +} + +func GoPaths() []string { + return nil +} + +func GoBin() string { + return "" +} + +func Map() map[string]string { + return nil +} + +func MustGet(key string) string { + return "" +} From 0eddaa066404c5b9e185a0eeccba8c5b2617e092 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Wed, 21 Aug 2024 00:36:48 -0400 Subject: [PATCH 11/17] syscall environment variables --- go/ql/lib/ext/syscall.model.yml | 7 +++++++ .../dataflow/flowsources/local/environment/test.go | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/go/ql/lib/ext/syscall.model.yml b/go/ql/lib/ext/syscall.model.yml index 14ddd68b4021..00ab6018c3fe 100644 --- a/go/ql/lib/ext/syscall.model.yml +++ b/go/ql/lib/ext/syscall.model.yml @@ -20,3 +20,10 @@ extensions: - ["syscall", "Conn", True, "SyscallConn", "", "", "Argument[receiver]", "ReturnValue[0]", "taint", "manual"] - ["syscall", "RawConn", True, "Read", "", "", "Argument[receiver]", "Argument[0]", "taint", "manual"] - ["syscall", "RawConn", True, "Write", "", "", "Argument[0]", "Argument[receiver]", "taint", "manual"] + - addsTo: + pack: codeql/go-all + extensible: sourceModel + data: + - ["syscall", "", False, "Environ", "", "", "ReturnValue", "environment", "manual"] + - ["syscall", "", False, "Getenv", "", "", "ReturnValue[0]", "environment", "manual"] + - ["syscall", "ProcAttr", True, "Env", "", "", "", "environment", "manual"] \ No newline at end of file diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index b25386ff6f63..400f38fd1331 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -8,6 +8,7 @@ import ( "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" "os" + "syscall" ) func osEnvironmentVariables() { @@ -97,3 +98,15 @@ func envyEnvironmentVariables() { fmt.Printf("HOME: %s\n", homeDir) } + +func syscallEnvironmentVariables() { + for _, envVar := range syscall.Environ() { // $ source + fmt.Println("%s", envVar) + } + + home, err := syscall.Getenv("HOME") // $ source + if err != nil { + return + } + fmt.Println("HOME: %s", home) +} From 318a376a78d3e715be9b6ae7422c00049b902f3b Mon Sep 17 00:00:00 2001 From: Edward Minnix III Date: Wed, 21 Aug 2024 09:43:04 -0400 Subject: [PATCH 12/17] Remove ProcAttr models Co-authored-by: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com> --- go/ql/lib/ext/os.model.yml | 1 - go/ql/lib/ext/syscall.model.yml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/go/ql/lib/ext/os.model.yml b/go/ql/lib/ext/os.model.yml index 592b55377f50..3d87eefe43f7 100644 --- a/go/ql/lib/ext/os.model.yml +++ b/go/ql/lib/ext/os.model.yml @@ -56,4 +56,3 @@ extensions: - ["os", "", False, "UserCacheDir", "", "", "ReturnValue[0]", "environment", "manual"] - ["os", "", False, "UserConfigDir", "", "", "ReturnValue[0]", "environment", "manual"] - ["os", "", False, "UserHomeDir", "", "", "ReturnValue[0]", "environment", "manual"] - - ["os", "ProcAttr", False, "Env", "", "", "", "environment", "manual"] # TODO: when sources can have access paths, use .ArrayElement diff --git a/go/ql/lib/ext/syscall.model.yml b/go/ql/lib/ext/syscall.model.yml index 00ab6018c3fe..9d65f2bedbd3 100644 --- a/go/ql/lib/ext/syscall.model.yml +++ b/go/ql/lib/ext/syscall.model.yml @@ -25,5 +25,4 @@ extensions: extensible: sourceModel data: - ["syscall", "", False, "Environ", "", "", "ReturnValue", "environment", "manual"] - - ["syscall", "", False, "Getenv", "", "", "ReturnValue[0]", "environment", "manual"] - - ["syscall", "ProcAttr", True, "Env", "", "", "", "environment", "manual"] \ No newline at end of file + - ["syscall", "", False, "Getenv", "", "", "ReturnValue[0]", "environment", "manual"] \ No newline at end of file From 7ae52425cea0888ba197b8f73b31766c9a95d3fe Mon Sep 17 00:00:00 2001 From: Edward Minnix III Date: Wed, 21 Aug 2024 09:43:24 -0400 Subject: [PATCH 13/17] Update package list in change note --- go/ql/lib/change-notes/2024-08-12-add-environment-models.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go/ql/lib/change-notes/2024-08-12-add-environment-models.md b/go/ql/lib/change-notes/2024-08-12-add-environment-models.md index 0b0317da8390..c511718475d5 100644 --- a/go/ql/lib/change-notes/2024-08-12-add-environment-models.md +++ b/go/ql/lib/change-notes/2024-08-12-add-environment-models.md @@ -3,6 +3,9 @@ category: minorAnalysis --- * Local source models for reading and parsing environment variables have been added for the following libraries: - os + - syscall + - github.com/caarlos0/env + - github.com/gobuffalo/envy - github.com/hashicorp/go-envparse - github.com/joho/godotenv - github.com/kelseyhightower/envconfig From 210ea5be79e02183d84b9fceb2a7268eea4f1ef5 Mon Sep 17 00:00:00 2001 From: Edward Minnix III Date: Wed, 21 Aug 2024 09:43:58 -0400 Subject: [PATCH 14/17] Add model from older versions of caarlos0/env Co-authored-by: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com> --- go/ql/lib/ext/github.com.caarlos0.env.model.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/go/ql/lib/ext/github.com.caarlos0.env.model.yml b/go/ql/lib/ext/github.com.caarlos0.env.model.yml index 58576632ea50..42f6380c3fa9 100644 --- a/go/ql/lib/ext/github.com.caarlos0.env.model.yml +++ b/go/ql/lib/ext/github.com.caarlos0.env.model.yml @@ -6,6 +6,7 @@ extensions: - ["github.com/caarlos0/env", "", False, "Parse", "", "", "Argument[0]", "environment", "manual"] - ["github.com/caarlos0/env", "", False, "ParseAs", "", "", "ReturnValue[0]", "environment", "manual"] - ["github.com/caarlos0/env", "", False, "ParseAsWithOptions", "", "", "ReturnValue[0]", "environment", "manual"] + - ["github.com/caarlos0/env", "", False, "ParseWithFuncs", "", "", "Argument[0]", "environment", "manual"] - ["github.com/caarlos0/env", "", False, "ParseWithOptions", "", "", "Argument[0]", "environment", "manual"] - addsTo: pack: codeql/go-all From 2aa3e1f7a264c5aa5f2acf0489633ff6bbc68c5f Mon Sep 17 00:00:00 2001 From: Edward Minnix III Date: Wed, 21 Aug 2024 09:44:20 -0400 Subject: [PATCH 15/17] Alphabetize models Co-authored-by: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com> --- go/ql/lib/ext/github.com.gobuffalo.envy.model.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/ql/lib/ext/github.com.gobuffalo.envy.model.yml b/go/ql/lib/ext/github.com.gobuffalo.envy.model.yml index aaedbb254c90..1d0d890560d9 100644 --- a/go/ql/lib/ext/github.com.gobuffalo.envy.model.yml +++ b/go/ql/lib/ext/github.com.gobuffalo.envy.model.yml @@ -5,8 +5,8 @@ extensions: data: - ["github.com/gobuffalo/envy", "", False, "Environ", "", "", "ReturnValue", "environment", "manual"] - ["github.com/gobuffalo/envy", "", False, "Get", "", "", "ReturnValue", "environment", "manual"] + - ["github.com/gobuffalo/envy", "", False, "GoBin", "", "", "ReturnValue", "environment", "manual"] - ["github.com/gobuffalo/envy", "", False, "GoPath", "", "", "ReturnValue", "environment", "manual"] - ["github.com/gobuffalo/envy", "", False, "GoPaths", "", "", "ReturnValue", "environment", "manual"] - - ["github.com/gobuffalo/envy", "", False, "GoBin", "", "", "ReturnValue", "environment", "manual"] - ["github.com/gobuffalo/envy", "", False, "Map", "", "", "ReturnValue", "environment", "manual"] - ["github.com/gobuffalo/envy", "", False, "MustGet", "", "", "ReturnValue[0]", "environment", "manual"] From 6fdff977e500bdbbf0d0f1f0fa588ab094f47b40 Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Wed, 21 Aug 2024 09:47:46 -0400 Subject: [PATCH 16/17] Fix test cases --- .../go/dataflow/flowsources/local/environment/test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go index 400f38fd1331..515888f570ed 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/test.go @@ -86,6 +86,10 @@ func caarlos0EnvironmentVariables() { cfg, err = env.ParseAs[config]() // $ source + if err != nil { + return + } + fmt.Printf("HOME: %s\n", cfg.Home) } @@ -104,8 +108,8 @@ func syscallEnvironmentVariables() { fmt.Println("%s", envVar) } - home, err := syscall.Getenv("HOME") // $ source - if err != nil { + home, found := syscall.Getenv("HOME") // $ source + if !found { return } fmt.Println("HOME: %s", home) From c2fa72196652ad7b15d0205625116141fbdf324a Mon Sep 17 00:00:00 2001 From: Ed Minnix Date: Wed, 21 Aug 2024 09:56:42 -0400 Subject: [PATCH 17/17] Fix stub --- .../environment/vendor/github.com/caarlos0/env/stub.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go index c504b1b17b9e..9223a3f4677f 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/flowsources/local/environment/vendor/github.com/caarlos0/env/stub.go @@ -1,6 +1,6 @@ package env -type Options struct {} +type Options struct{} func Must[T any](t T, err error) T { if err != nil { @@ -14,11 +14,13 @@ func Parse(v interface{}) error { } func ParseAs[T any]() (T, error) { - return nil, nil + var t T + return t, nil } func ParseAsWithOptions[T any](opts Options) (T, error) { - return nil, nil + var t T + return t, nil } func ParseWithOptions(v interface{}, opts Options) error { @@ -27,4 +29,4 @@ func ParseWithOptions(v interface{}, opts Options) error { func ToMap(env []string) map[string]string { return nil -} \ No newline at end of file +}