From 566d8165fcb33e0eb61775cb07f59769104f3d59 Mon Sep 17 00:00:00 2001 From: Kyle Hasty Date: Thu, 30 Jun 2022 12:19:04 -0400 Subject: [PATCH 1/7] Add GetProviderIacVersions function --- pkg/iac-providers/providers.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/iac-providers/providers.go b/pkg/iac-providers/providers.go index b53fb80b9..777874271 100644 --- a/pkg/iac-providers/providers.go +++ b/pkg/iac-providers/providers.go @@ -79,6 +79,17 @@ func SupportedIacVersions() []string { return iacVersions } +// GetProviderIacVersions returns list of Iac Provider versions for the given IaC type +func GetProviderIacVersions(iacType string) []string { + var versions []string + + for k := range supportedIacProviders[supportedIacType(iacType)] { + versions = append(versions, string(k)) + } + sort.Strings(versions) + return versions +} + // GetDefaultIacVersion returns the default IaC version for the given IaC type func GetDefaultIacVersion(iacType string) string { return string(defaultIacVersions[supportedIacType(iacType)]) From 3db6d9de0c505b38de00038286ab9ea8169a7d3a Mon Sep 17 00:00:00 2001 From: Kyle Hasty Date: Thu, 30 Jun 2022 12:27:15 -0400 Subject: [PATCH 2/7] Add providers endpoint --- pkg/http-server/iac-providers.go | 56 ++++++++++++++++++++++++++++++++ pkg/http-server/routes.go | 1 + 2 files changed, 57 insertions(+) create mode 100644 pkg/http-server/iac-providers.go diff --git a/pkg/http-server/iac-providers.go b/pkg/http-server/iac-providers.go new file mode 100644 index 000000000..13c9a172d --- /dev/null +++ b/pkg/http-server/iac-providers.go @@ -0,0 +1,56 @@ +/* + Copyright (C) 2022 Tenable, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package httpserver + +import ( + "encoding/json" + "fmt" + "net/http" + + iacProvider "github.com/tenable/terrascan/pkg/iac-providers" + + "go.uber.org/zap" +) + +type IacProvider struct { + Type string `json:"type"` + Versions []string `json:"versions"` + DefaultVersion string `json:"defaultVersion"` +} + +func (g *APIHandler) iacProviders(w http.ResponseWriter, r *http.Request) { + + var providers = []IacProvider{} + for _, provider := range iacProvider.SupportedIacProviders() { + providers = append(providers, IacProvider{ + Type: string(provider), + Versions: iacProvider.GetProviderIacVersions(provider), + DefaultVersion: iacProvider.GetDefaultIacVersion(provider), + }) + } + + response, err := json.MarshalIndent(providers, "", " ") + if err != nil { + errMsg := fmt.Sprintf("failed to create JSON. error: '%v'", err) + zap.S().Error(errMsg) + apiErrorResponse(w, errMsg, http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + apiResponse(w, string(response), http.StatusOK) +} diff --git a/pkg/http-server/routes.go b/pkg/http-server/routes.go index d33f7faf1..5eaa4952e 100644 --- a/pkg/http-server/routes.go +++ b/pkg/http-server/routes.go @@ -33,6 +33,7 @@ func (g *APIServer) Routes() []*Route { h := NewAPIHandler() routes := []*Route{ {verb: "GET", path: "/health", fn: h.Health}, + {verb: "GET", path: versionedPath("/providers"), fn: h.iacProviders}, {verb: "POST", path: versionedPath("/{iac}/{iacVersion}/{cloud}/local/file/scan"), fn: h.scanFile}, {verb: "POST", path: versionedPath("/{iac}/{iacVersion}/{cloud}/remote/dir/scan"), fn: h.scanRemoteRepo}, From 68cad5d76a2327e49327df276257e492ae74cbb5 Mon Sep 17 00:00:00 2001 From: Kyle Hasty Date: Thu, 30 Jun 2022 12:31:07 -0400 Subject: [PATCH 3/7] Fix set json resp Content-Type --- pkg/http-server/helpers.go | 2 +- pkg/http-server/iac-providers.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/http-server/helpers.go b/pkg/http-server/helpers.go index 1ff9d211c..00a68b049 100644 --- a/pkg/http-server/helpers.go +++ b/pkg/http-server/helpers.go @@ -23,8 +23,8 @@ import ( // apiResponse creates an API response func apiResponse(w http.ResponseWriter, msg string, statusCode int) { - w.WriteHeader(statusCode) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) fmt.Fprint(w, msg) } diff --git a/pkg/http-server/iac-providers.go b/pkg/http-server/iac-providers.go index 13c9a172d..db16c2931 100644 --- a/pkg/http-server/iac-providers.go +++ b/pkg/http-server/iac-providers.go @@ -51,6 +51,5 @@ func (g *APIHandler) iacProviders(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") apiResponse(w, string(response), http.StatusOK) } From 430b244c7b59eec13a95cec0615064ab3a1477ff Mon Sep 17 00:00:00 2001 From: Kyle Hasty Date: Thu, 30 Jun 2022 12:39:41 -0400 Subject: [PATCH 4/7] Update var name --- pkg/iac-providers/providers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/iac-providers/providers.go b/pkg/iac-providers/providers.go index 777874271..68a70b0c6 100644 --- a/pkg/iac-providers/providers.go +++ b/pkg/iac-providers/providers.go @@ -83,8 +83,8 @@ func SupportedIacVersions() []string { func GetProviderIacVersions(iacType string) []string { var versions []string - for k := range supportedIacProviders[supportedIacType(iacType)] { - versions = append(versions, string(k)) + for version := range supportedIacProviders[supportedIacType(iacType)] { + versions = append(versions, string(version)) } sort.Strings(versions) return versions From c47f47be5cf102d68c341434bb1cb4ac1552c386 Mon Sep 17 00:00:00 2001 From: Kyle Hasty Date: Thu, 30 Jun 2022 13:18:03 -0400 Subject: [PATCH 5/7] Add providers e2e test --- test/e2e/server/server_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/e2e/server/server_test.go b/test/e2e/server/server_test.go index aa3dc8041..9fe97e4fc 100644 --- a/test/e2e/server/server_test.go +++ b/test/e2e/server/server_test.go @@ -107,6 +107,7 @@ var _ = Describe("Server", func() { It("server should be accepting requests", func() { // logs are written in StdErr Eventually(session.Err, serverUtils.ServerCommandTimeout).Should(gbytes.Say("Route GET - /health")) + Eventually(session.Err, serverUtils.ServerCommandTimeout).Should(gbytes.Say("Route GET - /v1/providers")) Eventually(session.Err, serverUtils.ServerCommandTimeout).Should(gbytes.Say("Route POST - /v1/{iac}/{iacVersion}/{cloud}/local/file/scan")) Eventually(session.Err, serverUtils.ServerCommandTimeout).Should(gbytes.Say("Route POST - /v1/{iac}/{iacVersion}/{cloud}/remote/dir/scan")) Eventually(session.Err, serverUtils.ServerCommandTimeout).Should(gbytes.Say("http server listening at port 9010")) @@ -114,6 +115,7 @@ var _ = Describe("Server", func() { Context("request with no body on all handlers", func() { healthCheckURL := fmt.Sprintf("%s:%d/health", host, defaultPort) + providersURL := fmt.Sprintf("%s:%d/v1/providers", host, defaultPort) terraformV12LocalScanURL := fmt.Sprintf("%s:%d/v1/terraform/v12/all/local/file/scan", host, defaultPort) terrformV12RemoteScanURL := fmt.Sprintf("%s:%d/v1/terraform/v12/aws/remote/dir/scan", host, defaultPort) @@ -127,6 +129,16 @@ var _ = Describe("Server", func() { }) }) + When("GET request on providers is made", func() { + It("should get 200 OK response", func() { + r, err := serverUtils.MakeHTTPRequest(http.MethodGet, providersURL) + Expect(err).NotTo(HaveOccurred()) + defer r.Body.Close() + Expect(r).NotTo(BeNil()) + Expect(r.StatusCode).To(BeIdenticalTo(http.StatusOK)) + }) + }) + When("GET request on file scan handler is made", func() { It("should receive method not allowed response", func() { r, err := serverUtils.MakeHTTPRequest(http.MethodGet, terraformV12LocalScanURL) @@ -177,6 +189,16 @@ var _ = Describe("Server", func() { }) }) + When("POST request on providers", func() { + It("should receive method not allowed response", func() { + r, err := serverUtils.MakeHTTPRequest(http.MethodPost, providersURL) + Expect(err).NotTo(HaveOccurred()) + defer r.Body.Close() + Expect(r).NotTo(BeNil()) + Expect(r.StatusCode).To(BeIdenticalTo(http.StatusMethodNotAllowed)) + }) + }) + Context("server is stopped", func() { It("should gracefully exit", func() { if utils.IsWindowsPlatform() { From b0597123a771bb5c2a1a7929bdb399e0057cfbb1 Mon Sep 17 00:00:00 2001 From: Kyle Hasty Date: Fri, 1 Jul 2022 10:14:15 -0400 Subject: [PATCH 6/7] Add comments to iac providers --- pkg/http-server/iac-providers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/http-server/iac-providers.go b/pkg/http-server/iac-providers.go index db16c2931..8feae8b46 100644 --- a/pkg/http-server/iac-providers.go +++ b/pkg/http-server/iac-providers.go @@ -22,18 +22,18 @@ import ( "net/http" iacProvider "github.com/tenable/terrascan/pkg/iac-providers" - "go.uber.org/zap" ) +// IacProvider contains response body for iac providers type IacProvider struct { Type string `json:"type"` Versions []string `json:"versions"` DefaultVersion string `json:"defaultVersion"` } +// iacProviders returns list of iac providers func (g *APIHandler) iacProviders(w http.ResponseWriter, r *http.Request) { - var providers = []IacProvider{} for _, provider := range iacProvider.SupportedIacProviders() { providers = append(providers, IacProvider{ From 672591bfe3fcadb454a3b5abbea1d450d6674424 Mon Sep 17 00:00:00 2001 From: Kyle Hasty Date: Sat, 2 Jul 2022 11:10:42 -0400 Subject: [PATCH 7/7] Add IacProviders unit tests --- pkg/http-server/iac-providers_test.go | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 pkg/http-server/iac-providers_test.go diff --git a/pkg/http-server/iac-providers_test.go b/pkg/http-server/iac-providers_test.go new file mode 100644 index 000000000..07b7a7595 --- /dev/null +++ b/pkg/http-server/iac-providers_test.go @@ -0,0 +1,41 @@ +package httpserver + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" +) + +func TestIacProviders(t *testing.T) { + + handler := NewAPIHandler() + + t.Run("test providers api status", func(t *testing.T) { + var ( + req, _ = http.NewRequest(http.MethodGet, "/v1/providers", nil) + resp = httptest.NewRecorder() + want = http.StatusOK + ) + handler.iacProviders(resp, req) + got := resp.Result().StatusCode + + if got != want { + t.Errorf("incorrect providers status code, got: '%v', want: '%v'", got, want) + } + }) + + t.Run("test providers api response structure", func(t *testing.T) { + var ( + req, _ = http.NewRequest(http.MethodGet, "/v1/providers", nil) + resp = httptest.NewRecorder() + ) + handler.iacProviders(resp, req) + + var data []IacProvider + err := json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + t.Errorf("error parsing response body, error: '%v'", err.Error()) + } + }) +}