From 26f302b83d18bcdb7ecf6c79265a49bb2d48b2e3 Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 3 Aug 2021 09:53:45 -0400
Subject: [PATCH 01/19] Added remote ruleset logic
---
pkg/config/config.go | 61 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 60 insertions(+), 1 deletion(-)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index d6651de1..2c2fe5c8 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -2,7 +2,10 @@ package config
import (
"fmt"
+ "io"
"io/ioutil"
+ "net/http"
+ "net/url"
"os"
"path/filepath"
@@ -81,14 +84,70 @@ func (c *Config) ConfigureRules() {
}
func loadConfig(filename string) (c Config, err error) {
+ if isValidUrl(filename) {
+ // if fileName is a valid URL, we will download and set it to the config
+ log.Debug().Str("url", filename).Msg("Downloading file from")
+ //hardcoding this file and saving to root directory
+ downloadedFile := "downloadedRules.yaml"
+ err := DownloadFile(downloadedFile, filename)
+ if err != nil {
+ return c, err
+ }
+ filename = downloadedFile
+ log.Debug().Str("filename", filename).Msg("Saved remote config to local file.")
+ }
+
+ // TO DO = Need to add more error handling on reading/validating yamlFile file - issue # 16
yamlFile, err := ioutil.ReadFile(filename)
+ log.Debug().Str("filename", filename).Msg("Adding custom ruleset from")
if err != nil {
return c, err
}
-
return c, yaml.Unmarshal(yamlFile, &c)
}
+// isValidUrl tests a string to determine if it is a valid URL or not
+func isValidUrl(toTest string) bool {
+ _, err := url.ParseRequestURI(toTest)
+ if err != nil {
+ return false
+ }
+
+ u, err := url.Parse(toTest)
+ if err != nil || u.Scheme == "" || u.Host == "" {
+ return false
+ }
+ log.Debug().Str("remoteConfig", toTest).Msg("Valid URL for remote config.")
+ return true
+}
+
+//downloads file from url to set filepath
+func DownloadFile(filepath string, url string) error {
+ // Get the data
+ resp, err := http.Get(url)
+ if err != nil {
+ return err
+ }
+ // only parse response body if it is in the response is in the 2xx range
+ if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
+ log.Debug().Int("HTTP Response Status:", resp.StatusCode).Msg("Valid URL Response")
+ defer resp.Body.Close()
+
+ // Create the file
+ out, err := os.Create(filepath)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ // Write the body to file
+ _, err = io.Copy(out, resp.Body)
+ return err
+ } else {
+ return fmt.Errorf("unable to download remote config from url - http response code: %v", resp.StatusCode)
+ }
+}
+
func relative(filename string) string {
// viper provides an absolute path to the config file, but we want the relative
// path to the config file from the current directory to make it easy for woke to ignore it
From 59471553e7570b0efd8e888090c324ab1f116826 Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 3 Aug 2021 09:54:04 -0400
Subject: [PATCH 02/19] Added unit tests for remote rule sets
---
pkg/config/config_test.go | 51 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index ae46bf8c..00622697 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -42,12 +42,14 @@ func TestNewConfig(t *testing.T) {
enabledRules[i] = fmt.Sprintf("%q", c.Rules[i].Name)
}
+ loadedRemoteConfigMsg := `{"level":"debug","filename":"testdata/good.yaml","message":"Adding custom ruleset from"}`
+ loadedRemoteConfig := `{"level":"debug","filename":"testdata/good.yaml","message":"Adding custom ruleset from"}`
loadedConfigMsg := `{"level":"debug","config":"testdata/good.yaml","message":"loaded config file"}`
configRulesMsg := fmt.Sprintf(`{"level":"debug","rules":[%s],"message":"config rules enabled"}`, strings.Join(configRules, ","))
defaultRulesMsg := fmt.Sprintf(`{"level":"debug","rules":[%s],"message":"default rules enabled"}`, strings.Join(defaultRules, ","))
allRulesMsg := fmt.Sprintf(`{"level":"debug","rules":[%s],"message":"all rules enabled"}`, strings.Join(enabledRules, ","))
assert.Equal(t,
- loadedConfigMsg+"\n"+configRulesMsg+"\n"+defaultRulesMsg+"\n"+allRulesMsg+"\n",
+ loadedRemoteConfigMsg+"\n"+loadedRemoteConfig+"\n"+loadedConfigMsg+"\n"+configRulesMsg+"\n"+defaultRulesMsg+"\n"+allRulesMsg+"\n",
out.String())
})
@@ -155,6 +157,53 @@ func TestNewConfig(t *testing.T) {
})
}
+func Test_LoadConfig(t *testing.T) {
+ t.Run("valid-url", func(t *testing.T) {
+ c, err := loadConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
+ assert.NoError(t, err)
+ assert.NotNil(t, c)
+ //delete downloaded file after successful test
+ os.Remove("downloadedRules.yaml")
+ })
+
+ t.Run("invalid-url", func(t *testing.T) {})
+ _, err := loadConfig("https://raw.githubusercontent.com/get-woke/woke/main/example")
+ assert.Error(t, err)
+}
+
+func Test_isValidURL(t *testing.T) {
+ t.Run("valid-url", func(t *testing.T) {
+ boolResponse := isValidUrl("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
+ assert.True(t, boolResponse)
+ })
+
+ t.Run("invalid-url", func(t *testing.T) {
+ boolResponse := isValidUrl("Users/Document/test.yaml")
+ assert.False(t, boolResponse)
+ })
+
+ t.Run("invalid-url", func(t *testing.T) {
+ boolResponse := isValidUrl("/Users/Document/test.yaml")
+ assert.False(t, boolResponse)
+ })
+
+}
+
+func Test_DownloadFile(t *testing.T) {
+ t.Run("valid-url", func(t *testing.T) {
+ err := DownloadFile("DownloadedFile.yaml", "https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
+ assert.NoError(t, err)
+ //delete downloaded file after successful test
+ os.Remove("DownloadedFile.yaml")
+ })
+
+ t.Run("invalid-url", func(t *testing.T) {
+ err := DownloadFile("DownloadedFile.yaml", "https://raw.githubusercontent.com/get-woke/woke/main/example")
+ assert.Error(t, err)
+ })
+
+}
+
func Test_relative(t *testing.T) {
cwd, err := os.Getwd()
assert.NoError(t, err)
From 920fa68fefb94d9b5410427d7a7953a256e06ade Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 3 Aug 2021 10:00:23 -0400
Subject: [PATCH 03/19] Refactored method name for stylecheck - func isValidUrl
should be isValidURL (stylecheck)
---
pkg/config/config.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 2c2fe5c8..5d296f66 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -84,7 +84,7 @@ func (c *Config) ConfigureRules() {
}
func loadConfig(filename string) (c Config, err error) {
- if isValidUrl(filename) {
+ if isValidURL(filename) {
// if fileName is a valid URL, we will download and set it to the config
log.Debug().Str("url", filename).Msg("Downloading file from")
//hardcoding this file and saving to root directory
@@ -107,7 +107,7 @@ func loadConfig(filename string) (c Config, err error) {
}
// isValidUrl tests a string to determine if it is a valid URL or not
-func isValidUrl(toTest string) bool {
+func isValidURL(toTest string) bool {
_, err := url.ParseRequestURI(toTest)
if err != nil {
return false
From 85fe4cf6f8dc546362fa3ec7c7dfcc983f6e8469 Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 3 Aug 2021 10:00:46 -0400
Subject: [PATCH 04/19] Refactored method name for stylecheck - func isValidUrl
should be isValidURL (stylecheck)
---
pkg/config/config_test.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 00622697..bcdc893d 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -173,17 +173,17 @@ func Test_LoadConfig(t *testing.T) {
func Test_isValidURL(t *testing.T) {
t.Run("valid-url", func(t *testing.T) {
- boolResponse := isValidUrl("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
+ boolResponse := isValidURL("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
assert.True(t, boolResponse)
})
t.Run("invalid-url", func(t *testing.T) {
- boolResponse := isValidUrl("Users/Document/test.yaml")
+ boolResponse := isValidURL("Users/Document/test.yaml")
assert.False(t, boolResponse)
})
t.Run("invalid-url", func(t *testing.T) {
- boolResponse := isValidUrl("/Users/Document/test.yaml")
+ boolResponse := isValidURL("/Users/Document/test.yaml")
assert.False(t, boolResponse)
})
From 77d21081396e4460f49d3391dae625a74fca856a Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 3 Aug 2021 12:13:21 -0400
Subject: [PATCH 05/19] Fixed linting warnings and removed usage of http.get
---
pkg/config/config.go | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 5d296f66..4f12d8d9 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -8,6 +8,7 @@ import (
"net/url"
"os"
"path/filepath"
+ "time"
"github.com/get-woke/woke/pkg/rule"
@@ -87,7 +88,7 @@ func loadConfig(filename string) (c Config, err error) {
if isValidURL(filename) {
// if fileName is a valid URL, we will download and set it to the config
log.Debug().Str("url", filename).Msg("Downloading file from")
- //hardcoding this file and saving to root directory
+ // hardcoding this file and saving to root directory
downloadedFile := "downloadedRules.yaml"
err := DownloadFile(downloadedFile, filename)
if err != nil {
@@ -121,10 +122,15 @@ func isValidURL(toTest string) bool {
return true
}
-//downloads file from url to set filepath
+// downloads file from url to set filepath
func DownloadFile(filepath string, url string) error {
- // Get the data
- resp, err := http.Get(url)
+ // Removing gosec lint warning - as we have to pass in user specified url for remote config
+ // nolint:gosec
+ var netClient = &http.Client{
+ Timeout: time.Second * 10,
+ }
+ resp, err := netClient.Get(url)
+ //resp, err := http.Get(url)
if err != nil {
return err
}
From 91a3885452fe9b80949d22f739805da94afc535c Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 3 Aug 2021 12:13:44 -0400
Subject: [PATCH 06/19] Removed whitespace and linting warnings
---
pkg/config/config_test.go | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index bcdc893d..9c3e7962 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -162,7 +162,7 @@ func Test_LoadConfig(t *testing.T) {
c, err := loadConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
assert.NoError(t, err)
assert.NotNil(t, c)
- //delete downloaded file after successful test
+ // delete downloaded file after successful test
os.Remove("downloadedRules.yaml")
})
@@ -186,14 +186,13 @@ func Test_isValidURL(t *testing.T) {
boolResponse := isValidURL("/Users/Document/test.yaml")
assert.False(t, boolResponse)
})
-
}
func Test_DownloadFile(t *testing.T) {
t.Run("valid-url", func(t *testing.T) {
err := DownloadFile("DownloadedFile.yaml", "https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
assert.NoError(t, err)
- //delete downloaded file after successful test
+ // delete downloaded file after successful test
os.Remove("DownloadedFile.yaml")
})
@@ -201,7 +200,6 @@ func Test_DownloadFile(t *testing.T) {
err := DownloadFile("DownloadedFile.yaml", "https://raw.githubusercontent.com/get-woke/woke/main/example")
assert.Error(t, err)
})
-
}
func Test_relative(t *testing.T) {
From 5d4f398343c50dbf9951553a1a5b2cc26d3071af Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 3 Aug 2021 12:46:16 -0400
Subject: [PATCH 07/19] Refactored code to use newrequestwithcontext
---
pkg/config/config.go | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 4f12d8d9..2c464f4c 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,6 +1,7 @@
package config
import (
+ "context"
"fmt"
"io"
"io/ioutil"
@@ -125,15 +126,16 @@ func isValidURL(toTest string) bool {
// downloads file from url to set filepath
func DownloadFile(filepath string, url string) error {
// Removing gosec lint warning - as we have to pass in user specified url for remote config
- // nolint:gosec
- var netClient = &http.Client{
+ var client = &http.Client{
Timeout: time.Second * 10,
}
- resp, err := netClient.Get(url)
- //resp, err := http.Get(url)
+ ctx := context.Background()
+ req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
+ resp, err := client.Do(req)
if err != nil {
return err
}
+
// only parse response body if it is in the response is in the 2xx range
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
log.Debug().Int("HTTP Response Status:", resp.StatusCode).Msg("Valid URL Response")
@@ -146,6 +148,7 @@ func DownloadFile(filepath string, url string) error {
}
defer out.Close()
+ log.Debug().Int("here", resp.StatusCode).Msg("help")
// Write the body to file
_, err = io.Copy(out, resp.Body)
return err
From f6fb076df48aca3100a555a9057d8d17b9e936ae Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Wed, 4 Aug 2021 12:55:14 -0400
Subject: [PATCH 08/19] MAdding more error handling in response text
---
pkg/config/config.go | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 2c464f4c..5e1f0e07 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -125,7 +125,6 @@ func isValidURL(toTest string) bool {
// downloads file from url to set filepath
func DownloadFile(filepath string, url string) error {
- // Removing gosec lint warning - as we have to pass in user specified url for remote config
var client = &http.Client{
Timeout: time.Second * 10,
}
@@ -135,7 +134,6 @@ func DownloadFile(filepath string, url string) error {
if err != nil {
return err
}
-
// only parse response body if it is in the response is in the 2xx range
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
log.Debug().Int("HTTP Response Status:", resp.StatusCode).Msg("Valid URL Response")
@@ -147,13 +145,12 @@ func DownloadFile(filepath string, url string) error {
return err
}
defer out.Close()
-
- log.Debug().Int("here", resp.StatusCode).Msg("help")
// Write the body to file
_, err = io.Copy(out, resp.Body)
return err
} else {
- return fmt.Errorf("unable to download remote config from url - http response code: %v", resp.StatusCode)
+ body, _ := ioutil.ReadAll(resp.Body)
+ return fmt.Errorf("unable to download remote config from url. Response code: %v. Response body: %c", resp.StatusCode, body)
}
}
From 0e14eed3e8194d285df298a3afe61f45c3460bbb Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Wed, 4 Aug 2021 12:55:34 -0400
Subject: [PATCH 09/19] Adding additional edge cases for isValidURL
---
pkg/config/config_test.go | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 9c3e7962..0814c001 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -186,6 +186,16 @@ func Test_isValidURL(t *testing.T) {
boolResponse := isValidURL("/Users/Document/test.yaml")
assert.False(t, boolResponse)
})
+
+ t.Run("invalid-url", func(t *testing.T) {
+ boolResponse := isValidURL("C:User\testpath\test.yaml")
+ assert.False(t, boolResponse)
+ })
+
+ t.Run("invalid-url", func(t *testing.T) {
+ boolResponse := isValidURL("C:\\directory.com\test.yaml")
+ assert.False(t, boolResponse)
+ })
}
func Test_DownloadFile(t *testing.T) {
From b64f5f506d27c5eea6bffc90dc002c411f8c2057 Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Wed, 4 Aug 2021 15:36:14 -0400
Subject: [PATCH 10/19] Updated README to include documentation on adding
remote rulesets
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index b3ad4d03..bffdd204 100644
--- a/README.md
+++ b/README.md
@@ -203,6 +203,8 @@ This file will be picked up automatically up your customizations without needing
See [example.yaml](https://github.com/get-woke/woke/blob/main/example.yaml) for an example of adding custom rules.
You can also supply your own rules with `-c path/to/rules.yaml` if you want to handle different rulesets.
+You can also supply an external remote ruleset from any public URL. To do so, use the same -c flag, followed by the public URL. For example: '-c https://raw.githubusercontent.com/get-woke/woke/main/example.yaml'
+
The syntax for rules is very basic. You just need a name, a list of terms to match that violate the rule,
and a list of alternative suggestions.
From 865618f99d3d205fe93874933619e601c44b1562 Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Wed, 4 Aug 2021 15:36:30 -0400
Subject: [PATCH 11/19] Removed TODO comment
---
pkg/config/config.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 5e1f0e07..296213da 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -99,7 +99,6 @@ func loadConfig(filename string) (c Config, err error) {
log.Debug().Str("filename", filename).Msg("Saved remote config to local file.")
}
- // TO DO = Need to add more error handling on reading/validating yamlFile file - issue # 16
yamlFile, err := ioutil.ReadFile(filename)
log.Debug().Str("filename", filename).Msg("Adding custom ruleset from")
if err != nil {
From cef7e1b6ef9539223fa9970b71030eeba6a88166 Mon Sep 17 00:00:00 2001
From: mkcomer <61518615+mkcomer@users.noreply.github.com>
Date: Thu, 5 Aug 2021 14:02:38 -0400
Subject: [PATCH 12/19] Adding downloadedRules.yaml to woke ignore
---
.wokeignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.wokeignore b/.wokeignore
index 102aba62..2861fc7c 100644
--- a/.wokeignore
+++ b/.wokeignore
@@ -3,3 +3,4 @@ pkg/rule/default.yaml
example.yaml
README.md
testdata
+downloadedRules.yaml
From 46af11df1734fc99f7713f864ac5fc168a1977e6 Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 10 Aug 2021 13:25:43 -0400
Subject: [PATCH 13/19] Removed DownloadFile functionality and updated remote
config to marshall to a yaml file
---
pkg/config/config.go | 68 ++++++++++-----------------------------
pkg/config/config_test.go | 53 +++---------------------------
pkg/config/remote.go | 22 +++++++++++++
pkg/config/remote_test.go | 34 ++++++++++++++++++++
4 files changed, 78 insertions(+), 99 deletions(-)
create mode 100644 pkg/config/remote.go
create mode 100644 pkg/config/remote_test.go
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 91983fdd..a6cadc4d 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,15 +1,11 @@
package config
import (
- "context"
"fmt"
- "io"
"io/ioutil"
"net/http"
- "net/url"
"os"
"path/filepath"
- "time"
"github.com/get-woke/woke/pkg/rule"
@@ -31,7 +27,13 @@ func NewConfig(filename string) (*Config, error) {
var c Config
if len(filename) > 0 {
var err error
- c, err = loadConfig(filename)
+
+ if isValidURL(filename) {
+ c, err = loadRemoteConfig(filename)
+ } else {
+ c, err = loadConfig(filename)
+ }
+
if err != nil {
return nil, err
}
@@ -88,19 +90,6 @@ func (c *Config) ConfigureRules() {
}
func loadConfig(filename string) (c Config, err error) {
- if isValidURL(filename) {
- // if fileName is a valid URL, we will download and set it to the config
- log.Debug().Str("url", filename).Msg("Downloading file from")
- // hardcoding this file and saving to root directory
- downloadedFile := "downloadedRules.yaml"
- err := DownloadFile(downloadedFile, filename)
- if err != nil {
- return c, err
- }
- filename = downloadedFile
- log.Debug().Str("filename", filename).Msg("Saved remote config to local file.")
- }
-
yamlFile, err := ioutil.ReadFile(filename)
log.Debug().Str("filename", filename).Msg("Adding custom ruleset from")
if err != nil {
@@ -109,49 +98,26 @@ func loadConfig(filename string) (c Config, err error) {
return c, yaml.Unmarshal(yamlFile, &c)
}
-// isValidUrl tests a string to determine if it is a valid URL or not
-func isValidURL(toTest string) bool {
- _, err := url.ParseRequestURI(toTest)
- if err != nil {
- return false
- }
-
- u, err := url.Parse(toTest)
- if err != nil || u.Scheme == "" || u.Host == "" {
- return false
- }
- log.Debug().Str("remoteConfig", toTest).Msg("Valid URL for remote config.")
- return true
-}
-
-// downloads file from url to set filepath
-func DownloadFile(filepath string, url string) error {
- var client = &http.Client{
- Timeout: time.Second * 10,
- }
- ctx := context.Background()
- req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
+// gets the remote config from the url provided and returns config
+func loadRemoteConfig(url string) (c Config, err error) {
+ log.Debug().Str("url", url).Msg("Downloading file from")
+ client := &http.Client{}
+ req, err := http.NewRequest(http.MethodGet, url, nil)
resp, err := client.Do(req)
if err != nil {
- return err
+ return c, err
}
+ body, err := ioutil.ReadAll(resp.Body)
// only parse response body if it is in the response is in the 2xx range
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
log.Debug().Int("HTTP Response Status:", resp.StatusCode).Msg("Valid URL Response")
defer resp.Body.Close()
-
- // Create the file
- out, err := os.Create(filepath)
if err != nil {
- return err
+ return c, err
}
- defer out.Close()
- // Write the body to file
- _, err = io.Copy(out, resp.Body)
- return err
+ return c, yaml.Unmarshal(body, &c)
} else {
- body, _ := ioutil.ReadAll(resp.Body)
- return fmt.Errorf("unable to download remote config from url. Response code: %v. Response body: %c", resp.StatusCode, body)
+ return c, fmt.Errorf("unable to download remote config from url. Response code: %v. Response body: %c", resp.StatusCode, body)
}
}
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 0814c001..aeb5d7e7 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -157,61 +157,18 @@ func TestNewConfig(t *testing.T) {
})
}
-func Test_LoadConfig(t *testing.T) {
- t.Run("valid-url", func(t *testing.T) {
- c, err := loadConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
+func Test_LoadRemoteConfig(t *testing.T) {
+ t.Run("load-config-valid-url", func(t *testing.T) {
+ c, err := loadRemoteConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
assert.NoError(t, err)
assert.NotNil(t, c)
- // delete downloaded file after successful test
- os.Remove("downloadedRules.yaml")
})
- t.Run("invalid-url", func(t *testing.T) {})
- _, err := loadConfig("https://raw.githubusercontent.com/get-woke/woke/main/example")
+ t.Run("load-config-invalid-url", func(t *testing.T) {})
+ _, err := loadRemoteConfig("https://raw.githubusercontent.com/get-woke/woke/main/example")
assert.Error(t, err)
}
-func Test_isValidURL(t *testing.T) {
- t.Run("valid-url", func(t *testing.T) {
- boolResponse := isValidURL("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
- assert.True(t, boolResponse)
- })
-
- t.Run("invalid-url", func(t *testing.T) {
- boolResponse := isValidURL("Users/Document/test.yaml")
- assert.False(t, boolResponse)
- })
-
- t.Run("invalid-url", func(t *testing.T) {
- boolResponse := isValidURL("/Users/Document/test.yaml")
- assert.False(t, boolResponse)
- })
-
- t.Run("invalid-url", func(t *testing.T) {
- boolResponse := isValidURL("C:User\testpath\test.yaml")
- assert.False(t, boolResponse)
- })
-
- t.Run("invalid-url", func(t *testing.T) {
- boolResponse := isValidURL("C:\\directory.com\test.yaml")
- assert.False(t, boolResponse)
- })
-}
-
-func Test_DownloadFile(t *testing.T) {
- t.Run("valid-url", func(t *testing.T) {
- err := DownloadFile("DownloadedFile.yaml", "https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
- assert.NoError(t, err)
- // delete downloaded file after successful test
- os.Remove("DownloadedFile.yaml")
- })
-
- t.Run("invalid-url", func(t *testing.T) {
- err := DownloadFile("DownloadedFile.yaml", "https://raw.githubusercontent.com/get-woke/woke/main/example")
- assert.Error(t, err)
- })
-}
-
func Test_relative(t *testing.T) {
cwd, err := os.Getwd()
assert.NoError(t, err)
diff --git a/pkg/config/remote.go b/pkg/config/remote.go
new file mode 100644
index 00000000..b048bbde
--- /dev/null
+++ b/pkg/config/remote.go
@@ -0,0 +1,22 @@
+package config
+
+import (
+ "net/url"
+
+ "github.com/rs/zerolog/log"
+)
+
+// isValidUrl tests a string to determine if it is a valid URL or not
+func isValidURL(toTest string) bool {
+ _, err := url.ParseRequestURI(toTest)
+ if err != nil {
+ return false
+ }
+
+ u, err := url.Parse(toTest)
+ if err != nil || u.Scheme == "" || u.Host == "" {
+ return false
+ }
+ log.Debug().Str("remoteConfig", toTest).Msg("Valid URL for remote config.")
+ return true
+}
diff --git a/pkg/config/remote_test.go b/pkg/config/remote_test.go
new file mode 100644
index 00000000..cc101277
--- /dev/null
+++ b/pkg/config/remote_test.go
@@ -0,0 +1,34 @@
+package config
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_isValidURL(t *testing.T) {
+ t.Run("valid-url-test1", func(t *testing.T) {
+ boolResponse := isValidURL("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
+ assert.True(t, boolResponse)
+ })
+
+ t.Run("invalid-url-test1", func(t *testing.T) {
+ boolResponse := isValidURL("Users/Document/test.yaml")
+ assert.False(t, boolResponse)
+ })
+
+ t.Run("invalid-url-test2", func(t *testing.T) {
+ boolResponse := isValidURL("/Users/Document/test.yaml")
+ assert.False(t, boolResponse)
+ })
+
+ t.Run("invalid-url-test3", func(t *testing.T) {
+ boolResponse := isValidURL("C:User\testpath\test.yaml")
+ assert.False(t, boolResponse)
+ })
+
+ t.Run("invalid-url-test4", func(t *testing.T) {
+ boolResponse := isValidURL("C:\\directory.com\test.yaml")
+ assert.False(t, boolResponse)
+ })
+}
From 2103fd5fa66cab734ebbdfa862ab14f28454db1d Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Tue, 10 Aug 2021 13:39:38 -0400
Subject: [PATCH 14/19] Removed downloadedfile from wokeignore as downloadfile
functionality was removed
---
.wokeignore | 1 -
1 file changed, 1 deletion(-)
diff --git a/.wokeignore b/.wokeignore
index 2861fc7c..102aba62 100644
--- a/.wokeignore
+++ b/.wokeignore
@@ -3,4 +3,3 @@ pkg/rule/default.yaml
example.yaml
README.md
testdata
-downloadedRules.yaml
From d2c41855445779f77f14cf3970bbbba029bc8334 Mon Sep 17 00:00:00 2001
From: Mary Kate Comer
Date: Wed, 11 Aug 2021 16:19:08 -0400
Subject: [PATCH 15/19] Updated get remote config logic and updated unit tests
---
pkg/config/config.go | 21 ++++++++++++---------
pkg/config/config_test.go | 7 ++-----
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index a6cadc4d..a2bfc11f 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -2,6 +2,7 @@ package config
import (
"fmt"
+ "io"
"io/ioutil"
"net/http"
"os"
@@ -107,18 +108,20 @@ func loadRemoteConfig(url string) (c Config, err error) {
if err != nil {
return c, err
}
- body, err := ioutil.ReadAll(resp.Body)
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return c, err
+ }
+ defer resp.Body.Close()
+
// only parse response body if it is in the response is in the 2xx range
- if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
- log.Debug().Int("HTTP Response Status:", resp.StatusCode).Msg("Valid URL Response")
- defer resp.Body.Close()
- if err != nil {
- return c, err
- }
- return c, yaml.Unmarshal(body, &c)
- } else {
+ statusOK := resp.StatusCode >= 200 && resp.StatusCode <= 299
+ if !statusOK {
return c, fmt.Errorf("unable to download remote config from url. Response code: %v. Response body: %c", resp.StatusCode, body)
}
+
+ log.Debug().Int("HTTP Response Status:", resp.StatusCode).Msg("Valid URL Response")
+ return c, yaml.Unmarshal(body, &c)
}
func relative(filename string) string {
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index aeb5d7e7..4a13b71a 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -155,20 +155,17 @@ func TestNewConfig(t *testing.T) {
// check IncludeNote is not overridden for rule1
assert.Equal(t, true, *c.Rules[0].Options.IncludeNote)
})
-}
-func Test_LoadRemoteConfig(t *testing.T) {
- t.Run("load-config-valid-url", func(t *testing.T) {
+ t.Run("load-remote-config-valid-url", func(t *testing.T) {
c, err := loadRemoteConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
assert.NoError(t, err)
assert.NotNil(t, c)
})
- t.Run("load-config-invalid-url", func(t *testing.T) {})
+ t.Run("load-remote-config-invalid-url", func(t *testing.T) {})
_, err := loadRemoteConfig("https://raw.githubusercontent.com/get-woke/woke/main/example")
assert.Error(t, err)
}
-
func Test_relative(t *testing.T) {
cwd, err := os.Getwd()
assert.NoError(t, err)
From 11ba44c96c2759498d6e36f72b583b6699a0e7dc Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 29 Sep 2021 16:01:52 +0000
Subject: [PATCH 16/19] docs: autogenerated update for docs/snippets/woke.md:
`7eb51d`
GitHub Action run 1, commit: https://github.com/inclusive-dev-tools/woke/commit/7eb51dc855388d5407329fa2347827ce259a89ba
---
docs/snippets/woke.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/snippets/woke.md b/docs/snippets/woke.md
index f3c4e558..0b5b8ea0 100644
--- a/docs/snippets/woke.md
+++ b/docs/snippets/woke.md
@@ -7,6 +7,7 @@ Check for usage of non-inclusive language in your code and provide alternatives
### Synopsis
+
woke is a linter that will check your source code for usage of non-inclusive
language and provide suggestions for alternatives. Rules can be customized
to suit your needs.
@@ -29,4 +30,4 @@ woke [globs ...] [flags]
--stdin Read from stdin
```
-###### Auto generated by spf13/cobra on 13-Aug-2021
+###### Auto generated by spf13/cobra on 29-Sep-2021
From 4c82011418c81c0ff5c767487d05d44ac6d5852b Mon Sep 17 00:00:00 2001
From: MK Comer
Date: Thu, 7 Oct 2021 21:45:11 +0000
Subject: [PATCH 17/19] Updated request with context to resolve lint error.
added tests
---
README.md | 78 --------------------------------------
pkg/config/config.go | 7 +++-
pkg/config/config_test.go | 11 ++++++
pkg/config/coverage.out | 57 ++++++++++++++++++++++++++++
woke | Bin 0 -> 10806357 bytes
5 files changed, 74 insertions(+), 79 deletions(-)
delete mode 100644 README.md
create mode 100644 pkg/config/coverage.out
create mode 100755 woke
diff --git a/README.md b/README.md
deleted file mode 100644
index 75ae824b..00000000
--- a/README.md
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
- Detect non-inclusive language in your source code.
-
- I stay woke - Erykah Badu
-
-
----
-
-
-[](https://github.com/get-woke/woke/releases)
-
-[](https://github.com/get-woke/woke/actions)
-[](LICENSE)
-[](https://goreportcard.com/report/github.com/get-woke/woke)
-[](https://codecov.io/gh/get-woke/woke/branch/main)
-
-[](https://pkg.go.dev/github.com/get-woke/woke)
-[](https://github.com/pre-commit/pre-commit)
-
-[](https://app.fossa.com/projects/git%2Bgithub.com%2Fget-woke%2Fwoke?ref=badge_shield)
-[](https://github.com/avelino/awesome-go#other-software)
-
----
-
-Creating an inclusive work environment is imperative to a healthy, supportive, and
-productive culture, and an environment where everyone feels welcome and included.
-
-`woke` is a text file analysis tool that finds places within your source code that contain
-non-inclusive language and suggests replacing them with more inclusive alternatives.
-
-Companies like [GitHub](https://github.com/github/renaming), [Twitter](https://twitter.com/TwitterEng/status/1278733303508418560), and [Apple](https://developer.apple.com/news/?id=1o9zxsxl) are actively supporting a move to inclusive language.
-
-
-
-## Why is this named `woke`?
-
-> When [I](https://github.com/caitlinelfring) started writing `woke` in August 2020, my goal was to build a
-> tool that would shed light on non-inclusive language in source code and that would enable a long-term commitment to using inclusive language.
->
-> `woke` is a reference to an awareness of social and political issues, especially racial prejudice and discrimination,
-> and has a historic use within the African-American community.
-> This tool aims to push this kind of awareness even further into the software development community.
->
-> I named this project `woke` because I saw it as a tool to awaken our code to non-inclusive language.
->
-> You can read more about the origins and history of the word on [Wikipedia](https://en.wikipedia.org/wiki/Woke).
-
-## Install `woke`
-
-- [On my machine](https://docs.getwoke.tech/installation)
-- [On CI/CD systems](https://docs.getwoke.tech/installation/#ci)
-
-## Documentation
-
-Documentation is hosted at .
-
-## Contributing
-
-Please read [CONTRIBUTING.md](https://github.com/get-woke/woke/blob/main/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
-
-
-
-## Authors
-
-- **Caitlin Elfring** - [caitlinelfring](https://github.com/caitlinelfring)
-
-See also the list of [contributors](https://github.com/get-woke/woke/contributors) who have participated in this project.
-
-## License
-
-This application is licensed under the MIT License, you may obtain a copy of it
-[here](https://github.com/get-woke/woke/blob/main/LICENSE).
-
-[](https://app.fossa.com/projects/git%2Bgithub.com%2Fget-woke%2Fwoke?ref=badge_large)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index d9134162..0363a8e7 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,6 +1,7 @@
package config
import (
+ "context"
"fmt"
"io"
"io/ioutil"
@@ -136,7 +137,11 @@ func loadConfig(filename string) (c Config, err error) {
func loadRemoteConfig(url string) (c Config, err error) {
log.Debug().Str("url", url).Msg("Downloading file from")
client := &http.Client{}
- req, err := http.NewRequest(http.MethodGet, url, nil)
+ ctx := context.Background()
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
+ if err != nil {
+ return c, err
+ }
resp, err := client.Do(req)
if err != nil {
return c, err
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index d470c394..8d5d6415 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -209,6 +209,17 @@ func TestNewConfig(t *testing.T) {
assert.Equal(t, "No findings found.", c.GetSuccessExitMessage())
})
+ t.Run("load-config-with-bad-url", func(t *testing.T) {
+ _, err := NewConfig("https://raw.githubusercontent.com/get-woke/woke/main/example")
+ assert.Error(t, err)
+ })
+
+ t.Run("load-config-with-url", func(t *testing.T) {
+ c, err := NewConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
+ assert.NoError(t, err)
+ assert.NotNil(t, c)
+ })
+
t.Run("load-remote-config-valid-url", func(t *testing.T) {
c, err := loadRemoteConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
assert.NoError(t, err)
diff --git a/pkg/config/coverage.out b/pkg/config/coverage.out
new file mode 100644
index 00000000..e0786f44
--- /dev/null
+++ b/pkg/config/coverage.out
@@ -0,0 +1,57 @@
+mode: atomic
+github.com/get-woke/woke/pkg/config/remote.go:10.37,12.16 2 16
+github.com/get-woke/woke/pkg/config/remote.go:16.2,17.50 2 4
+github.com/get-woke/woke/pkg/config/remote.go:20.2,21.13 2 3
+github.com/get-woke/woke/pkg/config/remote.go:12.16,14.3 1 12
+github.com/get-woke/woke/pkg/config/remote.go:17.50,19.3 1 1
+github.com/get-woke/woke/pkg/config/config.go:29.50,31.23 2 12
+github.com/get-woke/woke/pkg/config/config.go:53.2,56.16 3 10
+github.com/get-woke/woke/pkg/config/config.go:31.23,34.27 2 11
+github.com/get-woke/woke/pkg/config/config.go:40.3,40.17 1 11
+github.com/get-woke/woke/pkg/config/config.go:44.3,48.60 3 9
+github.com/get-woke/woke/pkg/config/config.go:34.27,36.4 1 2
+github.com/get-woke/woke/pkg/config/config.go:36.9,38.4 1 9
+github.com/get-woke/woke/pkg/config/config.go:40.17,42.4 1 2
+github.com/get-woke/woke/pkg/config/config.go:49.8,51.3 1 1
+github.com/get-woke/woke/pkg/config/config.go:61.49,62.33 1 5
+github.com/get-woke/woke/pkg/config/config.go:65.2,65.30 1 2
+github.com/get-woke/woke/pkg/config/config.go:62.33,64.3 1 3
+github.com/get-woke/woke/pkg/config/config.go:68.53,69.28 1 143
+github.com/get-woke/woke/pkg/config/config.go:74.2,74.14 1 138
+github.com/get-woke/woke/pkg/config/config.go:69.28,70.23 1 951
+github.com/get-woke/woke/pkg/config/config.go:70.23,72.4 1 5
+github.com/get-woke/woke/pkg/config/config.go:81.35,82.38 1 13
+github.com/get-woke/woke/pkg/config/config.go:88.2,92.28 3 13
+github.com/get-woke/woke/pkg/config/config.go:106.2,106.34 1 13
+github.com/get-woke/woke/pkg/config/config.go:109.2,109.39 1 13
+github.com/get-woke/woke/pkg/config/config.go:82.38,83.28 1 143
+github.com/get-woke/woke/pkg/config/config.go:83.28,85.4 1 138
+github.com/get-woke/woke/pkg/config/config.go:92.28,93.42 1 164
+github.com/get-woke/woke/pkg/config/config.go:101.3,102.34 2 161
+github.com/get-woke/woke/pkg/config/config.go:93.42,95.30 1 77
+github.com/get-woke/woke/pkg/config/config.go:95.30,97.22 2 3
+github.com/get-woke/woke/pkg/config/config.go:106.34,108.3 1 4
+github.com/get-woke/woke/pkg/config/config.go:109.39,116.3 3 3
+github.com/get-woke/woke/pkg/config/config.go:120.36,121.32 1 4
+github.com/get-woke/woke/pkg/config/config.go:124.2,124.49 1 4
+github.com/get-woke/woke/pkg/config/config.go:121.32,123.3 1 0
+github.com/get-woke/woke/pkg/config/config.go:127.56,130.16 3 10
+github.com/get-woke/woke/pkg/config/config.go:133.2,133.40 1 9
+github.com/get-woke/woke/pkg/config/config.go:130.16,132.3 1 1
+github.com/get-woke/woke/pkg/config/config.go:137.57,142.16 5 4
+github.com/get-woke/woke/pkg/config/config.go:145.2,146.16 2 4
+github.com/get-woke/woke/pkg/config/config.go:149.2,150.16 2 4
+github.com/get-woke/woke/pkg/config/config.go:153.2,157.15 3 4
+github.com/get-woke/woke/pkg/config/config.go:161.2,162.36 2 2
+github.com/get-woke/woke/pkg/config/config.go:142.16,144.3 1 0
+github.com/get-woke/woke/pkg/config/config.go:146.16,148.3 1 0
+github.com/get-woke/woke/pkg/config/config.go:150.16,152.3 1 0
+github.com/get-woke/woke/pkg/config/config.go:157.15,159.3 1 2
+github.com/get-woke/woke/pkg/config/config.go:165.39,168.30 1 12
+github.com/get-woke/woke/pkg/config/config.go:174.2,174.17 1 11
+github.com/get-woke/woke/pkg/config/config.go:168.30,170.66 2 1
+github.com/get-woke/woke/pkg/config/config.go:170.66,172.4 1 1
+github.com/get-woke/woke/pkg/config/config.go:178.50,179.49 1 32
+github.com/get-woke/woke/pkg/config/config.go:179.49,181.24 2 32
+github.com/get-woke/woke/pkg/config/config.go:184.3,184.77 1 32
+github.com/get-woke/woke/pkg/config/config.go:181.24,183.4 1 286
diff --git a/woke b/woke
new file mode 100755
index 0000000000000000000000000000000000000000..2da17741a3eefcf3f64911127ef0f85f99067404
GIT binary patch
literal 10806357
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Ⓢ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