From 78c8c61bf2ac6e639e086cb081c855002681a352 Mon Sep 17 00:00:00 2001 From: Awea Date: Mon, 23 Jul 2018 09:54:52 +0200 Subject: [PATCH 01/12] :memo: Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9875828..e4ff0cb 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples ### Guides featuring webhook - [Webhook & JIRA](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks) by [@perfecto25](https://github.com/perfecto25) - [Trigger Ansible AWX job runs on SCM (e.g. git) commit](http://jpmens.net/2017/10/23/trigger-awx-job-runs-on-scm-commit/) by [@jpmens](http://mens.de/) + - [Deploy using GitHub webhooks](https://davidauthier.wearemd.com/blog/deploy-using-github-webhooks.html) by [@awea](https://davidauthier.wearemd.com) ## Community Contributions See the [webhook-contrib][wc] repository for a collections of tools and helpers related to [webhook][w] that have been contributed by the [webhook][w] community. From b93cdc346e2459c68a02a68ff3bcaf329c559e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adnan=20Hajdarevi=C4=87?= Date: Thu, 2 Aug 2018 11:29:49 +0200 Subject: [PATCH 02/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e4ff0cb..bc3e5bb 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ Check out [existing issues](https://github.com/adnanh/webhook/issues) to see if # Support active development ## Sponsors -## DigitalOcean +## DigitalOcean [DigitalOcean](https://www.digitalocean.com/?ref=webhook) is a simple and robust cloud computing platform, designed for developers. From 01e0c9e97261bdb78ebe8170c06e43599429db6a Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 5 Oct 2018 11:24:36 +0100 Subject: [PATCH 03/12] Fix links in Hook Definition docs --- docs/Hook-Definition.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Hook-Definition.md b/docs/Hook-Definition.md index 929e922..c248a77 100644 --- a/docs/Hook-Definition.md +++ b/docs/Hook-Definition.md @@ -11,12 +11,12 @@ Hooks are defined as JSON objects. Please note that in order to be considered va * `include-command-output-in-response` - boolean whether webhook should wait for the command to finish and return the raw output as a response to the hook initiator. If the command fails to execute or encounters any errors while executing the response will result in 500 Internal Server Error HTTP status code, otherwise the 200 OK status code will be returned. * `include-command-output-in-response-on-error` - boolean whether webhook should include command stdout & stderror as a response in failed executions. It only works if `include-command-output-in-response` is set to `true`. * `parse-parameters-as-json` - specifies the list of arguments that contain JSON strings. These parameters will be decoded by webhook and you can access them like regular objects in rules and `pass-arguments-to-command`. - * `pass-arguments-to-command` - specifies the list of arguments that will be passed to the command. Check [Referencing request values page](Referencing-Request-Values) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as + * `pass-arguments-to-command` - specifies the list of arguments that will be passed to the command. Check [Referencing request values page](Referencing-Request-Values.md) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as `{ "source": "string", "name": "argumentvalue" }` - * `pass-environment-to-command` - specifies the list of arguments that will be passed to the command as environment variables. If you do not specify the `"envname"` field in the referenced value, the hook will be in format "HOOK_argumentname", otherwise "envname" field will be used as it's name. Check [Referencing request values page](Referencing-Request-Values) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as + * `pass-environment-to-command` - specifies the list of arguments that will be passed to the command as environment variables. If you do not specify the `"envname"` field in the referenced value, the hook will be in format "HOOK_argumentname", otherwise "envname" field will be used as it's name. Check [Referencing request values page](Referencing-Request-Values.md) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as `{ "source": "string", "envname": "SOMETHING", "name": "argumentvalue" }` * `pass-file-to-command` - specifies a list of entries that will be serialized as a file. Incoming [data](Referencing-Request-Values.md) will be serialized in a request-temporary-file (otherwise parallel calls of the hook would lead to concurrent overwritings of the file). The filename to be addressed within the subsequent script is provided via an environment variable. Use `envname` to specify the name of the environment variable. If `envname` is not provided `HOOK_` and the name used to reference the request value are used. Defining `command-working-directory` will store the file relative to this location, if not provided, the systems temporary file directory will be used. If `base64decode` is true, the incoming binary data will be base 64 decoded prior to storing it into the file. By default the corresponding file will be removed after the webhook exited. - * `trigger-rule` - specifies the rule that will be evaluated in order to determine should the hook be triggered. Check [Hook rules page](Hook-Rules) to see the list of valid rules and their usage + * `trigger-rule` - specifies the rule that will be evaluated in order to determine should the hook be triggered. Check [Hook rules page](Hook-Rules.md) to see the list of valid rules and their usage * `trigger-rule-mismatch-http-response-code` - specifies the HTTP status code to be returned when the trigger rule is not satisfied ## Examples From a0880ab82df19681ea351473d968efb87da11759 Mon Sep 17 00:00:00 2001 From: Zach Cheung Date: Tue, 13 Nov 2018 23:30:47 +0800 Subject: [PATCH 04/12] docs: fix link --- docs/Hook-Definition.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Hook-Definition.md b/docs/Hook-Definition.md index 929e922..c248a77 100644 --- a/docs/Hook-Definition.md +++ b/docs/Hook-Definition.md @@ -11,12 +11,12 @@ Hooks are defined as JSON objects. Please note that in order to be considered va * `include-command-output-in-response` - boolean whether webhook should wait for the command to finish and return the raw output as a response to the hook initiator. If the command fails to execute or encounters any errors while executing the response will result in 500 Internal Server Error HTTP status code, otherwise the 200 OK status code will be returned. * `include-command-output-in-response-on-error` - boolean whether webhook should include command stdout & stderror as a response in failed executions. It only works if `include-command-output-in-response` is set to `true`. * `parse-parameters-as-json` - specifies the list of arguments that contain JSON strings. These parameters will be decoded by webhook and you can access them like regular objects in rules and `pass-arguments-to-command`. - * `pass-arguments-to-command` - specifies the list of arguments that will be passed to the command. Check [Referencing request values page](Referencing-Request-Values) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as + * `pass-arguments-to-command` - specifies the list of arguments that will be passed to the command. Check [Referencing request values page](Referencing-Request-Values.md) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as `{ "source": "string", "name": "argumentvalue" }` - * `pass-environment-to-command` - specifies the list of arguments that will be passed to the command as environment variables. If you do not specify the `"envname"` field in the referenced value, the hook will be in format "HOOK_argumentname", otherwise "envname" field will be used as it's name. Check [Referencing request values page](Referencing-Request-Values) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as + * `pass-environment-to-command` - specifies the list of arguments that will be passed to the command as environment variables. If you do not specify the `"envname"` field in the referenced value, the hook will be in format "HOOK_argumentname", otherwise "envname" field will be used as it's name. Check [Referencing request values page](Referencing-Request-Values.md) to see how to reference the values from the request. If you want to pass a static string value to your command you can specify it as `{ "source": "string", "envname": "SOMETHING", "name": "argumentvalue" }` * `pass-file-to-command` - specifies a list of entries that will be serialized as a file. Incoming [data](Referencing-Request-Values.md) will be serialized in a request-temporary-file (otherwise parallel calls of the hook would lead to concurrent overwritings of the file). The filename to be addressed within the subsequent script is provided via an environment variable. Use `envname` to specify the name of the environment variable. If `envname` is not provided `HOOK_` and the name used to reference the request value are used. Defining `command-working-directory` will store the file relative to this location, if not provided, the systems temporary file directory will be used. If `base64decode` is true, the incoming binary data will be base 64 decoded prior to storing it into the file. By default the corresponding file will be removed after the webhook exited. - * `trigger-rule` - specifies the rule that will be evaluated in order to determine should the hook be triggered. Check [Hook rules page](Hook-Rules) to see the list of valid rules and their usage + * `trigger-rule` - specifies the rule that will be evaluated in order to determine should the hook be triggered. Check [Hook rules page](Hook-Rules.md) to see the list of valid rules and their usage * `trigger-rule-mismatch-http-response-code` - specifies the HTTP status code to be returned when the trigger rule is not satisfied ## Examples From fc0544e4a2f0b5d64359d2005bbf77c8fd15aa69 Mon Sep 17 00:00:00 2001 From: Adnan Hajdarevic Date: Tue, 13 Nov 2018 21:01:42 +0100 Subject: [PATCH 05/12] Bump version to 2.6.9 --- webhook.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webhook.go b/webhook.go index f106697..5d41077 100644 --- a/webhook.go +++ b/webhook.go @@ -23,7 +23,7 @@ import ( ) const ( - version = "2.6.8" + version = "2.6.9" ) var ( From 98f86cf044ecec51c6c19802fe57c33756c0a3ea Mon Sep 17 00:00:00 2001 From: Adnan Hajdarevic Date: Tue, 13 Nov 2018 21:12:20 +0100 Subject: [PATCH 06/12] Fix Makefile to include .exe extension for windows builds --- Makefile | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e9f04d6..b341c1f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -OS = darwin freebsd linux openbsd windows +OS = darwin freebsd linux openbsd ARCHS = 386 arm amd64 arm64 -all: build release +all: build release release-windows build: deps go build @@ -18,6 +18,15 @@ release: clean deps done \ done +release-windows: clean deps + @for arch in $(ARCHS);\ + do \ + echo "Building windows-$$arch"; \ + mkdir -p build/webhook-windows-$$arch/; \ + GOOS=windows GOARCH=$$arch go build -o build/webhook-windows-$$arch/webhook.exe; \ + tar cz -C build -f build/webhook-windows-$$arch.tar.gz webhook-windows-$$arch; \ + done + test: deps go test ./... From e8628cd662836662296bc02d6c125958428d26fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adnan=20Hajdarevi=C4=87?= Date: Fri, 16 Nov 2018 10:45:37 +0100 Subject: [PATCH 07/12] Add more guides to the README --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bc3e5bb..0d877a6 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,20 @@ Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples - [Webhook & JIRA](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks) by [@perfecto25](https://github.com/perfecto25) - [Trigger Ansible AWX job runs on SCM (e.g. git) commit](http://jpmens.net/2017/10/23/trigger-awx-job-runs-on-scm-commit/) by [@jpmens](http://mens.de/) - [Deploy using GitHub webhooks](https://davidauthier.wearemd.com/blog/deploy-using-github-webhooks.html) by [@awea](https://davidauthier.wearemd.com) - + - [Setting up Automatic Deployment and Builds Using Webhooks](https://willbrowning.me/setting-up-automatic-deployment-and-builds-using-webhooks/) by [Will Browning](https://willbrowning.me/about/) + - [Auto deploy your Node.js app on push to GitHub in 3 simple steps](https://webhookrelay.com/blog/2018/07/17/auto-deploy-on-git-push/) by Karolis Rusenas + - [Automate Static Site Deployments with Salt, Git, and Webhooks](https://www.linode.com/docs/applications/configuration-management/automate-a-static-site-deployment-with-salt/) by [Linode](https://www.linode.com) + - [Using Prometheus to Automatically Scale WebLogic Clusters on Kubernetes](https://blogs.oracle.com/weblogicserver/using-prometheus-to-automatically-scale-weblogic-clusters-on-kubernetes-v5) by [Marina Kogan](https://blogs.oracle.com/author/9a4fe754-1cc2-4c64-95fc-360642b62927) + - [Github Pages and Jekyll - A New Platform for LACNIC Labs](https://labs.lacnic.net/a-new-platform-for-lacniclabs/) by [Carlos Martínez Cagnazzo](https://twitter.com/carlosm3011) + - [How to Deploy React Apps Using Webhooks and Integrating Slack on Ubuntu](https://www.alibabacloud.com/blog/how-to-deploy-react-apps-using-webhooks-and-integrating-slack-on-ubuntu_594116) by Arslan Ud Din Shafiq + - [Private webhooks](https://ihateithe.re/2018/01/private-webhooks/) by [Thomas](https://ihateithe.re/colophon/) + - [Adventures in webhooks](https://medium.com/@draketech/adventures-in-webhooks-2d6584501c62) by [Drake](https://medium.com/@draketech) + - [http://notes.spencerlyon.com/2016/01/04/github-pro-tips/](http://notes.spencerlyon.com/2016/01/04/github-pro-tips/) by [Spencer Lyon](http://notes.spencerlyon.com/) + - [XiaoMi Vacuum + Amazon Button = Dash Cleaning](https://www.instructables.com/id/XiaoMi-Vacuum-Amazon-Button-Dash-Cleaning/) by [c0mmensal](https://www.instructables.com/member/c0mmensal/) + - VIDEO: [Gitlab CI/CD configuration using Docker and adnanh/webhook to deploy on VPS - Tutorial #1](https://www.youtube.com/watch?v=Qhn-lXjyrZA&feature=youtu.be) by [Yes! Let's Learn Software Engineering](https://www.youtube.com/channel/UCH4XJf2BZ_52fbf8fOBMF3w) + - ... + - Want to add your own? Open an Issue or create a PR :-) + ## Community Contributions See the [webhook-contrib][wc] repository for a collections of tools and helpers related to [webhook][w] that have been contributed by the [webhook][w] community. From b2899d1d3e3403bbc15cf66516d3e4f1e1db1786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adnan=20Hajdarevi=C4=87?= Date: Fri, 16 Nov 2018 10:46:35 +0100 Subject: [PATCH 08/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d877a6..a2259d4 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples - [How to Deploy React Apps Using Webhooks and Integrating Slack on Ubuntu](https://www.alibabacloud.com/blog/how-to-deploy-react-apps-using-webhooks-and-integrating-slack-on-ubuntu_594116) by Arslan Ud Din Shafiq - [Private webhooks](https://ihateithe.re/2018/01/private-webhooks/) by [Thomas](https://ihateithe.re/colophon/) - [Adventures in webhooks](https://medium.com/@draketech/adventures-in-webhooks-2d6584501c62) by [Drake](https://medium.com/@draketech) - - [http://notes.spencerlyon.com/2016/01/04/github-pro-tips/](http://notes.spencerlyon.com/2016/01/04/github-pro-tips/) by [Spencer Lyon](http://notes.spencerlyon.com/) + - [GitHub pro tips](http://notes.spencerlyon.com/2016/01/04/github-pro-tips/) by [Spencer Lyon](http://notes.spencerlyon.com/) - [XiaoMi Vacuum + Amazon Button = Dash Cleaning](https://www.instructables.com/id/XiaoMi-Vacuum-Amazon-Button-Dash-Cleaning/) by [c0mmensal](https://www.instructables.com/member/c0mmensal/) - VIDEO: [Gitlab CI/CD configuration using Docker and adnanh/webhook to deploy on VPS - Tutorial #1](https://www.youtube.com/watch?v=Qhn-lXjyrZA&feature=youtu.be) by [Yes! Let's Learn Software Engineering](https://www.youtube.com/channel/UCH4XJf2BZ_52fbf8fOBMF3w) - ... From ce7f8d5d28c44fa042799a9ce9bc1d703a27791e Mon Sep 17 00:00:00 2001 From: ruliezz Date: Fri, 16 Nov 2018 15:23:39 +0100 Subject: [PATCH 09/12] Updated with a Gitea example --- docs/Hook-Examples.md | 647 +++++++++++++++++++++++------------------- 1 file changed, 351 insertions(+), 296 deletions(-) diff --git a/docs/Hook-Examples.md b/docs/Hook-Examples.md index aedba52..b18abf7 100644 --- a/docs/Hook-Examples.md +++ b/docs/Hook-Examples.md @@ -1,296 +1,351 @@ -# Hook examples -This page is still work in progress. Feel free to contribute! - -## Incoming Github webhook -```json -[ - { - "id": "webhook", - "execute-command": "/home/adnan/redeploy-go-webhook.sh", - "command-working-directory": "/home/adnan/go", - "pass-arguments-to-command": - [ - { - "source": "payload", - "name": "head_commit.id" - }, - { - "source": "payload", - "name": "pusher.name" - }, - { - "source": "payload", - "name": "pusher.email" - } - ], - "trigger-rule": - { - "and": - [ - { - "match": - { - "type": "payload-hash-sha1", - "secret": "mysecret", - "parameter": - { - "source": "header", - "name": "X-Hub-Signature" - } - } - }, - { - "match": - { - "type": "value", - "value": "refs/heads/master", - "parameter": - { - "source": "payload", - "name": "ref" - } - } - } - ] - } - } -] -``` - -## Incoming Bitbucket webhook - -Bitbucket does not pass any secrets back to the webhook. [Per their documentation](https://confluence.atlassian.com/bitbucket/manage-webhooks-735643732.html#Managewebhooks-trigger_webhookTriggeringwebhooks), in order to verify that the webhook came from Bitbucket you must whitelist the IP range `104.192.143.0/24`: - -```json -[ - { - "id": "webhook", - "execute-command": "/home/adnan/redeploy-go-webhook.sh", - "command-working-directory": "/home/adnan/go", - "pass-arguments-to-command": - [ - { - "source": "payload", - "name": "actor.username" - } - ], - "trigger-rule": - { - "match": - { - "type": "ip-whitelist", - "ip-range": "104.192.143.0/24" - } - } - } -] -``` - -## Incoming Gitlab Webhook -Gitlab provides webhooks for many kinds of events. -Refer to this URL for example request body content: [gitlab-ce/integrations/webhooks](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/project/integrations/webhooks.md) -Values in the request body can be accessed in the command or to the match rule by referencing 'payload' as the source: -```json -[ - { - "id": "redeploy-webhook", - "execute-command": "/home/adnan/redeploy-go-webhook.sh", - "command-working-directory": "/home/adnan/go", - "pass-arguments-to-command": - [ - { - "source": "payload", - "name": "user_name" - } - ], - "response-message": "Executing redeploy script", - "trigger-rule": - { - "match": - { - "type": "value", - "value": "", - "parameter": - { - "source": "header", - "name": "X-Gitlab-Token" - } - } - } - } -] -``` - -## Incoming Gogs webhook -```json -[ - { - "id": "webhook", - "execute-command": "/home/adnan/redeploy-go-webhook.sh", - "command-working-directory": "/home/adnan/go", - "pass-arguments-to-command": - [ - { - "source": "payload", - "name": "head_commit.id" - }, - { - "source": "payload", - "name": "pusher.name" - }, - { - "source": "payload", - "name": "pusher.email" - } - ], - "trigger-rule": - { - "and": - [ - { - "match": - { - "type": "payload-hash-sha256", - "secret": "mysecret", - "parameter": - { - "source": "header", - "name": "X-Gogs-Signature" - } - } - }, - { - "match": - { - "type": "value", - "value": "refs/heads/master", - "parameter": - { - "source": "payload", - "name": "ref" - } - } - } - ] - } - } -] -``` - -## Slack slash command -```json -[ - { - "id": "redeploy-webhook", - "execute-command": "/home/adnan/redeploy-go-webhook.sh", - "command-working-directory": "/home/adnan/go", - "response-message": "Executing redeploy script", - "trigger-rule": - { - "match": - { - "type": "value", - "value": "", - "parameter": - { - "source": "payload", - "name": "token" - } - } - } - } -] -``` - -## A simple webhook with a secret key in GET query - -__Not recommended in production due to low security__ - -`example.com:9000/hooks/simple-one` - won't work -`example.com:9000/hooks/simple-one?token=42` - will work - -```json -[ - { - "id": "simple-one", - "execute-command": "/path/to/command.sh", - "response-message": "Executing simple webhook...", - "trigger-rule": - { - "match": - { - "type": "value", - "value": "42", - "parameter": - { - "source": "url", - "name": "token" - } - } - } - } -] -``` - -# JIRA Webhooks -[Guide by @perfecto25](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks) - -# Pass File-to-command sample - -## Webhook configuration - -
-[
-  {
-    "id": "test-file-webhook",
-    "execute-command": "/bin/ls",
-    "command-working-directory": "/tmp",
-    "pass-file-to-command":
-    [
-      {
-      	"source": "payload",
- 	"name": "binary",
-      	"envname": "ENV_VARIABLE", // to use $ENV_VARIABLE in execute-command
-                                   // if not defined, $HOOK_BINARY will be provided
-      	"base64decode": true,      // defaults to false
-      }
-    ],
-    "include-command-output-in-response": true
-  }
-]
-
- -## Sample client usage - -Store the following file as `testRequest.json`. - -
-{"binary":"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjEzMTA4RDI0QzMxQjExRTBCMzYzRjY1QUQ1Njc4QzFBIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjEzMTA4RDIzQzMxQjExRTBCMzYzRjY1QUQ1Njc4QzFBIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+IBFgEwAAAmJJREFUeNqkk89rE1EQx2d/NNq0xcYYayPYJDWC9ODBsKIgAREjBmvEg2cvHnr05KHQ9iB49SL+/BMEfxBQKHgwCEbTNNIYaqgaoanFJi+rcXezye4689jYkIMIDnx47837zrx583YFx3Hgf0xA6/dJyAkkgUy4vgryAnmNWH9L4EVmotFoKplMHgoGg6PkrFarjXQ6/bFcLj/G5W1E+3NaX4KZeDx+dX5+7kg4HBlmrC6JoiDFYrGhROLM/mp1Y6JSqdCd3/SW0GUqEAjkl5ZyHTSHKBQKnO6a9khD2m5cr91IJBJ1VVWdiM/n6LruNJtNDs3JR3ukIW03SHTHi8iVsbG9I51OG1bW16HVasHQZopDc/JZVgdIQ1o3BmTkEnJXURS/KIpgGAYPkCQJPi0u8uzDKQN0XQPbtgE1MmrHs9nsfSqAEjxCNtHxZHLy4G4smUQgyzL4LzOegDGGp1ucVqsNqKVrpJCM7F4hg6iaZvhqtZrg8XjA4xnAU3XeKLqWaRImoIZeQXVjQO5pYp4xNVirsR1erxer2O4yfa227WCwhtWoJmn7m0h270NxmemFW4706zMm8GCgxBGEASCfhnukIW03iFdQnOPz0LNKp3362JqQzSw4u2LXBe+Bs3xD+/oc1NxN55RiC9fOme0LEQiRf2rBzaKEeJJ37ZWTVunBeGN2WmQjg/DeLTVP89nzAive2dMwlo9bpFVC2xWMZr+A720FVn88fAUb3wDMOjyN7YNc6TvUSHQ4AH6TOUdLL7em68UtWPsJqxgTpgeiLu1EBt1R+Me/mF7CQPTfAgwAGxY2vOTrR3oAAAAASUVORK5CYII="}
-
- -use then the curl tool to execute a request to the webhook. - -
-#!/bin/bash
-curl -H "Content-Type:application/json" -X POST -d @testRequest.json \
-http://localhost:9000/hooks/test-file-webhook
-
- -or in a single line, using https://github.com/jpmens/jo to generate the JSON code -
-jo binary=%filename.zip | curl -H "Content-Type:application/json" -X POST -d @- \
-http://localhost:9000/hooks/test-file-webhook
-
- - -## Incoming Scalr Webhook -[Guide by @hassanbabaie] -Scalr makes webhook calls based on an event to a configured webhook endpoint (for example Host Down, Host Up). Webhook endpoints are URLs where Scalr will deliver Webhook notifications. -Scalr assigns a unique signing key for every configured webhook endpoint. -Refer to this URL for information on how to setup the webhook call on the Scalr side: [Scalr Wiki Webhooks](https://scalr-wiki.atlassian.net/wiki/spaces/docs/pages/6193173/Webhooks) -In order to leverage the Signing Key for addtional authentication/security you must configure the trigger rule with a match type of "scalr-signature". - -```json +# Hook examples +This page is still work in progress. Feel free to contribute! + +## Incoming Github webhook +```json +[ + { + "id": "webhook", + "execute-command": "/home/adnan/redeploy-go-webhook.sh", + "command-working-directory": "/home/adnan/go", + "pass-arguments-to-command": + [ + { + "source": "payload", + "name": "head_commit.id" + }, + { + "source": "payload", + "name": "pusher.name" + }, + { + "source": "payload", + "name": "pusher.email" + } + ], + "trigger-rule": + { + "and": + [ + { + "match": + { + "type": "payload-hash-sha1", + "secret": "mysecret", + "parameter": + { + "source": "header", + "name": "X-Hub-Signature" + } + } + }, + { + "match": + { + "type": "value", + "value": "refs/heads/master", + "parameter": + { + "source": "payload", + "name": "ref" + } + } + } + ] + } + } +] +``` + +## Incoming Bitbucket webhook + +Bitbucket does not pass any secrets back to the webhook. [Per their documentation](https://confluence.atlassian.com/bitbucket/manage-webhooks-735643732.html#Managewebhooks-trigger_webhookTriggeringwebhooks), in order to verify that the webhook came from Bitbucket you must whitelist the IP range `104.192.143.0/24`: + +```json +[ + { + "id": "webhook", + "execute-command": "/home/adnan/redeploy-go-webhook.sh", + "command-working-directory": "/home/adnan/go", + "pass-arguments-to-command": + [ + { + "source": "payload", + "name": "actor.username" + } + ], + "trigger-rule": + { + "match": + { + "type": "ip-whitelist", + "ip-range": "104.192.143.0/24" + } + } + } +] +``` + +## Incoming Gitlab Webhook +Gitlab provides webhooks for many kinds of events. +Refer to this URL for example request body content: [gitlab-ce/integrations/webhooks](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/project/integrations/webhooks.md) +Values in the request body can be accessed in the command or to the match rule by referencing 'payload' as the source: +```json +[ + { + "id": "redeploy-webhook", + "execute-command": "/home/adnan/redeploy-go-webhook.sh", + "command-working-directory": "/home/adnan/go", + "pass-arguments-to-command": + [ + { + "source": "payload", + "name": "user_name" + } + ], + "response-message": "Executing redeploy script", + "trigger-rule": + { + "match": + { + "type": "value", + "value": "", + "parameter": + { + "source": "header", + "name": "X-Gitlab-Token" + } + } + } + } +] +``` + +## Incoming Gogs webhook +```json +[ + { + "id": "webhook", + "execute-command": "/home/adnan/redeploy-go-webhook.sh", + "command-working-directory": "/home/adnan/go", + "pass-arguments-to-command": + [ + { + "source": "payload", + "name": "head_commit.id" + }, + { + "source": "payload", + "name": "pusher.name" + }, + { + "source": "payload", + "name": "pusher.email" + } + ], + "trigger-rule": + { + "and": + [ + { + "match": + { + "type": "payload-hash-sha256", + "secret": "mysecret", + "parameter": + { + "source": "header", + "name": "X-Gogs-Signature" + } + } + }, + { + "match": + { + "type": "value", + "value": "refs/heads/master", + "parameter": + { + "source": "payload", + "name": "ref" + } + } + } + ] + } + } +] +``` +## Incoming Gitea webhook +```json +[ + { + "id": "webhook", + "execute-command": "/home/adnan/redeploy-go-webhook.sh", + "command-working-directory": "/home/adnan/go", + "pass-arguments-to-command": + [ + { + "source": "payload", + "name": "head_commit.id" + }, + { + "source": "payload", + "name": "pusher.name" + }, + { + "source": "payload", + "name": "pusher.email" + } + ], + "trigger-rule": + { + "and": + [ + { + "match": + { + "type": "value", + "value": "mysecret", + "parameter": + { + "source": "payload", + "name": "secret" + } + } + }, + { + "match": + { + "type": "value", + "value": "refs/heads/master", + "parameter": + { + "source": "payload", + "name": "ref" + } + } + } + ] + } + } +] +``` + +## Slack slash command +```json +[ + { + "id": "redeploy-webhook", + "execute-command": "/home/adnan/redeploy-go-webhook.sh", + "command-working-directory": "/home/adnan/go", + "response-message": "Executing redeploy script", + "trigger-rule": + { + "match": + { + "type": "value", + "value": "", + "parameter": + { + "source": "payload", + "name": "token" + } + } + } + } +] +``` + +## A simple webhook with a secret key in GET query + +__Not recommended in production due to low security__ + +`example.com:9000/hooks/simple-one` - won't work +`example.com:9000/hooks/simple-one?token=42` - will work + +```json +[ + { + "id": "simple-one", + "execute-command": "/path/to/command.sh", + "response-message": "Executing simple webhook...", + "trigger-rule": + { + "match": + { + "type": "value", + "value": "42", + "parameter": + { + "source": "url", + "name": "token" + } + } + } + } +] +``` + +# JIRA Webhooks +[Guide by @perfecto25](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks) + +# Pass File-to-command sample + +## Webhook configuration + +
+[
+  {
+    "id": "test-file-webhook",
+    "execute-command": "/bin/ls",
+    "command-working-directory": "/tmp",
+    "pass-file-to-command":
+    [
+      {
+      	"source": "payload",
+ 	"name": "binary",
+      	"envname": "ENV_VARIABLE", // to use $ENV_VARIABLE in execute-command
+                                   // if not defined, $HOOK_BINARY will be provided
+      	"base64decode": true,      // defaults to false
+      }
+    ],
+    "include-command-output-in-response": true
+  }
+]
+
+ +## Sample client usage + +Store the following file as `testRequest.json`. + +
+{"binary":"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjEzMTA4RDI0QzMxQjExRTBCMzYzRjY1QUQ1Njc4QzFBIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjEzMTA4RDIzQzMxQjExRTBCMzYzRjY1QUQ1Njc4QzFBIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+IBFgEwAAAmJJREFUeNqkk89rE1EQx2d/NNq0xcYYayPYJDWC9ODBsKIgAREjBmvEg2cvHnr05KHQ9iB49SL+/BMEfxBQKHgwCEbTNNIYaqgaoanFJi+rcXezye4689jYkIMIDnx47837zrx583YFx3Hgf0xA6/dJyAkkgUy4vgryAnmNWH9L4EVmotFoKplMHgoGg6PkrFarjXQ6/bFcLj/G5W1E+3NaX4KZeDx+dX5+7kg4HBlmrC6JoiDFYrGhROLM/mp1Y6JSqdCd3/SW0GUqEAjkl5ZyHTSHKBQKnO6a9khD2m5cr91IJBJ1VVWdiM/n6LruNJtNDs3JR3ukIW03SHTHi8iVsbG9I51OG1bW16HVasHQZopDc/JZVgdIQ1o3BmTkEnJXURS/KIpgGAYPkCQJPi0u8uzDKQN0XQPbtgE1MmrHs9nsfSqAEjxCNtHxZHLy4G4smUQgyzL4LzOegDGGp1ucVqsNqKVrpJCM7F4hg6iaZvhqtZrg8XjA4xnAU3XeKLqWaRImoIZeQXVjQO5pYp4xNVirsR1erxer2O4yfa227WCwhtWoJmn7m0h270NxmemFW4706zMm8GCgxBGEASCfhnukIW03iFdQnOPz0LNKp3362JqQzSw4u2LXBe+Bs3xD+/oc1NxN55RiC9fOme0LEQiRf2rBzaKEeJJ37ZWTVunBeGN2WmQjg/DeLTVP89nzAive2dMwlo9bpFVC2xWMZr+A720FVn88fAUb3wDMOjyN7YNc6TvUSHQ4AH6TOUdLL7em68UtWPsJqxgTpgeiLu1EBt1R+Me/mF7CQPTfAgwAGxY2vOTrR3oAAAAASUVORK5CYII="}
+
+ +use then the curl tool to execute a request to the webhook. + +
+#!/bin/bash
+curl -H "Content-Type:application/json" -X POST -d @testRequest.json \
+http://localhost:9000/hooks/test-file-webhook
+
+ +or in a single line, using https://github.com/jpmens/jo to generate the JSON code +
+jo binary=%filename.zip | curl -H "Content-Type:application/json" -X POST -d @- \
+http://localhost:9000/hooks/test-file-webhook
+
+ + +## Incoming Scalr Webhook +[Guide by @hassanbabaie] +Scalr makes webhook calls based on an event to a configured webhook endpoint (for example Host Down, Host Up). Webhook endpoints are URLs where Scalr will deliver Webhook notifications. +Scalr assigns a unique signing key for every configured webhook endpoint. +Refer to this URL for information on how to setup the webhook call on the Scalr side: [Scalr Wiki Webhooks](https://scalr-wiki.atlassian.net/wiki/spaces/docs/pages/6193173/Webhooks) +In order to leverage the Signing Key for addtional authentication/security you must configure the trigger rule with a match type of "scalr-signature". + +```json [ { "id": "redeploy-webhook", @@ -319,6 +374,6 @@ In order to leverage the Signing Key for addtional authentication/security you m } ] } -] - -``` \ No newline at end of file +] + +``` From f76426e9b0cfe22ec217458f57aed0086c38ba5f Mon Sep 17 00:00:00 2001 From: Adnan Hajdarevic Date: Sat, 17 Nov 2018 19:01:26 +0100 Subject: [PATCH 10/12] add handler for the route to be used as a healtcheck endpoint, fixes #233 --- hook/hook.go | 2 +- webhook.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hook/hook.go b/hook/hook.go index 721b731..d8e4635 100644 --- a/hook/hook.go +++ b/hook/hook.go @@ -426,7 +426,7 @@ type Hook struct { JSONStringParameters []Argument `json:"parse-parameters-as-json,omitempty"` TriggerRule *Rules `json:"trigger-rule,omitempty"` TriggerRuleMismatchHttpResponseCode int `json:"trigger-rule-mismatch-http-response-code,omitempty"` - IncomingPayloadContentType string `json:"incoming-payload-content-type,omitempty"` + IncomingPayloadContentType string `json:"incoming-payload-content-type,omitempty"` } // ParseJSONParameters decodes specified arguments to JSON objects and replaces the diff --git a/webhook.go b/webhook.go index 5d41077..dd5d881 100644 --- a/webhook.go +++ b/webhook.go @@ -185,6 +185,10 @@ func main() { hooksURL = "/" + *hooksURLPrefix + "/{id}" } + router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprintf(w, "OK") + }) + router.HandleFunc(hooksURL, hookHandler) n.UseHandler(router) From 1a17dc83fe580557d0065e981fa8d480a8c65de5 Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Wed, 2 Jan 2019 16:09:27 -0600 Subject: [PATCH 11/12] Return errors on empty secrets during signature validations Fixes #207 --- hook/hook.go | 12 ++++++++++++ hook/hook_test.go | 14 +++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/hook/hook.go b/hook/hook.go index d8e4635..ef03e0b 100644 --- a/hook/hook.go +++ b/hook/hook.go @@ -94,6 +94,10 @@ func (e *ParseError) Error() string { // CheckPayloadSignature calculates and verifies SHA1 signature of the given payload func CheckPayloadSignature(payload []byte, secret string, signature string) (string, error) { + if secret == "" { + return "", errors.New("signature validation secret can not be empty") + } + signature = strings.TrimPrefix(signature, "sha1=") mac := hmac.New(sha1.New, []byte(secret)) @@ -111,6 +115,10 @@ func CheckPayloadSignature(payload []byte, secret string, signature string) (str // CheckPayloadSignature256 calculates and verifies SHA256 signature of the given payload func CheckPayloadSignature256(payload []byte, secret string, signature string) (string, error) { + if secret == "" { + return "", errors.New("signature validation secret can not be empty") + } + signature = strings.TrimPrefix(signature, "sha256=") mac := hmac.New(sha256.New, []byte(secret)) @@ -134,6 +142,10 @@ func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey if _, ok := headers["Date"]; !ok { return false, nil } + if signingKey == "" { + return false, errors.New("signature validation signing key can not be empty") + } + providedSignature := headers["X-Signature"].(string) dateHeader := headers["Date"].(string) mac := hmac.New(sha1.New, []byte(signingKey)) diff --git a/hook/hook_test.go b/hook/hook_test.go index 1794a7c..bba9136 100644 --- a/hook/hook_test.go +++ b/hook/hook_test.go @@ -19,6 +19,7 @@ var checkPayloadSignatureTests = []struct { // failures {[]byte(`{"a": "z"}`), "secret", "XXXe04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", false}, {[]byte(`{"a": "z"}`), "secreX", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "900225703e9342328db7307692736e2f7cc7b36e", false}, + {[]byte(`{"a": "z"}`), "", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "", false}, } func TestCheckPayloadSignature(t *testing.T) { @@ -28,7 +29,7 @@ func TestCheckPayloadSignature(t *testing.T) { t.Errorf("failed to check payload signature {%q, %q, %q}:\nexpected {mac:%#v, ok:%#v},\ngot {mac:%#v, ok:%#v}", tt.payload, tt.secret, tt.signature, tt.mac, tt.ok, mac, (err == nil)) } - if err != nil && strings.Contains(err.Error(), tt.mac) { + if err != nil && tt.mac != "" && strings.Contains(err.Error(), tt.mac) { t.Errorf("error message should not disclose expected mac: %s", err) } } @@ -45,6 +46,7 @@ var checkPayloadSignature256Tests = []struct { {[]byte(`{"a": "z"}`), "secret", "sha256=f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", true}, // failures {[]byte(`{"a": "z"}`), "secret", "XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", false}, + {[]byte(`{"a": "z"}`), "", "XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "", false}, } func TestCheckPayloadSignature256(t *testing.T) { @@ -54,7 +56,7 @@ func TestCheckPayloadSignature256(t *testing.T) { t.Errorf("failed to check payload signature {%q, %q, %q}:\nexpected {mac:%#v, ok:%#v},\ngot {mac:%#v, ok:%#v}", tt.payload, tt.secret, tt.signature, tt.mac, tt.ok, mac, (err == nil)) } - if err != nil && strings.Contains(err.Error(), tt.mac) { + if err != nil && tt.mac != "" && strings.Contains(err.Error(), tt.mac) { t.Errorf("error message should not disclose expected mac: %s", err) } } @@ -92,6 +94,12 @@ var checkScalrSignatureTests = []struct { []byte(`{"a": "b"}`), "bilFGi4ZVZUdG+C6r0NIM9tuRq6PaG33R3eBUVhLwMAErGBaazvXe4Gq2DcJs5q+", "48e395e38ac48988929167df531eb2da00063a7d", false, }, + { + "Missing signing key", + map[string]interface{}{"Date": "Thu 07 Sep 2017 06:30:04 UTC", "X-Signature": "48e395e38ac48988929167df531eb2da00063a7d"}, + []byte(`{"a": "b"}`), "", + "48e395e38ac48988929167df531eb2da00063a7d", false, + }, } func TestCheckScalrSignature(t *testing.T) { @@ -102,7 +110,7 @@ func TestCheckScalrSignature(t *testing.T) { testCase.description, testCase.ok, valid) } - if err != nil && strings.Contains(err.Error(), testCase.expectedSignature) { + if err != nil && testCase.secret != "" && strings.Contains(err.Error(), testCase.expectedSignature) { t.Errorf("error message should not disclose expected mac: %s on test case %s", err, testCase.description) } } From f056f9430536280454d9f8220ef28ca86fdabc07 Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Wed, 2 Jan 2019 16:50:23 -0600 Subject: [PATCH 12/12] Allow multiple values for ip-whitelist Allow the value of ip-whitelist to consist of multiple space-separated addresses or CIDRs. Updates #290 --- hook/hook.go | 43 +++++++++++++++++++------------------------ hook/hook_test.go | 26 +++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/hook/hook.go b/hook/hook.go index d8e4635..665a510 100644 --- a/hook/hook.go +++ b/hook/hook.go @@ -168,41 +168,36 @@ func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey func CheckIPWhitelist(remoteAddr string, ipRange string) (bool, error) { // Extract IP address from remote address. - ip := remoteAddr + // IPv6 addresses will likely be surrounded by []. + ip := strings.Trim(remoteAddr, " []") - if strings.LastIndex(remoteAddr, ":") != -1 { - ip = remoteAddr[0:strings.LastIndex(remoteAddr, ":")] + if i := strings.LastIndex(ip, ":"); i != -1 { + ip = ip[:i] } - ip = strings.TrimSpace(ip) - - // IPv6 addresses will likely be surrounded by [], so don't forget to remove those. - - if strings.HasPrefix(ip, "[") && strings.HasSuffix(ip, "]") { - ip = ip[1 : len(ip)-1] - } - - parsedIP := net.ParseIP(strings.TrimSpace(ip)) - + parsedIP := net.ParseIP(ip) if parsedIP == nil { return false, fmt.Errorf("invalid IP address found in remote address '%s'", remoteAddr) } - // Extract IP range in CIDR form. If a single IP address is provided, turn it into CIDR form. + for _, r := range strings.Fields(ipRange) { + // Extract IP range in CIDR form. If a single IP address is provided, turn it into CIDR form. - ipRange = strings.TrimSpace(ipRange) + if !strings.Contains(r, "/") { + r = r + "/32" + } - if !strings.Contains(ipRange, "/") { - ipRange = ipRange + "/32" + _, cidr, err := net.ParseCIDR(r) + if err != nil { + return false, err + } + + if cidr.Contains(parsedIP) { + return true, nil + } } - _, cidr, err := net.ParseCIDR(ipRange) - - if err != nil { - return false, err - } - - return cidr.Contains(parsedIP), nil + return false, nil } // ReplaceParameter replaces parameter value with the passed value in the passed map diff --git a/hook/hook_test.go b/hook/hook_test.go index 1794a7c..e0c1c26 100644 --- a/hook/hook_test.go +++ b/hook/hook_test.go @@ -108,6 +108,30 @@ func TestCheckScalrSignature(t *testing.T) { } } +var checkIPWhitelistTests = []struct { + addr string + ipRange string + expect bool + ok bool +}{ + {"[ 10.0.0.1:1234 ] ", " 10.0.0.1 ", true, true}, + {"[ 10.0.0.1:1234 ] ", " 10.0.0.0 ", false, true}, + {"[ 10.0.0.1:1234 ] ", " 10.0.0.1 10.0.0.1 ", true, true}, + {"[ 10.0.0.1:1234 ] ", " 10.0.0.0/31 ", true, true}, + {" [2001:db8:1:2::1:1234] ", " 2001:db8:1::/48 ", true, true}, + {" [2001:db8:1:2::1:1234] ", " 2001:db8:1::/48 2001:db8:1::/64", true, true}, + {" [2001:db8:1:2::1:1234] ", " 2001:db8:1::/64 ", false, true}, +} + +func TestCheckIPWhitelist(t *testing.T) { + for _, tt := range checkIPWhitelistTests { + result, err := CheckIPWhitelist(tt.addr, tt.ipRange) + if (err == nil) != tt.ok || result != tt.expect { + t.Errorf("ip whitelist test failed {%q, %q}:\nwant {expect:%#v, ok:%#v},\ngot {result:%#v, ok:%#v}", tt.addr, tt.ipRange, tt.expect, tt.ok, result, err) + } + } +} + var extractParameterTests = []struct { s string params interface{} @@ -129,7 +153,7 @@ var extractParameterTests = []struct { {"a.501.b", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": "y"}, map[string]interface{}{"b": "z"}}}, "", false}, // non-existent slice index {"a.502.b", map[string]interface{}{"a": []interface{}{}}, "", false}, // non-existent slice index {"a.b.503", map[string]interface{}{"a": map[string]interface{}{"b": []interface{}{"x", "y", "z"}}}, "", false}, // trailing, non-existent slice index - {"a.b", interface{}("a"), "", false}, // non-map, non-slice input + {"a.b", interface{}("a"), "", false}, // non-map, non-slice input } func TestExtractParameter(t *testing.T) {