diff --git a/.travis.yml b/.travis.yml index 52eaa1d..569300f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ addons: - runc script: +- make test - make dist - make prepare-test - make test-e2e diff --git a/Makefile b/Makefile index b0f319f..e8451e3 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,10 @@ all: local local: CGO_ENABLED=0 GOOS=linux go build -o bin/faasd +.PHONY: test +test: + CGO_ENABLED=0 GOOS=linux go test -ldflags $(LDFLAGS) ./... + .PHONY: dist dist: CGO_ENABLED=0 GOOS=linux go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd diff --git a/pkg/provider/handlers/secret.go b/pkg/provider/handlers/secret.go index 1dd6d55..9ea451e 100644 --- a/pkg/provider/handlers/secret.go +++ b/pkg/provider/handlers/secret.go @@ -2,11 +2,13 @@ package handlers import ( "encoding/json" + "fmt" "io/ioutil" "log" "net/http" "os" "path" + "strings" "github.com/containerd/containerd" "github.com/openfaas/faas-provider/types" @@ -76,17 +78,6 @@ func createSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, } } -func parseSecret(r *http.Request) (types.Secret, error) { - secret := types.Secret{} - bytesOut, err := ioutil.ReadAll(r.Body) - if err != nil { - return secret, err - } - - err = json.Unmarshal(bytesOut, &secret) - return secret, err -} - func deleteSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) { secret, err := parseSecret(r) if err != nil { @@ -103,3 +94,29 @@ func deleteSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, return } } + +func parseSecret(r *http.Request) (types.Secret, error) { + secret := types.Secret{} + bytesOut, err := ioutil.ReadAll(r.Body) + if err != nil { + return secret, err + } + + err = json.Unmarshal(bytesOut, &secret) + if err != nil { + return secret, err + } + + if isTraversal(secret.Name) { + return secret, fmt.Errorf(traverseErrorSt) + } + + return secret, err +} + +const traverseErrorSt = "directory traversal found in name" + +func isTraversal(name string) bool { + return strings.Contains(name, fmt.Sprintf("%s", string(os.PathSeparator))) || + strings.Contains(name, "..") +} diff --git a/pkg/provider/handlers/secret_test.go b/pkg/provider/handlers/secret_test.go new file mode 100644 index 0000000..b4f2bfc --- /dev/null +++ b/pkg/provider/handlers/secret_test.go @@ -0,0 +1,63 @@ +package handlers + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/openfaas/faas-provider/types" +) + +func Test_parseSecretValidName(t *testing.T) { + + s := types.Secret{Name: "authorized_keys"} + body, _ := json.Marshal(s) + reader := bytes.NewReader(body) + r := httptest.NewRequest(http.MethodPost, "/", reader) + _, err := parseSecret(r) + + if err != nil { + t.Fatalf("secret name is valid with no traversal characters") + } +} + +func Test_parseSecretValidNameWithDot(t *testing.T) { + + s := types.Secret{Name: "authorized.keys"} + body, _ := json.Marshal(s) + reader := bytes.NewReader(body) + r := httptest.NewRequest(http.MethodPost, "/", reader) + _, err := parseSecret(r) + + if err != nil { + t.Fatalf("secret name is valid with no traversal characters") + } +} + +func Test_parseSecretWithTraversalWithSlash(t *testing.T) { + + s := types.Secret{Name: "/root/.ssh/authorized_keys"} + body, _ := json.Marshal(s) + reader := bytes.NewReader(body) + r := httptest.NewRequest(http.MethodPost, "/", reader) + _, err := parseSecret(r) + + if err == nil { + t.Fatalf("secret name should fail due to path traversal") + } +} + +func Test_parseSecretWithTraversalWithDoubleDot(t *testing.T) { + + s := types.Secret{Name: ".."} + body, _ := json.Marshal(s) + reader := bytes.NewReader(body) + r := httptest.NewRequest(http.MethodPost, "/", reader) + _, err := parseSecret(r) + + if err == nil { + t.Fatalf("secret name should fail due to path traversal") + } +}