Skip to content

Commit

Permalink
add support for sending webhook notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
Yusuf Kanchwala committed Aug 10, 2020
1 parent 600a6e6 commit 0ddf0a0
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 2 deletions.
8 changes: 8 additions & 0 deletions pkg/notifications/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package notifications

// Notifier defines the interface which every type of notification provider
// needs to implement to claim support in terrascan
type Notifier interface {
Init() error
SendNotification() error
}
57 changes: 57 additions & 0 deletions pkg/notifications/notifiers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright (C) 2020 Accurics, 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 notifications

import (
"fmt"
"reflect"

"go.uber.org/zap"
)

var (
errNotifierNotSupported = fmt.Errorf("notifier not supported")
)

// NewNotifier returns a new notifier
func NewNotifier(notifierType string) (notifier Notifier, err error) {

// get notifier from supportedNotifierss
notifierObject, supported := supportedNotifiers[supportedNotifierType(notifierType)]
if !supported {
zap.S().Errorf("notifier type '%s' not supported", notifierType)
return notifier, errNotifierNotSupported
}

// notifier
notifier = reflect.New(notifierObject).Interface().(Notifier)

// initialize notifier
notifier.Init()

// successful
return notifier, nil
}

// IsNotifierSupported returns true/false depending on whether the notifier
// is supported in terrascan or not
func IsNotifierSupported(notifierType string) bool {
if _, supported := supportedNotifiers[supportedNotifierType(notifierType)]; !supported {
return false
}
return true
}
29 changes: 29 additions & 0 deletions pkg/notifications/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright (C) 2020 Accurics, 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 notifications

import (
"reflect"
)

// map of supported notifier types
var supportedNotifiers = make(map[supportedNotifierType]reflect.Type)

// RegisterNotifier registers an notifier provider for terrascan
func RegisterNotifier(notifierType supportedNotifierType, notifierProvider reflect.Type) {
supportedNotifiers[notifierType] = notifierProvider
}
20 changes: 20 additions & 0 deletions pkg/notifications/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Copyright (C) 2020 Accurics, 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 notifications

// SupportedNotifierType data type for supported IaC provider
type supportedNotifierType string
35 changes: 35 additions & 0 deletions pkg/notifications/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
Copyright (C) 2020 Accurics, 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 notifications

import (
"reflect"

webhookNotifier "github.com/accurics/terrascan/pkg/notifications/webhook"
)

// terraform specific constants
const (
terraform supportedNotifierType = "webhook"
)

// register terraform as an IaC provider with terrascan
func init() {

// register iac provider
RegisterNotifier(terraform, reflect.TypeOf(webhookNotifier.Webhook{}))
}
23 changes: 23 additions & 0 deletions pkg/notifications/webhook/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Copyright (C) 2020 Accurics, 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 webhook

// Webhook implements the Notifier interface
type Webhook struct {
url string
authToken string
}
51 changes: 51 additions & 0 deletions pkg/notifications/webhook/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright (C) 2020 Accurics, 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 webhook

import (
"go.uber.org/zap"
)

// Init initalizes the webhook notifier, reads config file and configures the
// necessary parameters for webhook notifications to work
func (w *Webhook) Init() error {

// check if conf file exists

// parse conf file

// read webhook url and auth token

// initalize Webhook struct with url and token

// succesful
zap.S().Debug("initialized webhook notifier")
return nil
}

// SendNotification sends webhook notification i.e sends a http POST request
// to the configured URL
func (w *Webhook) SendNotification() error {

// make http POST request

// validate http response

// successful
zap.S().Debug("sent webhook notification")
return nil
}
18 changes: 16 additions & 2 deletions pkg/runtime/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"go.uber.org/zap"

iacProvider "github.com/accurics/terrascan/pkg/iac-providers"
"github.com/accurics/terrascan/pkg/notifications"
)

// Executor object
Expand All @@ -29,7 +30,9 @@ type Executor struct {
cloudType string
iacType string
iacVersion string
configFile string
iacProvider iacProvider.IacProvider
notifiers notifications.Notifier
}

// NewExecutor creates a runtime object
Expand Down Expand Up @@ -66,23 +69,34 @@ func (e *Executor) Init() error {
return err
}

// create new notifiers
e.notifiers, err = notifications.NewNotifier("webhook")
if err != nil {
zap.S().Errorf("failed to create notifier(s). error: '%s'", err)
return err
}

zap.S().Debug("initialized executor")
return nil
}

// Execute validates the inputs, processes the IaC, creates json output
func (e *Executor) Execute() (normalized interface{}, err error) {

// create normalized output from Iac
if e.dirPath != "" {
normalized, err = e.iacProvider.LoadIacDir(e.dirPath)
} else {
// create config from IaC
normalized, err = e.iacProvider.LoadIacFile(e.filePath)
}
if err != nil {
return normalized, err
}

// write output
// evaluate policies

// send notifications, if configured
e.notifiers.SendNotification()

// successful
return normalized, nil
Expand Down
58 changes: 58 additions & 0 deletions pkg/utils/http/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package httputils

import (
"bytes"
"encoding/json"
"log"
"net/http"

"github.com/hashicorp/go-retryablehttp"
"go.uber.org/zap"
)

const (
errNewRequest = fmt.Errorf("failed to create http request")
errDoRequest = fmt.Errorf("failed to make http request")
)

// default global http client
var client *http.Client = &http.Client{}

// init creates a http client which retries on errors like connection timeouts,
// server too slow respond etc.
func init() {
retryClient := retryablehttp.NewClient()
retryClient.RetryMax = 10
client = retryClient.StandardClient()
}

// SendRequest sends a http request on the given url
func SendRequest(method, url, token string, data []byte) (*http.Response, error) {

var resp *http.Response

// new http request
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
zap.S().Errorf("failed to create http request; method: '%v', url: '%v'")
return resp, errNewRequest
}
req.Header.Set("Content-Type", "application/json")
if token != nil {
req.Header.Set("Authorization", fmt.Sprintf("Bearer: '%s'", token))
}

// make request
resp, err := client.Do(req)
if err != nil {
zap.S().Errorf("failed to make http request; method: '%v', url: '%v'")
return resp, errDoRequest
}

return resp, err
}

// SendPOSTRequest sends a http POST request
func SendPOSTRequest(url, token string) (*http.Response, error) {
return SendRequest("POST", url, token)
}

0 comments on commit 0ddf0a0

Please sign in to comment.