Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ slack:
slack_channel: "recon"
slack_username: "test"
slack_format: "{{data}}"
slack_icon_emoji: ":ghost:"
slack_webhook_url: "https://hooks.slack.com/services/XXXXXX"

- id: "vulns"
slack_channel: "vulns"
slack_username: "test"
slack_format: "{{data}}"
slack_icon_emoji: ":ghost:"
slack_webhook_url: "https://hooks.slack.com/services/XXXXXX"

discord:
Expand Down
43 changes: 33 additions & 10 deletions pkg/providers/slack/slack.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package slack

import (
"bytes"
"encoding/json"
"fmt"
"net/url"
"net/http"
"strings"

"github.com/containrrr/shoutrrr"
"github.com/pkg/errors"
"go.uber.org/multierr"

Expand All @@ -28,6 +29,7 @@ type Options struct {
SlackThreadTS string `yaml:"slack_thread_ts,omitempty"`
SlackToken string `yaml:"slack_token,omitempty"`
SlackFormat string `yaml:"slack_format,omitempty"`
SlackIconEmoji string `yaml:"slack_icon_emoji,omitempty"`
}

func New(options []*Options, ids []string) (*Provider, error) {
Expand All @@ -50,6 +52,7 @@ func (p *Provider) Send(message, CliFormat string) error {
for _, pr := range p.Slack {
msg := utils.FormatMessage(message, utils.SelectFormat(CliFormat, pr.SlackFormat), p.counter)

// Handle threaded messages separately
if pr.SlackThreads {
if pr.SlackToken == "" {
err := errors.Wrap(fmt.Errorf("slack_token value is required to start a thread"),
Expand All @@ -70,22 +73,42 @@ func (p *Provider) Send(message, CliFormat string) error {
continue
}
} else {
slackTokens := strings.TrimPrefix(pr.SlackWebHookURL, "https://hooks.slack.com/services/")
url := &url.URL{
Scheme: "slack",
Path: slackTokens,
// Send via webhook with emoji and username
if !strings.HasPrefix(pr.SlackWebHookURL, "https://hooks.slack.com/services/") {
err := errors.Wrap(fmt.Errorf("invalid slack webhook URL"),
fmt.Sprintf("failed to send slack notification for id: %s ", pr.ID))
SlackErr = multierr.Append(SlackErr, err)
continue
}

payload := map[string]interface{}{
"text": msg,
}
if pr.SlackUsername != "" {
payload["username"] = pr.SlackUsername
}
if pr.SlackIconEmoji != "" {
payload["icon_emoji"] = pr.SlackIconEmoji
}

err := shoutrrr.Send(url.String(), msg)
jsonPayload, err := json.Marshal(payload)
if err != nil {
err = errors.Wrap(err,
fmt.Sprintf("failed to send slack notification for id: %s ", pr.ID))
err = errors.Wrap(err, fmt.Sprintf("failed to marshal Slack payload for id: %s", pr.ID))
SlackErr = multierr.Append(SlackErr, err)
continue
}

resp, err := http.Post(pr.SlackWebHookURL, "application/json", bytes.NewBuffer(jsonPayload))
if err != nil || resp.StatusCode >= 400 {
if err == nil {
err = fmt.Errorf("received non-success status: %s", resp.Status)
}
err = errors.Wrap(err, fmt.Sprintf("failed to send slack notification for id: %s", pr.ID))
SlackErr = multierr.Append(SlackErr, err)
continue
}
}
Comment on lines +101 to 110
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Close response body and add request timeout.

The HTTP response body must be closed to prevent resource leaks. Also, consider adding a timeout to prevent hanging requests.

+client := &http.Client{
+    Timeout: 10 * time.Second,
+}
+
-resp, err := http.Post(pr.SlackWebHookURL, "application/json", bytes.NewBuffer(jsonPayload))
+resp, err := client.Post(pr.SlackWebHookURL, "application/json", bytes.NewBuffer(jsonPayload))
+if resp != nil {
+    defer resp.Body.Close()
+}
 if err != nil || resp.StatusCode >= 400 {
     if err == nil {
         err = fmt.Errorf("received non-success status: %s", resp.Status)
     }
     err = errors.Wrap(err, fmt.Sprintf("failed to send slack notification for id: %s", pr.ID))
     SlackErr = multierr.Append(SlackErr, err)
     continue
 }

Don't forget to add the time import at the top of the file.

🤖 Prompt for AI Agents
In pkg/providers/slack/slack.go around lines 101 to 110, the HTTP response body
is not closed, causing potential resource leaks, and the HTTP request lacks a
timeout, risking hanging requests. Fix this by deferring resp.Body.Close()
immediately after the POST request succeeds, and replace http.Post with an
http.Client that has a timeout set (e.g., 10 seconds). Also, add the "time"
package import at the top of the file to support the timeout configuration.

gologger.Verbose().Msgf("Slack notification sent for id: %s", pr.ID)

}
return SlackErr
}