Skip to content

Commit

Permalink
Merge pull request #485 from moorereason/iss234-soft-sig-errors
Browse files Browse the repository at this point in the history
Add soft signature failure support
  • Loading branch information
adnanh authored Dec 6, 2020
2 parents 9dec52c + 6d2f26d commit b6e5b11
Show file tree
Hide file tree
Showing 7 changed files with 554 additions and 6 deletions.
1 change: 1 addition & 0 deletions docs/Hook-Definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Hooks are defined as objects in the JSON or YAML hooks configuration file. Pleas
* `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.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
* `trigger-signature-soft-failures` - allow signature validation failures within Or rules; by default, signature failures are treated as errors.

## Examples
Check out [Hook examples page](Hook-Examples.md) for more complex examples of hooks.
15 changes: 14 additions & 1 deletion internal/hook/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ func (e *SignatureError) Error() string {
return fmt.Sprintf("invalid payload signature %s%s", e.Signature, empty)
}

// IsSignatureError returns whether err is of type SignatureError.
func IsSignatureError(err error) bool {
switch err.(type) {
case *SignatureError:
return true
default:
return false
}
}

// ArgumentError describes an invalid argument passed to Hook.
type ArgumentError struct {
Argument Argument
Expand Down Expand Up @@ -563,6 +573,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"`
TriggerSignatureSoftFailures bool `json:"trigger-signature-soft-failures,omitempty"`
IncomingPayloadContentType string `json:"incoming-payload-content-type,omitempty"`
SuccessHttpResponseCode int `json:"success-http-response-code,omitempty"`
HTTPMethods []string `json:"http-methods"`
Expand Down Expand Up @@ -845,7 +856,9 @@ func (r OrRule) Evaluate(req *Request) (bool, error) {
rv, err := v.Evaluate(req)
if err != nil {
if !IsParameterNodeError(err) {
return false, err
if !req.AllowSignatureErrors || (req.AllowSignatureErrors && !IsSignatureError(err)) {
return false, err
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions internal/hook/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type Request struct {

// The underlying HTTP request.
RawRequest *http.Request

// Treat signature errors as simple validate failures.
AllowSignatureErrors bool
}

func (r *Request) ParseJSONPayload() error {
Expand Down
143 changes: 143 additions & 0 deletions test/hooks.json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,149 @@
]
}
},
{
"id": "github-multi-sig",
"execute-command": "{{ .Hookecho }}",
"command-working-directory": "/",
"http-methods": ["Post "],
"include-command-output-in-response": true,
"trigger-rule-mismatch-http-response-code": 400,
"trigger-signature-soft-failures": true,
"pass-environment-to-command":
[
{
"source": "payload",
"name": "head_commit.timestamp"
}
],
"pass-arguments-to-command":
[
{
"source": "payload",
"name": "head_commit.id"
},
{
"source": "payload",
"name": "head_commit.author.email"
}
],
"trigger-rule":
{
"and":
[
"or":
[
{
"match":
{
"type": "payload-hmac-sha1",
"secret": "mysecretFAIL",
"parameter":
{
"source": "header",
"name": "X-Hub-Signature"
}
}
},
{
"match":
{
"type": "payload-hmac-sha1",
"secret": "mysecret",
"parameter":
{
"source": "header",
"name": "X-Hub-Signature"
}
}
}
],
{
"match":
{
"type": "value",
"value": "refs/heads/master",
"parameter":
{
"source": "payload",
"name": "ref"
}
}
}
]
}
},
{
"id": "github-multi-sig-fail",
"execute-command": "{{ .Hookecho }}",
"command-working-directory": "/",
"http-methods": ["Post "],
"include-command-output-in-response": true,
"trigger-rule-mismatch-http-response-code": 400,
"pass-environment-to-command":
[
{
"source": "payload",
"name": "head_commit.timestamp"
}
],
"pass-arguments-to-command":
[
{
"source": "payload",
"name": "head_commit.id"
},
{
"source": "payload",
"name": "head_commit.author.email"
}
],
"trigger-rule":
{
"and":
[
"or":
[
{
"match":
{
"type": "payload-hmac-sha1",
"secret": "mysecretFAIL",
"parameter":
{
"source": "header",
"name": "X-Hub-Signature"
}
}
},
{
"match":
{
"type": "payload-hmac-sha1",
"secret": "mysecret",
"parameter":
{
"source": "header",
"name": "X-Hub-Signature"
}
}
}
],
{
"match":
{
"type": "value",
"value": "refs/heads/master",
"parameter":
{
"source": "payload",
"name": "ref"
}
}
}
]
}
},
{
"id": "bitbucket",
"execute-command": "{{ .Hookecho }}",
Expand Down
75 changes: 75 additions & 0 deletions test/hooks.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,81 @@
name: head_commit.timestamp
command-working-directory: /

- id: github-multi-sig
http-methods:
- "Post "
trigger-rule:
and:
- or:
- match:
parameter:
source: header
name: X-Hub-Signature
secret: mysecretFAIL
type: payload-hmac-sha1
- match:
parameter:
source: header
name: X-Hub-Signature
secret: mysecret
type: payload-hmac-sha1
- match:
parameter:
source: payload
name: ref
type: value
value: refs/heads/master
include-command-output-in-response: true
trigger-rule-mismatch-http-response-code: 400
trigger-signature-soft-failures: true
execute-command: '{{ .Hookecho }}'
pass-arguments-to-command:
- source: payload
name: head_commit.id
- source: payload
name: head_commit.author.email
pass-environment-to-command:
- source: payload
name: head_commit.timestamp
command-working-directory: /

- id: github-multi-sig-fail
http-methods:
- "Post "
trigger-rule:
and:
- or:
- match:
parameter:
source: header
name: X-Hub-Signature
secret: mysecretFAIL
type: payload-hmac-sha1
- match:
parameter:
source: header
name: X-Hub-Signature
secret: mysecret
type: payload-hmac-sha1
- match:
parameter:
source: payload
name: ref
type: value
value: refs/heads/master
include-command-output-in-response: true
trigger-rule-mismatch-http-response-code: 400
execute-command: '{{ .Hookecho }}'
pass-arguments-to-command:
- source: payload
name: head_commit.id
- source: payload
name: head_commit.author.email
pass-environment-to-command:
- source: payload
name: head_commit.timestamp
command-working-directory: /

- id: bitbucket
trigger-rule:
and:
Expand Down
3 changes: 3 additions & 0 deletions webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,9 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
if matchedHook.TriggerRule == nil {
ok = true
} else {
// Save signature soft failures option in request for evaluators
req.AllowSignatureErrors = matchedHook.TriggerSignatureSoftFailures

ok, err = matchedHook.TriggerRule.Evaluate(req)
if err != nil {
if !hook.IsParameterNodeError(err) {
Expand Down
Loading

0 comments on commit b6e5b11

Please sign in to comment.