Compare commits

..

14 Commits

Author SHA1 Message Date
Arran
0c0bf0b244
Add Gitea and Uberspace Guide (#579) 2022-02-21 13:15:17 +01:00
Marek Isalski
c7f7163aaa
Update Bitbucket example to reference Atlassian's outgoing IP subnets (#578)
* Update Bitbucket example to check all of Atlassian's outgoing IP ranges

Co-authored-by: Marek Isalski <git@maz.nu>
2022-02-14 09:24:38 +01:00
Adnan Hajdarević
36e77b1c7a
Merge pull request #567 from Prince-Mendiratta/master
Fix broken link for guide to Jira and webhook integration
2021-12-31 09:22:42 +01:00
Prince Mendiratta
5189c62651
Fix broken link for guide to Jira and webhook integration
Signed-off-by: Prince Mendiratta <prince.mendi@gmail.com>
2021-12-29 16:51:20 +05:30
Adnan Hajdarević
75f406845f
Update README.md 2021-10-11 12:47:23 +02:00
Adnan Hajdarević
105b019e2b
Merge pull request #559 from Anksus/master
Update README.md
2021-09-27 13:41:06 +02:00
Ankit_Susne
4f00a26293
Update README.md 2021-09-25 22:56:38 +05:30
Adnan Hajdarević
2a36f24269
Merge pull request #529 from benjaoming/patch-1
Clarify version number for which the example works
2021-07-29 14:20:53 +02:00
Benjamin Balder Bach
1ec494fb0d
Clarify version number for which the example works
#461 changed option name and in https://github.com/adnanh/webhook/pull/528#issuecomment-826165812, @moorereason suggests to look at old tags of example documentation. This would mean that users have to read through random old documentation to discover why their packaged version doesn't work . Suggesting that clarity in the examples is preferable.

Recall that renaming this doesn't give the user some easy exception. It just mean that the trigger isn't satisfied, so there are A LOT of options for debugging.

(which takes a lot of time to do, so that's why this information is important)
2021-04-25 13:26:02 +02:00
Adnan Hajdarević
e329b6d9ff
Merge pull request #518 from adhawkins/busybox-tests
Allow tests to run on systems that use busybox (such as Alpine)
2021-03-15 22:43:13 +01:00
Andy Hawkins
181672afcc Allow tests to run on systems that use busybox (such as Alpine) 2021-03-13 16:02:44 +00:00
Adnan Hajdarević
d523af1b6c
Fixes #497 2021-02-28 09:55:08 +01:00
Adnan Hajdarević
390e3bd772
Merge pull request #503 from TheCatLady/add-alt-docker-images
Add alternative Docker images
2021-01-29 20:08:46 +01:00
TheCatLady
21549749c0
Add alternative Docker images 2021-01-28 12:16:03 -05:00
5 changed files with 60 additions and 45 deletions

View File

@ -24,7 +24,7 @@ If you don't have time to waste configuring, hosting, debugging and maintaining
# #
<a href="https://www.hookdeck.io/?ref=adnanh-webhook"><img src="http://hajdarevic.net/hookdeck-logo.svg" height="17" alt="hookdeck" align="left" /></a> If you need a way of inspecting, monitoring and replaying webhooks without the back and forth troubleshooting, [give Hookdeck a try!](https://www.hookdeck.io/?ref=adnanh-webhook) <a href="https://www.hookdeck.com/?ref=adnanh-webhook"><img src="http://hajdarevic.net/hookdeck-logo.svg" height="17" alt="hookdeck" align="left" /></a> If you need a way of inspecting, monitoring and replaying webhooks without the back and forth troubleshooting, [give Hookdeck a try!](https://www.hookdeck.com/?ref=adnanh-webhook)
# Getting started # Getting started
## Installation ## Installation
@ -113,13 +113,17 @@ TLS version and cipher suite selection flags are available from the command line
If you want to set CORS headers, you can use the `-header name=value` flag while starting [webhook][w] to set the appropriate CORS headers that will be returned with each response. If you want to set CORS headers, you can use the `-header name=value` flag while starting [webhook][w] to set the appropriate CORS headers that will be returned with each response.
## Interested in running webhook inside of a Docker container? ## Interested in running webhook inside of a Docker container?
You can use [almir/webhook](https://hub.docker.com/r/almir/webhook/) docker image, or create your own (please read [this discussion](https://github.com/adnanh/webhook/issues/63)). You can use one of the following Docker images, or create your own (please read [this discussion](https://github.com/adnanh/webhook/issues/63)):
- [almir/webhook](https://github.com/almir/docker-webhook)
- [roxedus/webhook](https://github.com/Roxedus/docker-webhook)
- [thecatlady/webhook](https://github.com/thecatlady/docker-webhook)
## Examples ## Examples
Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples of hooks. Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples of hooks.
### Guides featuring webhook ### Guides featuring webhook
- [Webhook & JIRA](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks) by [@perfecto25](https://github.com/perfecto25) - [Plex 2 Telegram](https://gitlab.com/-/snippets/1972594) by [@psyhomb](https://github.com/psyhomb)
- [Webhook & JIRA](https://sites.google.com/site/mrxpalmeiras/more/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/) - [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) - [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/) - [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/)
@ -134,6 +138,8 @@ Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples
- [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/) - [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/)
- [Set up Automated Deployments From Github With Webhook](https://maximorlov.com/automated-deployments-from-github-with-webhook/) by [Maxim Orlov](https://twitter.com/_maximization) - [Set up Automated Deployments From Github With Webhook](https://maximorlov.com/automated-deployments-from-github-with-webhook/) by [Maxim Orlov](https://twitter.com/_maximization)
- 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) - 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)
- [Integrate automatic deployment in 20 minutes using webhooks + Nginx setup](https://anksus.me/blog/integrate-automatic-deployment-in-20-minutes-using-webhooks) by [Anksus](https://github.com/Anksus)
- [Automatically redeploy your static blog with Gitea, Uberspace & Webhook](https://by.arran.nz/posts/code/webhook-deploy/) by [Arran](https://arran.nz)
- ... - ...
- Want to add your own? Open an Issue or create a PR :-) - Want to add your own? Open an Issue or create a PR :-)

View File

@ -23,6 +23,9 @@ although the examples on this page all use the JSON format.
* [Pass string arguments to command](#pass-string-arguments-to-command) * [Pass string arguments to command](#pass-string-arguments-to-command)
## Incoming Github webhook ## Incoming Github webhook
This example works on 2.8+ versions of Webhook - if you are on a previous series, change `payload-hmac-sha1` to `payload-hash-sha1`.
```json ```json
[ [
{ {
@ -80,7 +83,7 @@ although the examples on this page all use the JSON format.
## Incoming Bitbucket webhook ## 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`: Bitbucket does not pass any secrets back to the webhook. [Per their documentation](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/#Outgoing-Connections), in order to verify that the webhook came from Bitbucket you must whitelist a set of IP ranges:
```json ```json
[ [
@ -97,11 +100,23 @@ Bitbucket does not pass any secrets back to the webhook. [Per their documentati
], ],
"trigger-rule": "trigger-rule":
{ {
"match": "or":
{ [
"type": "ip-whitelist", { "match": { "type": "ip-whitelist", "ip-range": "13.52.5.96/28" } },
"ip-range": "104.192.143.0/24" { "match": { "type": "ip-whitelist", "ip-range": "13.236.8.224/28" } },
} { "match": { "type": "ip-whitelist", "ip-range": "18.136.214.96/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "18.184.99.224/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "18.234.32.224/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "18.246.31.224/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "52.215.192.224/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "104.192.137.240/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "104.192.138.240/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "104.192.140.240/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "104.192.142.240/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "104.192.143.240/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "185.166.143.240/28" } },
{ "match": { "type": "ip-whitelist", "ip-range": "185.166.142.240/28" } }
]
} }
} }
] ]
@ -309,7 +324,7 @@ __Not recommended in production due to low security__
``` ```
## JIRA Webhooks ## JIRA Webhooks
[Guide by @perfecto25](https://sites.google.com/site/mrxpalmeiras/notes/jira-webhooks) [Guide by @perfecto25](https://sites.google.com/site/mrxpalmeiras/more/jira-webhooks)
## Pass File-to-command sample ## Pass File-to-command sample

View File

@ -394,7 +394,7 @@ func GetParameter(s string, params interface{}) (interface{}, error) {
return v, nil return v, nil
} }
// Check for dotted references // Checked for dotted references
p := strings.SplitN(s, ".", 2) p := strings.SplitN(s, ".", 2)
if pValue, ok := params.(map[string]interface{})[p[0]]; ok { if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
if len(p) > 1 { if len(p) > 1 {
@ -411,23 +411,23 @@ func GetParameter(s string, params interface{}) (interface{}, error) {
// ExtractParameterAsString extracts value from interface{} as string based on // ExtractParameterAsString extracts value from interface{} as string based on
// the passed string. Complex data types are rendered as JSON instead of the Go // the passed string. Complex data types are rendered as JSON instead of the Go
// Stringer format. // Stringer format.
func ExtractParameterAsString(s string, params interface{}) (string, interface{}, error) { func ExtractParameterAsString(s string, params interface{}) (string, error) {
pValue, err := GetParameter(s, params) pValue, err := GetParameter(s, params)
if err != nil { if err != nil {
return "", nil, err return "", err
} }
switch v := reflect.ValueOf(pValue); v.Kind() { switch v := reflect.ValueOf(pValue); v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice: case reflect.Array, reflect.Map, reflect.Slice:
r, err := json.Marshal(pValue) r, err := json.Marshal(pValue)
if err != nil { if err != nil {
return "", pValue, err return "", err
} }
return string(r), r, nil return string(r), nil
default: default:
return fmt.Sprintf("%v", pValue), pValue, nil return fmt.Sprintf("%v", pValue), nil
} }
} }
@ -442,7 +442,7 @@ type Argument struct {
// Get Argument method returns the value for the Argument's key name // Get Argument method returns the value for the Argument's key name
// based on the Argument's source // based on the Argument's source
func (ha *Argument) Get(r *Request) (string, interface{}, error) { func (ha *Argument) Get(r *Request) (string, error) {
var source *map[string]interface{} var source *map[string]interface{}
key := ha.Name key := ha.Name
@ -458,55 +458,55 @@ func (ha *Argument) Get(r *Request) (string, interface{}, error) {
source = &r.Payload source = &r.Payload
case SourceString: case SourceString:
return ha.Name, ha.Name, nil return ha.Name, nil
case SourceRawRequestBody: case SourceRawRequestBody:
return string(r.Body), r.Body, nil return string(r.Body), nil
case SourceRequest: case SourceRequest:
if r == nil || r.RawRequest == nil { if r == nil || r.RawRequest == nil {
return "", nil, errors.New("request is nil") return "", errors.New("request is nil")
} }
switch strings.ToLower(ha.Name) { switch strings.ToLower(ha.Name) {
case "remote-addr": case "remote-addr":
return r.RawRequest.RemoteAddr, r.RawRequest.RemoteAddr, nil return r.RawRequest.RemoteAddr, nil
case "method": case "method":
return r.RawRequest.Method, r.RawRequest.Method, nil return r.RawRequest.Method, nil
default: default:
return "", nil, fmt.Errorf("unsupported request key: %q", ha.Name) return "", fmt.Errorf("unsupported request key: %q", ha.Name)
} }
case SourceEntirePayload: case SourceEntirePayload:
res, err := json.Marshal(&r.Payload) res, err := json.Marshal(&r.Payload)
if err != nil { if err != nil {
return "", r.Payload, err return "", err
} }
return string(res), r.Payload, nil return string(res), nil
case SourceEntireHeaders: case SourceEntireHeaders:
res, err := json.Marshal(&r.Headers) res, err := json.Marshal(&r.Headers)
if err != nil { if err != nil {
return "", r.Headers, err return "", err
} }
return string(res), r.Headers, nil return string(res), nil
case SourceEntireQuery: case SourceEntireQuery:
res, err := json.Marshal(&r.Query) res, err := json.Marshal(&r.Query)
if err != nil { if err != nil {
return "", r.Query, err return "", err
} }
return string(res), r.Query, nil return string(res), nil
} }
if source != nil { if source != nil {
return ExtractParameterAsString(key, *source) return ExtractParameterAsString(key, *source)
} }
return "", nil, errors.New("no source for value retrieval") return "", errors.New("no source for value retrieval")
} }
// Header is a structure containing header name and it's value // Header is a structure containing header name and it's value
@ -589,7 +589,7 @@ func (h *Hook) ParseJSONParameters(r *Request) []error {
errors := make([]error, 0) errors := make([]error, 0)
for i := range h.JSONStringParameters { for i := range h.JSONStringParameters {
arg, _, err := h.JSONStringParameters[i].Get(r) arg, err := h.JSONStringParameters[i].Get(r)
if err != nil { if err != nil {
errors = append(errors, &ArgumentError{h.JSONStringParameters[i]}) errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
} else { } else {
@ -645,7 +645,7 @@ func (h *Hook) ExtractCommandArguments(r *Request) ([]string, []error) {
args = append(args, h.ExecuteCommand) args = append(args, h.ExecuteCommand)
for i := range h.PassArgumentsToCommand { for i := range h.PassArgumentsToCommand {
arg, _, err := h.PassArgumentsToCommand[i].Get(r) arg, err := h.PassArgumentsToCommand[i].Get(r)
if err != nil { if err != nil {
args = append(args, "") args = append(args, "")
errors = append(errors, &ArgumentError{h.PassArgumentsToCommand[i]}) errors = append(errors, &ArgumentError{h.PassArgumentsToCommand[i]})
@ -669,7 +669,7 @@ func (h *Hook) ExtractCommandArgumentsForEnv(r *Request) ([]string, []error) {
args := make([]string, 0) args := make([]string, 0)
errors := make([]error, 0) errors := make([]error, 0)
for i := range h.PassEnvironmentToCommand { for i := range h.PassEnvironmentToCommand {
arg, _, err := h.PassEnvironmentToCommand[i].Get(r) arg, err := h.PassEnvironmentToCommand[i].Get(r)
if err != nil { if err != nil {
errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]}) errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]})
continue continue
@ -705,7 +705,7 @@ func (h *Hook) ExtractCommandArgumentsForFile(r *Request) ([]FileParameter, []er
args := make([]FileParameter, 0) args := make([]FileParameter, 0)
errors := make([]error, 0) errors := make([]error, 0)
for i := range h.PassFileToCommand { for i := range h.PassFileToCommand {
arg, _, err := h.PassFileToCommand[i].Get(r) arg, err := h.PassFileToCommand[i].Get(r)
if err != nil { if err != nil {
errors = append(errors, &ArgumentError{h.PassFileToCommand[i]}) errors = append(errors, &ArgumentError{h.PassFileToCommand[i]})
continue continue
@ -898,8 +898,6 @@ type MatchRule struct {
const ( const (
MatchValue string = "value" MatchValue string = "value"
MatchRegex string = "regex" MatchRegex string = "regex"
MatchIsNull string = "is-null"
MatchExists string = "exists"
MatchHMACSHA1 string = "payload-hmac-sha1" MatchHMACSHA1 string = "payload-hmac-sha1"
MatchHMACSHA256 string = "payload-hmac-sha256" MatchHMACSHA256 string = "payload-hmac-sha256"
MatchHMACSHA512 string = "payload-hmac-sha512" MatchHMACSHA512 string = "payload-hmac-sha512"
@ -919,17 +917,13 @@ func (r MatchRule) Evaluate(req *Request) (bool, error) {
return CheckScalrSignature(req, r.Secret, true) return CheckScalrSignature(req, r.Secret, true)
} }
arg, rawValue, err := r.Parameter.Get(req) arg, err := r.Parameter.Get(req)
if err == nil { if err == nil {
switch r.Type { switch r.Type {
case MatchValue: case MatchValue:
return compare(arg, r.Value), nil return compare(arg, r.Value), nil
case MatchRegex: case MatchRegex:
return regexp.MatchString(r.Regex, arg) return regexp.MatchString(r.Regex, arg)
case MatchIsNull:
return rawValue == nil, nil
case MatchExists:
return true, nil
case MatchHashSHA1: case MatchHashSHA1:
log.Print(`warn: use of deprecated option payload-hash-sha1; use payload-hmac-sha1 instead`) log.Print(`warn: use of deprecated option payload-hash-sha1; use payload-hmac-sha1 instead`)
fallthrough fallthrough
@ -950,7 +944,6 @@ func (r MatchRule) Evaluate(req *Request) (bool, error) {
return err == nil, err return err == nil, err
} }
} }
return false, err return false, err
} }

View File

@ -245,7 +245,7 @@ var extractParameterTests = []struct {
func TestExtractParameter(t *testing.T) { func TestExtractParameter(t *testing.T) {
for _, tt := range extractParameterTests { for _, tt := range extractParameterTests {
value, _, err := ExtractParameterAsString(tt.s, tt.params) value, err := ExtractParameterAsString(tt.s, tt.params)
if (err == nil) != tt.ok || value != tt.value { if (err == nil) != tt.ok || value != tt.value {
t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.s, tt.value, tt.ok, value, err) t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.s, tt.value, tt.ok, value, err)
} }
@ -281,7 +281,7 @@ func TestArgumentGet(t *testing.T) {
Payload: tt.payload, Payload: tt.payload,
RawRequest: tt.request, RawRequest: tt.request,
} }
value, _, err := a.Get(r) value, err := a.Get(r)
if (err == nil) != tt.ok || value != tt.value { if (err == nil) != tt.ok || value != tt.value {
t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.source, tt.name, tt.value, tt.ok, value, err) t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.source, tt.name, tt.value, tt.ok, value, err)
} }

3
webhook_test.go Normal file → Executable file
View File

@ -33,7 +33,8 @@ func TestStaticParams(t *testing.T) {
spHeaders["Accept"] = "*/*" spHeaders["Accept"] = "*/*"
// case 2: binary with spaces in its name // case 2: binary with spaces in its name
err := os.Symlink("/bin/echo", "/tmp/with space") d1 := []byte("#!/bin/sh\n/bin/echo\n")
err := ioutil.WriteFile("/tmp/with space", d1, 0755)
if err != nil { if err != nil {
t.Fatalf("%v", err) t.Fatalf("%v", err)
} }