Path: blob/dev/pkg/reporting/exporters/splunk/splunkhec.go
2070 views
package splunk12import (3"bytes"4"crypto/tls"5"fmt"6"io"7"net"8"net/http"9"time"1011"github.com/pkg/errors"12"github.com/projectdiscovery/nuclei/v3/pkg/output"13"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"14"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"15"github.com/projectdiscovery/retryablehttp-go"16"github.com/projectdiscovery/useragent"17)1819// Options contains necessary options required for splunk communication20type Options struct {21// Host is the hostname and port of the splunk instance22Host string `yaml:"host" validate:"required"`23Port int `yaml:"port" validate:"gte=0,lte=65535"`24// SSL (optional) enables ssl for splunk connection25SSL bool `yaml:"ssl"`26// SSLVerification (optional) disables SSL verification for splunk27SSLVerification bool `yaml:"ssl-verification"`28// Token for HEC instance29Token string `yaml:"token" validate:"required"`30IndexName string `yaml:"index-name" validate:"required"`3132HttpClient *retryablehttp.Client `yaml:"-"`33ExecutionId string `yaml:"-"`34}3536type data struct {37Event *output.ResultEvent `json:"event"`38}3940// Exporter type for splunk41type Exporter struct {42url string43authentication string44splunk *http.Client45}4647// New creates and returns a new exporter for splunk48func New(option *Options) (*Exporter, error) {49var ei *Exporter5051dialers := protocolstate.GetDialersWithId(option.ExecutionId)52if dialers == nil {53return nil, fmt.Errorf("dialers not initialized for %s", option.ExecutionId)54}5556var client *http.Client57if option.HttpClient != nil {58client = option.HttpClient.HTTPClient59} else {60client = &http.Client{61Timeout: 5 * time.Second,62Transport: &http.Transport{63MaxIdleConns: 10,64MaxIdleConnsPerHost: 10,65DialContext: dialers.Fastdialer.Dial,66DialTLSContext: dialers.Fastdialer.DialTLS,67TLSClientConfig: &tls.Config{InsecureSkipVerify: option.SSLVerification},68},69}70}7172// preparing url for splunk73scheme := "http://"74if option.SSL {75scheme = "https://"76}7778// Authentication header for HEC79authentication := "Splunk " + option.Token8081// add HEC endpoint, index, source, sourcetype82addr := option.Host83if option.Port > 0 {84addr = net.JoinHostPort(addr, fmt.Sprint(option.Port))85}86base_url := fmt.Sprintf("%s%s", scheme, addr)87sourcetype := "nuclei:splunk-hec:exporter:json"88url := fmt.Sprintf("%s/services/collector/event?index=%s&sourcetype=%s&source=%s", base_url, option.IndexName, sourcetype, base_url)8990ei = &Exporter{91url: url,92authentication: authentication,93splunk: client,94}95return ei, nil96}9798// Export exports a passed result event to Splunk99func (exporter *Exporter) Export(event *output.ResultEvent) error {100// creating a request101req, err := http.NewRequest(http.MethodPost, exporter.url, nil)102if err != nil {103return errors.Wrap(err, "could not make request")104}105if len(exporter.authentication) > 0 {106req.Header.Add("Authorization", exporter.authentication)107}108userAgent := useragent.PickRandom()109req.Header.Set("User-Agent", userAgent.Raw)110req.Header.Add("Content-Type", "application/json")111112d := data{Event: event}113b, err := json.Marshal(&d)114if err != nil {115return err116}117req.Body = io.NopCloser(bytes.NewReader(b))118119res, err := exporter.splunk.Do(req)120if err != nil {121return err122}123124b, err = io.ReadAll(res.Body)125if err != nil {126return errors.New(err.Error() + "error thrown by splunk " + string(b))127}128if res.StatusCode >= http.StatusMultipleChoices {129return errors.New("splunk responded with an error: " + string(b))130}131return nil132}133134// Close closes the exporter after operation135func (exporter *Exporter) Close() error {136return nil137}138139140