Skip to content

Commit

Permalink
Ability to pull in rules from remote repos (#122)
Browse files Browse the repository at this point in the history
Co-authored-by: Mary Kate Comer <mkcomer@RYPETERS-CON.redmond.corp.microsoft.com>
Co-authored-by: Mary Kate Comer <mkcomer@Marys-MacBook-Pro.local>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: MK Comer <mcomer@microsoft.com>
  • Loading branch information
5 people authored Nov 4, 2021
1 parent da6e23b commit c5a39fe
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 4 deletions.
42 changes: 40 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package config

import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"

Expand All @@ -27,7 +30,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
}
Expand Down Expand Up @@ -117,13 +126,42 @@ func (c *Config) RemoveRule(i int) {

func loadConfig(filename string) (c Config, err error) {
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)
}

// 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{}
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
}
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
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 {
// 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
Expand Down
27 changes: 25 additions & 2 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}`, strings.Join(configRules, ","))
defaultRulesMsg := fmt.Sprintf(`{"level":"debug","rules":[%s],"message":"default rules"}`, strings.Join(defaultRules, ","))
allRulesMsg := fmt.Sprintf(`{"level":"debug","rules":[%s],"message":"all enabled rules"}`, 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())
})

Expand Down Expand Up @@ -206,8 +208,29 @@ func TestNewConfig(t *testing.T) {
assert.EqualValues(t, expected.Rules, c.Rules)
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)
assert.NotNil(t, c)
})

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)
Expand Down
22 changes: 22 additions & 0 deletions pkg/config/remote.go
Original file line number Diff line number Diff line change
@@ -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
}
34 changes: 34 additions & 0 deletions pkg/config/remote_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}

0 comments on commit c5a39fe

Please sign in to comment.