From 6916f89a2b50aeec6445c54670e0e2da5f7918eb Mon Sep 17 00:00:00 2001 From: Andrew Harvey Date: Wed, 2 Aug 2017 22:02:55 +1000 Subject: [PATCH 1/5] Update README.md for Debian stretch release of webhook package --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6bdc7a09..85239005 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ $ go get github.com/adnanh/webhook to get the latest version of the [webhook](https://github.com/adnanh/webhook/). ### Using package manager -#### Debian "sid" -If you are using unstable version of Debian linux ("sid"), you can install webhook using `apt-get install webhook` which will install community packaged version (thanks [@freeekanayaka](https://github.com/freeekanayaka)) from https://packages.debian.org/sid/webhook +#### Debian +If you are using Debian linux ("stretch" or later), you can install webhook using `apt-get install webhook` which will install community packaged version (thanks [@freeekanayaka](https://github.com/freeekanayaka)) from https://packages.debian.org/sid/webhook ### Download prebuilt binaries Prebuilt binaries for different architectures are available at [GitHub Releases](https://github.com/adnanh/webhook/releases). From d52d7bde1cd63992bb4dba4945b03076a6eb760a Mon Sep 17 00:00:00 2001 From: Ivan Pesin Date: Fri, 25 Aug 2017 23:30:08 -0400 Subject: [PATCH 2/5] Fixed source code formatting with go fmt (spaces to tabs) --- webhook.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webhook.go b/webhook.go index 1034290c..2567bccc 100644 --- a/webhook.go +++ b/webhook.go @@ -155,11 +155,11 @@ func main() { l.SetFormat("{{.Status}} | {{.Duration}} | {{.Hostname}} | {{.Method}} {{.Path}} \n") - standardLogger := log.New(os.Stdout, "[webhook] ", log.Ldate|log.Ltime) + standardLogger := log.New(os.Stdout, "[webhook] ", log.Ldate|log.Ltime) - if !*verbose { - standardLogger.SetOutput(ioutil.Discard) - } + if !*verbose { + standardLogger.SetOutput(ioutil.Discard) + } l.ALogger = standardLogger From 1fc4445668bbb8b831a2e0a701551479e578a0a3 Mon Sep 17 00:00:00 2001 From: Ivan Pesin Date: Fri, 25 Aug 2017 23:31:02 -0400 Subject: [PATCH 3/5] Produce warnings if unable to locate binary and if static parameters specified erroneously --- webhook.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/webhook.go b/webhook.go index 2567bccc..12731c8a 100644 --- a/webhook.go +++ b/webhook.go @@ -312,7 +312,21 @@ func hookHandler(w http.ResponseWriter, r *http.Request) { func handleHook(h *hook.Hook, headers, query, payload *map[string]interface{}, body *[]byte) (string, error) { var errors []error - cmd := exec.Command(h.ExecuteCommand) + // check the command exists + cmdPath, err := exec.LookPath(h.ExecuteCommand) + if err != nil { + log.Printf("unable to locate command: '%s'", h.ExecuteCommand) + + // check if parameters specified in execute-command by mistake + if strings.IndexByte(h.ExecuteCommand, ' ') != -1 { + s := strings.Fields(h.ExecuteCommand)[0] + log.Printf("use 'pass-arguments-to-command' to specify args for '%s'", s) + } + + return "", err + } + + cmd := exec.Command(cmdPath) cmd.Dir = h.CommandWorkingDirectory cmd.Args, errors = h.ExtractCommandArguments(headers, query, payload) From c6febd35b5b0c7642d69eeae37c8c0cb99b2a448 Mon Sep 17 00:00:00 2001 From: Ivan Pesin Date: Sun, 10 Sep 2017 19:35:08 -0500 Subject: [PATCH 4/5] Added a test for command static parameters handling --- webhook_test.go | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/webhook_test.go b/webhook_test.go index 5b3c760e..f1375003 100644 --- a/webhook_test.go +++ b/webhook_test.go @@ -1,8 +1,10 @@ package main import ( + "errors" "fmt" "io/ioutil" + "log" "net" "net/http" "os" @@ -17,6 +19,77 @@ import ( "github.com/adnanh/webhook/hook" ) +func TestStaticParams(t *testing.T) { + var tests = []struct { + s string + err error + }{ + {"passed\n", nil}, + {"", nil}, + {"", errors.New("exec: \"/bin/echo success\": stat /bin/echo success: no such file or directory")}, + } + + spHooks := make([]hook.Hook, 0, 3) + spHooks = append(spHooks, hook.Hook{ + ID: "static-params-ok", + ExecuteCommand: "/bin/echo", + CommandWorkingDirectory: "/tmp", + ResponseMessage: "success", + CaptureCommandOutput: true, + PassArgumentsToCommand: []hook.Argument{ + hook.Argument{Source: "string", Name: "passed"}, + }, + }, + ) + err := os.Symlink("/usr/bin/true", "/tmp/with space") + if err != nil { + t.Fatalf("%v", err) + } + defer os.Remove("/tmp/with space") + spHooks = append(spHooks, hook.Hook{ + ID: "static-params-name-space", + ExecuteCommand: "/tmp/with space", + CommandWorkingDirectory: "/tmp", + ResponseMessage: "success", + CaptureCommandOutput: true, + PassArgumentsToCommand: []hook.Argument{ + hook.Argument{Source: "string", Name: "passed"}, + }, + }, + ) + spHooks = append(spHooks, hook.Hook{ + ID: "static-params-bad", + ExecuteCommand: "/bin/echo success", + CommandWorkingDirectory: "/tmp", + ResponseMessage: "success", + CaptureCommandOutput: true, + PassArgumentsToCommand: []hook.Argument{ + hook.Argument{Source: "string", Name: "failed"}, + }, + }, + ) + + spHeaders := make(map[string]interface{}) + spHeaders["User-Agent"] = "curl/7.54.0" + spHeaders["Accept"] = "*/*" + + log.SetOutput(ioutil.Discard) + + for i, h := range spHooks { + s, err := handleHook(&h, &spHeaders, &map[string]interface{}{}, &map[string]interface{}{}, &[]byte{}) + switch { + case err == nil && tests[i].err != nil: + t.Fatalf("Expected error, but got none") + case err == nil && tests[i].s != s: + t.Fatalf("Expected: %s\nGot: %s\n", tests[i].s, s) + case err != nil && tests[i].err == nil: + t.Fatalf("Unexpected error: %v\n", err) + case err != nil && tests[i].s != s: + t.Fatalf("Expected: %s\nGot: %s\n", tests[i].s, s) + } + } +} + func TestWebhook(t *testing.T) { hookecho, cleanupHookecho := buildHookecho(t) defer cleanupHookecho() From 241061c26d5ff64bd1bf71535c059930991e34aa Mon Sep 17 00:00:00 2001 From: Ivan Pesin Date: Sun, 10 Sep 2017 21:08:15 -0500 Subject: [PATCH 5/5] Improved TestStaticParams to check for log ouput --- webhook_test.go | 89 +++++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/webhook_test.go b/webhook_test.go index f1375003..f7419707 100644 --- a/webhook_test.go +++ b/webhook_test.go @@ -1,7 +1,7 @@ package main import ( - "errors" + "bytes" "fmt" "io/ioutil" "log" @@ -10,6 +10,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "runtime" "strings" "testing" @@ -20,17 +21,13 @@ import ( ) func TestStaticParams(t *testing.T) { - var tests = []struct { - s string - err error - }{ - {"passed\n", nil}, - {"", nil}, - {"", errors.New("exec: \"/bin/echo success\": stat /bin/echo success: no such file or directory")}, - } - spHooks := make([]hook.Hook, 0, 3) - spHooks = append(spHooks, hook.Hook{ + spHeaders := make(map[string]interface{}) + spHeaders["User-Agent"] = "curl/7.54.0" + spHeaders["Accept"] = "*/*" + + // case 1: correct entry + spHook := &hook.Hook{ ID: "static-params-ok", ExecuteCommand: "/bin/echo", CommandWorkingDirectory: "/tmp", @@ -39,14 +36,28 @@ func TestStaticParams(t *testing.T) { PassArgumentsToCommand: []hook.Argument{ hook.Argument{Source: "string", Name: "passed"}, }, - }, - ) - err := os.Symlink("/usr/bin/true", "/tmp/with space") + } + + b := &bytes.Buffer{} + log.SetOutput(b) + + s, err := handleHook(spHook, &spHeaders, &map[string]interface{}{}, &map[string]interface{}{}, &[]byte{}) + if err != nil { + t.Fatalf("Unexpected error: %v\n", err) + } + matched, _ := regexp.MatchString("(?s).*command output: passed.*static-params-ok", b.String()) + if !matched { + t.Fatalf("Unexpected log output:\n%s", b) + } + + // case 2: binary with spaces in its name + err = os.Symlink("/usr/bin/true", "/tmp/with space") if err != nil { t.Fatalf("%v", err) } defer os.Remove("/tmp/with space") - spHooks = append(spHooks, hook.Hook{ + + spHook = &hook.Hook{ ID: "static-params-name-space", ExecuteCommand: "/tmp/with space", CommandWorkingDirectory: "/tmp", @@ -55,9 +66,22 @@ func TestStaticParams(t *testing.T) { PassArgumentsToCommand: []hook.Argument{ hook.Argument{Source: "string", Name: "passed"}, }, - }, - ) - spHooks = append(spHooks, hook.Hook{ + } + + b = &bytes.Buffer{} + log.SetOutput(b) + + s, err = handleHook(spHook, &spHeaders, &map[string]interface{}{}, &map[string]interface{}{}, &[]byte{}) + if err != nil { + t.Fatalf("Unexpected error: %v\n", err) + } + matched, _ = regexp.MatchString("(?s)command output: .*static-params-name-space", b.String()) + if !matched { + t.Fatalf("Unexpected log output:\n%sn", b) + } + + // case 3: parameters specified in execute-command + spHook = &hook.Hook{ ID: "static-params-bad", ExecuteCommand: "/bin/echo success", CommandWorkingDirectory: "/tmp", @@ -66,27 +90,18 @@ func TestStaticParams(t *testing.T) { PassArgumentsToCommand: []hook.Argument{ hook.Argument{Source: "string", Name: "failed"}, }, - }, - ) + } - spHeaders := make(map[string]interface{}) - spHeaders["User-Agent"] = "curl/7.54.0" - spHeaders["Accept"] = "*/*" + b = &bytes.Buffer{} + log.SetOutput(b) - log.SetOutput(ioutil.Discard) - - for i, h := range spHooks { - s, err := handleHook(&h, &spHeaders, &map[string]interface{}{}, &map[string]interface{}{}, &[]byte{}) - switch { - case err == nil && tests[i].err != nil: - t.Fatalf("Expected error, but got none") - case err == nil && tests[i].s != s: - t.Fatalf("Expected: %s\nGot: %s\n", tests[i].s, s) - case err != nil && tests[i].err == nil: - t.Fatalf("Unexpected error: %v\n", err) - case err != nil && tests[i].s != s: - t.Fatalf("Expected: %s\nGot: %s\n", tests[i].s, s) - } + s, err = handleHook(spHook, &spHeaders, &map[string]interface{}{}, &map[string]interface{}{}, &[]byte{}) + if err == nil { + t.Fatalf("Error expected, but none returned: %s\n", s) + } + matched, _ = regexp.MatchString("(?s)unable to locate command: ..bin.echo success.*use 'pass-arguments-to-command'", b.String()) + if !matched { + t.Fatalf("Unexpected log output:\n%s\n", b) } }