Skip to content

Commit 46abf7a

Browse files
authored
Merge pull request #667 from aquasecurity/SLK-110843-webhook-auth-headers
SLK-110843 feat(webhook): add Authorization header support for webhook outputs
2 parents dc4767c + 991e4ea commit 46abf7a

File tree

3 files changed

+133
-5
lines changed

3 files changed

+133
-5
lines changed

outputs/webhook.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ const (
1717
)
1818

1919
type WebhookOutput struct {
20-
Name string
21-
Url string
20+
Name string
21+
Url string
22+
Headers map[string][]string
2223
}
2324

2425
func (webhook *WebhookOutput) GetType() string {
@@ -47,7 +48,22 @@ func (webhook *WebhookOutput) Init() error {
4748
func (webhook *WebhookOutput) Send(content map[string]string) (data.OutputResponse, error) {
4849
log.Logger.Infof("Sending webhook to %q", webhook.Url)
4950
dataStr := content["description"] //it's not supposed to work with legacy renderer
50-
resp, err := http.Post(webhook.Url, "application/json", strings.NewReader(dataStr))
51+
52+
req, err := http.NewRequest("POST", webhook.Url, strings.NewReader(dataStr))
53+
if err != nil {
54+
log.Logger.Errorf("Sending webhook Error: %v", err)
55+
return data.OutputResponse{}, err
56+
}
57+
req.Header.Set("Content-Type", "application/json")
58+
59+
for key, values := range webhook.Headers {
60+
for _, v := range values {
61+
req.Header.Add(key, v)
62+
}
63+
}
64+
65+
client := &http.Client{}
66+
resp, err := client.Do(req)
5167
if err != nil {
5268
log.Logger.Errorf("Sending webhook Error: %v", err)
5369
return data.OutputResponse{}, err

outputs/webhook_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package outputs
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestWebhookOutput_Send_WithHeaders(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
headers map[string][]string
15+
expectedHeaders map[string]string
16+
}{
17+
{
18+
name: "Bearer token auth",
19+
headers: map[string][]string{
20+
"Authorization": {"Bearer test-token-123"},
21+
},
22+
expectedHeaders: map[string]string{
23+
"Authorization": "Bearer test-token-123",
24+
"Content-Type": "application/json",
25+
},
26+
},
27+
{
28+
name: "Basic auth",
29+
headers: map[string][]string{
30+
"Authorization": {"Basic dXNlcjpwYXNz"},
31+
},
32+
expectedHeaders: map[string]string{
33+
"Authorization": "Basic dXNlcjpwYXNz",
34+
"Content-Type": "application/json",
35+
},
36+
},
37+
{
38+
name: "No headers",
39+
headers: nil,
40+
expectedHeaders: map[string]string{
41+
"Content-Type": "application/json",
42+
},
43+
},
44+
{
45+
name: "Multiple custom headers",
46+
headers: map[string][]string{
47+
"Authorization": {"Bearer token"},
48+
"X-Custom": {"custom-value"},
49+
},
50+
expectedHeaders: map[string]string{
51+
"Authorization": "Bearer token",
52+
"X-Custom": "custom-value",
53+
"Content-Type": "application/json",
54+
},
55+
},
56+
}
57+
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
var capturedHeaders http.Header
61+
62+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
63+
capturedHeaders = r.Header
64+
w.WriteHeader(http.StatusOK)
65+
}))
66+
defer server.Close()
67+
68+
webhook := &WebhookOutput{
69+
Name: "test-webhook",
70+
Url: server.URL,
71+
Headers: tt.headers,
72+
}
73+
74+
_, err := webhook.Send(map[string]string{"description": `{"test": "data"}`})
75+
assert.NoError(t, err)
76+
77+
for key, expectedValue := range tt.expectedHeaders {
78+
assert.Equal(t, expectedValue, capturedHeaders.Get(key), "Header %s mismatch", key)
79+
}
80+
})
81+
}
82+
}
83+
84+
func TestWebhookOutput_Send_ErrorCases(t *testing.T) {
85+
t.Run("Bad URL", func(t *testing.T) {
86+
webhook := &WebhookOutput{
87+
Name: "test-webhook",
88+
Url: "not-a-valid-url://invalid",
89+
}
90+
91+
_, err := webhook.Send(map[string]string{"description": `{}`})
92+
assert.Error(t, err)
93+
})
94+
95+
t.Run("Server returns error status", func(t *testing.T) {
96+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
97+
w.WriteHeader(http.StatusUnauthorized)
98+
_, _ = w.Write([]byte("Unauthorized"))
99+
}))
100+
defer server.Close()
101+
102+
webhook := &WebhookOutput{
103+
Name: "test-webhook",
104+
Url: server.URL,
105+
}
106+
107+
_, err := webhook.Send(map[string]string{"description": `{}`})
108+
assert.Error(t, err)
109+
assert.Contains(t, err.Error(), "Unauthorized")
110+
})
111+
}

router/builders.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ func buildSplunkOutput(sourceSettings *data.OutputSettings) *outputs.SplunkOutpu
2727

2828
func buildWebhookOutput(sourceSettings *data.OutputSettings) *outputs.WebhookOutput {
2929
return &outputs.WebhookOutput{
30-
Name: sourceSettings.Name,
31-
Url: sourceSettings.Url,
30+
Name: sourceSettings.Name,
31+
Url: sourceSettings.Url,
32+
Headers: sourceSettings.Headers,
3233
}
3334
}
3435

0 commit comments

Comments
 (0)