{"id":17131238,"url":"https://github.com/phramz/webhug","last_synced_at":"2025-10-12T14:47:15.469Z","repository":{"id":57509310,"uuid":"236209065","full_name":"phramz/webhug","owner":"phramz","description":"🤗 A generic webhook dispatcher written in Golang","archived":false,"fork":false,"pushed_at":"2025-05-05T12:16:22.000Z","size":132,"stargazers_count":5,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-15T03:34:28.636Z","etag":null,"topics":["automation","continuous-integration","deployment","webhook"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phramz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-01-25T18:08:48.000Z","updated_at":"2024-06-20T12:01:06.000Z","dependencies_parsed_at":"2023-02-17T19:00:51.737Z","dependency_job_id":"2db5a314-551d-4e65-84e8-655e7cd34353","html_url":"https://github.com/phramz/webhug","commit_stats":null,"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"purl":"pkg:github/phramz/webhug","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phramz%2Fwebhug","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phramz%2Fwebhug/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phramz%2Fwebhug/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phramz%2Fwebhug/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phramz","download_url":"https://codeload.github.com/phramz/webhug/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phramz%2Fwebhug/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279011609,"owners_count":26084964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","continuous-integration","deployment","webhook"],"created_at":"2024-10-14T19:14:24.379Z","updated_at":"2025-10-12T14:47:15.445Z","avatar_url":"https://github.com/phramz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"Webhug 🤗\n==========\n\nA generic webhook dispatcher.\n\n## Build \u0026 Run\n\n``` \ngit clone https://github.com/phramz/webhug.git\nmake build\n./webhug\n\n\u003e INFO : 2020/01/25 18:25:03.520102 webhug.go:16: reading config ...\n\u003e INFO : 2020/01/25 18:25:03.521061 webhug.go:20: setting up webhook 'example' at path '/example/'\n\u003e INFO : 2020/01/25 18:25:03.521134 webhug.go:37: 🤗 webhug listening on :8080 ...\n```\n\nWith the default config you should now be able to try this:\n``` \ncurl -H 'x-auth-token: top secret' -X POST -d '{\"some\": [\"random\", \"json\"]}' http://localhost:8080/example/\n\n\u003e [example] {\"some\": [\"random\", \"json\"]}HOSTNAME=fbc56923c4e7\n\u003e [example] SHLVL=1\n\u003e [example] HOME=/root\n\u003e [example] WEBHUG_WEBHOOK=example\n\u003e [example] WEBHUG_REQUEST_HEADER={\"Accept\":[\"*/*\"],\"Content-Length\":[\"28\"],\"Content-Type\":[\"application/x-www-form-urlencoded\"],\"User-Agent\":[\"curl/7.64.1\"],\"X-Auth-Token\":[\"top secret\"]}\n\u003e [example] RELEASE_VERSION=d770d49e68\n\u003e [example] PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n\u003e [example] CUSTOM_ENV_VAR1=hello\n\u003e [example] WEBHUG_REQUEST_REMOTE_ADDR=172.17.0.1:48240\n\u003e [example] CUSTOM_ENV_VAR2=world!\n\u003e [example] WEBHUG_REQUEST_METHOD=POST\n\u003e [example] PWD=/etc/webhug\n\n```\n\n## Docker\n\nThere is also a docker image available. If you prefer running webhug inside a container try this:\n``` \ndocker run --rm -p 8080:8080 gregthebunny/webhug\n```\n\nWithin the image the default config is located at `/etc/webhug/config.yaml`. To provide your own config you\nmight either mount it into the container\n``` \ndocker run --rm -p 8080:8080 -v /path/to/your/config.yaml:/etc/webhug/config.yaml gregthebunny/webhug\n```\n\nor build your on image eg:\n\n```Dockerfile\nFROM gregthebunny/webhug\n\nCOPY /path/to/your/config.yaml /etc/webhug/config.yaml\n```\n\nAs the image comes with a docker-in-docker setup (`FROM docker:19.03`) it enables you to\nrun docker commands within your actions. To do so you need to mount your hosts docker socket into the \ncontainer:\n``` \ndocker run --rm -p 8080:8080 \\\n    -v /path/to/your/config.yaml:/etc/webhug/config.yaml \\\n    -v /var/run/docker.sock:/var/run/docker.sock \\\n    gregthebunny/webhug \n```\n    \n## Config\n\nAll configuration has to be done in the `config.yaml` file. A working example can be found \nin the project root:\n\n```yaml\nwebhug:\n  listen: \":8080\"\n  webhooks:\n    # \"example\" is the name of the webhook as well as the the endpoint uri eg:\n    # curl -H 'x-auth-token: top secret' -X POST -d '{\"some\": [\"random\", \"json\"]}' http://localhost:8080/example/\n    example:\n      security:\n        # one of \"header\" (match header value), \"github\" (match x-hub-signature), \"none\" (no security at all), \"deny\" (deny all)\n        type: header\n        key: x-auth-token\n        value: top secret\n      action:\n        # one of \"shell\" (execute shell command), \"none\" (do nothing)\n        type: shell\n        # if true stdout will be returned in the http response\n        response: true\n        # the executable to run\n        cmd: \"/bin/sh\"\n        # arguments that will be passed along to the executable\n        args: [\"-c\", \"cat; env;\"]\n        # some extra environment variables that will be injected.\n        env:\n          - \"CUSTOM_ENV_VAR1=hello\"\n          - \"CUSTOM_ENV_VAR2=world!\"\n\n    # ... of course you can configure more than one webhook!\n    # This one here uses githubstyle authentication:\n    # curl -H 'x-hub-signature: sha1=01f8da98455877d8beefe6e8276d59ac102857d5' -X POST -d '{\"some\": [\"random\", \"json\"]}' http://localhost:8080/example-github/\n    example-github:\n      security:\n        type: github\n        secret: super secret\n      action:\n        type: shell\n        response: true\n        cmd: \"/bin/sh\"\n        args: [\"-c\", \"cat; env;\"]\n        env:\n          - \"CUSTOM_ENV_VAR1=hello\"\n          - \"CUSTOM_ENV_VAR2=world!\"\n\n    # this example shows how to pass along values from the request:\n    # curl -v -X POST -d '{\"some\": [{\"random\": \"value\", \"nested\": {\"value\": \"json\"}}]}'  -H 'Content-type: application/json' http://localhost:8080/example-with-template-insecure/\n    #\n    # WARNING: If your planing to use values from request as arguments\n    #          beware of command-injection attacks. Take security measures, eg proper escaping etc\n    #          https://owasp.org/www-community/attacks/Command_Injection\n    #          NEVER DO IT THIS WAY!! It is just an example to show what is possible.\n    example-with-template-insecure:\n      security:\n        type: none\n      action:\n        type: shell\n        response: true\n        cmd: \"/bin/sh\"\n        args:\n          - '-c'\n          - |\n              echo 'CUSTOM_ENV_VAR_COMMAND=$CUSTOM_ENV_VAR_COMMAND';\n              echo 'Request from: {{ .Request.RemoteAddr }}';\n              echo 'Content-type: {{ index .Request.Header \"content-type\" }}';\n              echo 'Body: {{ .Request.Body }}';\n              echo 'Value: \"{{ index .Request.Json.some 0 \"random\" }}\" from \"{{ index .Request.Json.some 0 \"nested\" \"value\" }}\"';\n        env:\n          - 'CUSTOM_ENV_VAR_COMMAND={{ index .Env \"PWD\" }}'\n\n    # ... this example shows two more secure ways of doing the same\n    # curl -v -X POST -d '{\"some\": [{\"random\": \"value\", \"nested\": {\"value\": \"json\"}}]}'  -H 'Content-type: application/json' http://localhost:8080/example-with-template/\n    example-with-template:\n      security:\n        type: none\n      action:\n        type: shell\n        response: true\n        cmd: \"/bin/sh\"\n        args:\n          - '-c'\n          - |\n              # either using environment variables like this\n              echo CUSTOM_ENV_VAR_COMMAND=${CUSTOM_ENV_VAR_COMMAND};\n              echo Request from: ${CUSTOM_ENV_VAR_REQUEST_FROM};\n              echo Content-type: ${CUSTOM_ENV_VAR_REQUEST_CTYPE};\n              echo Body: ${CUSTOM_ENV_VAR_REQUEST_BODY};\n              echo Value: ${CUSTOM_ENV_VAR_REQUEST_VALUE};\n\n              # or take the arguments directly\n              echo CUSTOM_ENV_VAR_COMMAND=${0};\n              echo Request from: ${1};\n              echo Content-type: ${2};\n              echo Body: ${3};\n              echo Value: ${4};\n\n          - '{{ index .Env \"PWD\" }}'\n          - '{{ .Request.RemoteAddr }};'\n          - '{{ index .Request.Header \"content-type\" }}'\n          - '{{ .Request.Body }}'\n          - '\"{{ index .Request.Json.some 0 \"random\" }}\" from \"{{ index .Request.Json.some 0 \"nested\" \"value\" }}\"'\n        env:\n          - 'CUSTOM_ENV_VAR_COMMAND={{ index .Env \"PWD\" }}'\n          - 'CUSTOM_ENV_VAR_REQUEST_FROM={{ .Request.RemoteAddr }}'\n          - 'CUSTOM_ENV_VAR_REQUEST_CTYPE={{ index .Request.Header \"content-type\" }}'\n          - 'CUSTOM_ENV_VAR_REQUEST_BODY={{ .Request.Body }}'\n          - 'CUSTOM_ENV_VAR_REQUEST_VALUE=\"{{ index .Request.Json.some 0 \"random\" }}\" from \"{{ index .Request.Json.some 0 \"nested\" \"value\" }}\"'\n``` \n\n### Security\n\nThe security section lets you configure the authentication for each webhook.\n\n#### Deny\n\nThis will deny every request. Might be useful to temporary disable a certain webhook:\n```yaml\nwebhug:\n  webhooks:\n    example:\n      security:\n        type: deny\n``` \n#### None\n\n*Please do not run any \"open\" webhooks in production .. nasty things can happen!*\n\nThe following example will leave your webhook open for everyone: \n```yaml\nwebhug:\n  webhooks:\n    example:\n      security:\n        type: none\n``` \n#### Header\n\nThe most basic way of access control lets you define a shared secret to be present in the request header.\nThis is not recommended but better then nothing. Always make sure that you make your endpoint SSL only!\n\n```yaml\nwebhug:\n  webhooks:\n    example:\n      security:\n        type: header\n        key: x-any-header-you-like\n        value: abc123topsecrettoken\n``` \n\nTrigger your webhook like that:\n``` \ncurl -H 'x-any-header-you-like: abc123topsecrettoken' -X POST http://localhost:8080/example/\n```\n\n#### Github\n\nThis will check for githubs `X-Hub-Signature` header and validates the digest using the shared secret:\n```yaml\nwebhug:\n  webhooks:\n    example:\n      security:\n        type: github\n        secret: 123456789abcdefghi\n``` \n\nFurther readings:\n- https://developer.github.com/webhooks/securing/\n- https://developer.github.com/webhooks/\n\n### Actions\n\nBy now there is only one action supported with more to come.\n\n#### Shell\n\nThis will execute the given command and passes the request body to stdin (see example). The following\nenvironment variables will be available by default:\n\n- `WEBHUG_WEBHOOK`: Name of the webhook which triggered the action\n- `WEBHUG_REQUEST_METHOD`: The request method\n- `WEBHUG_REQUEST_REMOTE_ADDR`: The remote address which triggered the action\n\n\n### Templating\n\nSome config keys will be interpolated to add some further flexibility to your configuration.\nThe string interpolation will be done via Golang templating. If your not familiar with the syntax this\ncheat sheet might come in handy:\nhttps://curtisvermeeren.github.io/2017/09/14/Golang-Templates-Cheatsheet\n\nThe actual keys which values could be templated the following:\n- `webhug.webhooks.\u003c*\u003e.security[type=header].key`\n- `webhug.webhooks.\u003c*\u003e.security[type=header].value`\n- `webhug.webhooks.\u003c*\u003e.security[type=github].secret`\n- `webhug.webhooks.\u003c*\u003e.action[type=shell].args.\u003c*\u003e`\n- `webhug.webhooks.\u003c*\u003e.action[type=shell].env.\u003c*\u003e`\n\nDuring rendering you have access to this data:\n```go\nstruct {\n    // all environment variable of the context webhug is running in are here\n    Env            map[string]string\n    Webhook        struct {\n        Name       string\n        Format     string\n    }\n    Request        struct {\n        Body       string\n        Method     string\n        // if a POST request comes with a json content-type header\n        // the body will be automatically deserialized and could be access via\n        // this property. \n        Json       interface{}\n        Uri        string\n        Host       string\n        RemoteAddr string\n        Query      string\n        Scheme     string\n        Username   string\n        Password   string\n        // all headers lowercase CanonicalMIMEHeaderKeys \n        Header     map[string]string\n        // all variables from the requests query string\n        Get        map[string][]string\n        // see https://golang.org/pkg/net/http/#Cookie\n        Cookie     map[string]*http.Cookie\n    }\n}\n```\n\n## Github Actions\n\nIf you want to trigger Webhugs out of your Github Actions you might find this useful:\nhttps://github.com/phramz/webhug-action\n\n## License\n``` \nThe MIT License (MIT)\n\nCopyright (c) 2020 Maximilian Reichel \u003cinfo@phramz.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphramz%2Fwebhug","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphramz%2Fwebhug","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphramz%2Fwebhug/lists"}