mirror of https://github.com/daffainfo/nuclei.git
(feat) add Gitea reporting platform support (#4522)
* (feat) add Gitea reporting platform support * (fix) remove debugging code * (chore) fix typo * (chore) update syntax with recent changes * (fix) use Index to update issue comments * (feat) add gitea config example * added missing option --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com>dev
parent
4b55c26fc0
commit
0bd447834c
|
@ -33,6 +33,23 @@
|
|||
# # issue-label is the label of the created issue type
|
||||
# issue-label: bug
|
||||
#
|
||||
# Gitea contains configuration options for a gitea issue tracker
|
||||
#gitea:
|
||||
# # base-url is the optional self-hosted Gitea application url (defaults to https://gitea.com)
|
||||
# base-url: https://localhost:8443/
|
||||
# # token is the token for a Gitea account to use
|
||||
# token: test-token
|
||||
# # project-owner is the owner (user or org) of the repository
|
||||
# project-owner: "1234"
|
||||
# # project-name is the name of the repository
|
||||
# project-name: "1234"
|
||||
# # issue-label is a custom label to add to created issues
|
||||
# issue-label: bug
|
||||
# # severity-as-label (optional) adds the severity as a label of the created issue
|
||||
# severity-as-label: true
|
||||
# # duplicate-issue-check (optional) flag to enable duplicate tracking issue check
|
||||
# duplicate-issue-check: false
|
||||
#
|
||||
# Jira contains configuration options for Jira issue tracker
|
||||
#jira:
|
||||
# # cloud is the boolean which tells if Jira instance is running in the cloud or on-prem version is used
|
||||
|
|
3
go.mod
3
go.mod
|
@ -46,6 +46,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.17.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
|
||||
github.com/DataDog/gostackparse v0.6.0
|
||||
|
@ -130,6 +131,7 @@ require (
|
|||
github.com/containerd/continuity v0.4.2 // indirect
|
||||
github.com/corpix/uarand v0.2.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/denisbrodbeck/machineid v1.0.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
|
@ -143,6 +145,7 @@ require (
|
|||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
|
|
9
go.sum
9
go.sum
|
@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
code.gitea.io/sdk/gitea v0.17.0 h1:8JPBss4+Jf7AE1YcfyiGrngTXE8dFSG3si/bypsTH34=
|
||||
code.gitea.io/sdk/gitea v0.17.0/go.mod h1:ndkDk99BnfiUCCYEUhpNzi0lpmApXlwRFqClBlOlEBg=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
|
@ -256,6 +258,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
|
||||
|
@ -338,6 +342,8 @@ github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
|||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
|
@ -533,6 +539,7 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
|||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
|
@ -1146,8 +1153,10 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/sarif"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/splunk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/gitea"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/github"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/gitlab"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/jira"
|
||||
|
@ -23,6 +24,8 @@ type Options struct {
|
|||
GitHub *github.Options `yaml:"github"`
|
||||
// GitLab contains configuration options for GitLab Issue Tracker
|
||||
GitLab *gitlab.Options `yaml:"gitlab"`
|
||||
// Gitea contains configuration options for Gitea Issue Tracker
|
||||
Gitea *gitea.Options `yaml:"gitea"`
|
||||
// Jira contains configuration options for Jira Issue Tracker
|
||||
Jira *jira.Options `yaml:"jira"`
|
||||
// MarkdownExporter contains configuration options for Markdown Exporter Module
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/sarif"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/splunk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/gitea"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/github"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/gitlab"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/jira"
|
||||
|
@ -115,6 +116,15 @@ func New(options *Options, db string) (Client, error) {
|
|||
}
|
||||
client.trackers = append(client.trackers, tracker)
|
||||
}
|
||||
if options.Gitea != nil {
|
||||
options.Gitea.HttpClient = options.HttpClient
|
||||
options.Gitea.OmitRaw = options.OmitRaw
|
||||
tracker, err := gitea.New(options.Gitea)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Wrap(ErrReportingClientCreation)
|
||||
}
|
||||
client.trackers = append(client.trackers, tracker)
|
||||
}
|
||||
if options.Jira != nil {
|
||||
options.Jira.HttpClient = options.HttpClient
|
||||
options.Jira.OmitRaw = options.OmitRaw
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown/util"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/format"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
)
|
||||
|
||||
// Integration is a client for an issue tracker integration
|
||||
type Integration struct {
|
||||
client *gitea.Client
|
||||
options *Options
|
||||
}
|
||||
|
||||
// Options contains the configuration options for gitea issue tracker client
|
||||
type Options struct {
|
||||
// BaseURL (optional) is the self-hosted Gitea application url
|
||||
BaseURL string `yaml:"base-url" validate:"omitempty,url"`
|
||||
// Token is the token for gitea account.
|
||||
Token string `yaml:"token" validate:"required"`
|
||||
// ProjectOwner is the owner (user or org) of the repository.
|
||||
ProjectOwner string `yaml:"project-owner" validate:"required"`
|
||||
// ProjectName is the name of the repository.
|
||||
ProjectName string `yaml:"project-name" validate:"required"`
|
||||
// IssueLabel is the label of the created issue type
|
||||
IssueLabel string `yaml:"issue-label"`
|
||||
// SeverityAsLabel (optional) adds the severity as the label of the created
|
||||
// issue.
|
||||
SeverityAsLabel bool `yaml:"severity-as-label"`
|
||||
// DuplicateIssueCheck is a bool to enable duplicate tracking issue check and update the newest
|
||||
DuplicateIssueCheck bool `yaml:"duplicate-issue-check" default:"false"`
|
||||
|
||||
HttpClient *retryablehttp.Client `yaml:"-"`
|
||||
OmitRaw bool `yaml:"-"`
|
||||
}
|
||||
|
||||
// New creates a new issue tracker integration client based on options.
|
||||
func New(options *Options) (*Integration, error) {
|
||||
|
||||
var opts []gitea.ClientOption
|
||||
opts = append(opts, gitea.SetToken(options.Token))
|
||||
|
||||
if options.HttpClient != nil {
|
||||
opts = append(opts, gitea.SetHTTPClient(options.HttpClient.HTTPClient))
|
||||
}
|
||||
|
||||
var remote string
|
||||
if options.BaseURL != "" {
|
||||
parsed, err := url.Parse(options.BaseURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not parse custom baseurl")
|
||||
}
|
||||
if !strings.HasSuffix(parsed.Path, "/") {
|
||||
parsed.Path += "/"
|
||||
}
|
||||
remote = parsed.String()
|
||||
} else {
|
||||
remote = `https://gitea.com/`
|
||||
}
|
||||
|
||||
git, err := gitea.NewClient(remote, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Integration{client: git, options: options}, nil
|
||||
}
|
||||
|
||||
// CreateIssue creates an issue in the tracker
|
||||
func (i *Integration) CreateIssue(event *output.ResultEvent) error {
|
||||
summary := format.Summary(event)
|
||||
description := format.CreateReportDescription(event, util.MarkdownFormatter{}, i.options.OmitRaw)
|
||||
|
||||
labels := []string{}
|
||||
severityLabel := fmt.Sprintf("Severity: %s", event.Info.SeverityHolder.Severity.String())
|
||||
if i.options.SeverityAsLabel && severityLabel != "" {
|
||||
labels = append(labels, severityLabel)
|
||||
}
|
||||
if label := i.options.IssueLabel; label != "" {
|
||||
labels = append(labels, label)
|
||||
}
|
||||
customLabels, err := i.getLabelIDsByNames(labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var issue *gitea.Issue
|
||||
if i.options.DuplicateIssueCheck {
|
||||
issue, err = i.findIssueByTitle(summary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if issue == nil {
|
||||
_, _, err = i.client.CreateIssue(i.options.ProjectOwner, i.options.ProjectName, gitea.CreateIssueOption{
|
||||
Title: summary,
|
||||
Body: description,
|
||||
Labels: customLabels,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = i.client.CreateIssueComment(i.options.ProjectOwner, i.options.ProjectName, issue.Index, gitea.CreateIssueCommentOption{
|
||||
Body: description,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *Integration) findIssueByTitle(title string) (*gitea.Issue, error) {
|
||||
|
||||
issueList, _, err := i.client.ListRepoIssues(i.options.ProjectOwner, i.options.ProjectName, gitea.ListIssueOption{
|
||||
State: "all",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, issue := range issueList {
|
||||
if issue.Title == title {
|
||||
return issue, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (i *Integration) getLabelIDsByNames(labels []string) ([]int64, error) {
|
||||
|
||||
var ids []int64
|
||||
|
||||
existingLabels, _, err := i.client.ListRepoLabels(i.options.ProjectOwner, i.options.ProjectName, gitea.ListLabelsOptions{
|
||||
ListOptions: gitea.ListOptions{Page: -1},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
getLabel := func(name string) int64 {
|
||||
for _, existingLabel := range existingLabels {
|
||||
if existingLabel.Name == name {
|
||||
return existingLabel.ID
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
for _, label := range labels {
|
||||
labelID := getLabel(label)
|
||||
if labelID == -1 {
|
||||
newLabel, _, err := i.client.CreateLabel(i.options.ProjectOwner, i.options.ProjectName, gitea.CreateLabelOption{
|
||||
Name: label,
|
||||
Color: `#00aabb`,
|
||||
Description: label,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ids = append(ids, newLabel.ID)
|
||||
} else {
|
||||
ids = append(ids, labelID)
|
||||
}
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
Loading…
Reference in New Issue