diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 1281c3a..421c5c3 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,7 +1,6 @@ name: 🔨 Build Test on: - push: pull_request: workflow_dispatch: @@ -15,7 +14,7 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 - name: Test run: go test ./... diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9f533f8..16a8cfb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,10 +1,8 @@ name: 🚨 CodeQL Analysis on: - workflow_dispatch: pull_request: - branches: - - dev + workflow_dispatch: jobs: analyze: diff --git a/.github/workflows/dockerhub-push.yml b/.github/workflows/dockerhub-push.yml index 2f9d972..64ddb9e 100644 --- a/.github/workflows/dockerhub-push.yml +++ b/.github/workflows/dockerhub-push.yml @@ -17,7 +17,7 @@ jobs: - name: Get Github tag id: meta run: | - echo "::set-output name=tag::$(curl --silent "https://api.github.com/repos/projectdiscovery/chaos-client/releases/latest" | jq -r .tag_name)" + curl --silent "https://api.github.com/repos/projectdiscovery/chaos-client/releases/latest" | jq -r .tag_name | xargs -I {} echo TAG={} >> $GITHUB_OUTPUT - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -37,4 +37,4 @@ jobs: context: . platforms: linux/amd64,linux/arm64,linux/arm push: true - tags: projectdiscovery/chaos-client:latest,projectdiscovery/chaos-client:${{ steps.meta.outputs.tag }} \ No newline at end of file + tags: projectdiscovery/chaos-client:latest,projectdiscovery/chaos-client:${{ steps.meta.outputs.TAG }} \ No newline at end of file diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 5baf150..433fb3d 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -1,6 +1,6 @@ name: 🙏🏻 Lint Test + on: - push: pull_request: workflow_dispatch: @@ -11,10 +11,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 - + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.18 - name: Run golangci-lint - uses: golangci/golangci-lint-action@v3.2.0 + uses: golangci/golangci-lint-action@v3.3.1 with: version: latest args: --timeout 5m - working-directory: . \ No newline at end of file + working-directory: . diff --git a/.github/workflows/release-binary.yml b/.github/workflows/release-binary.yml index 2975180..42f4de0 100644 --- a/.github/workflows/release-binary.yml +++ b/.github/workflows/release-binary.yml @@ -2,6 +2,8 @@ name: 🎉 Release Binary on: create: + tags: + - v* workflow_dispatch: jobs: @@ -21,5 +23,6 @@ jobs: args: "release --rm-dist" version: latest workdir: . - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + SLACK_WEBHOOK: "${{ secrets.RELEASE_SLACK_WEBHOOK }}" \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml index 1030715..3dc2fea 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -33,3 +33,10 @@ archives: checksum: algorithm: sha256 + +announce: + slack: + enabled: true + channel: '#release' + username: GoReleaser + message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index c269e6e..a5f41b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.19.0-alpine as build-env +FROM golang:1.19.3-alpine as build-env RUN go install -v github.com/projectdiscovery/chaos-client/cmd/chaos@latest FROM alpine:latest diff --git a/go.mod b/go.mod index 97c1ec7..ebe57da 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,11 @@ module github.com/projectdiscovery/chaos-client -go 1.17 +go 1.18 require ( github.com/json-iterator/go v1.1.12 github.com/pkg/errors v0.9.1 github.com/projectdiscovery/gologger v1.1.4 - github.com/projectdiscovery/httputil v0.0.0-20210906072657-f3a099cb20bc github.com/projectdiscovery/retryablehttp-go v1.0.2 ) @@ -14,6 +13,8 @@ require ( github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - golang.org/x/net v0.0.0-20210521195947-fe42d452be8f // indirect - golang.org/x/text v0.3.6 // indirect + github.com/projectdiscovery/ratelimit v0.0.1 + github.com/projectdiscovery/utils v0.0.3 + golang.org/x/net v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect ) diff --git a/go.sum b/go.sum index b85951b..9b9ebe1 100644 --- a/go.sum +++ b/go.sum @@ -25,25 +25,32 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= -github.com/projectdiscovery/httputil v0.0.0-20210906072657-f3a099cb20bc h1:C0L6pUvVI+sPJSBaPQJEG/HjPtg8Mgs2vEpsdrl064A= -github.com/projectdiscovery/httputil v0.0.0-20210906072657-f3a099cb20bc/go.mod h1:BueJPSPWAX11IFS6bdAqTkekiIz5Fgco5LVc1kqO9L4= +github.com/projectdiscovery/ratelimit v0.0.1 h1:GnCfbKmkLdDLXT3QS4KS0zCsuDGkoRQE0YDbTqzQmS8= +github.com/projectdiscovery/ratelimit v0.0.1/go.mod h1:zenrIElIcKg0Y9h7pMfTlw5vaI/kCl8uxXm+PfgbBSw= github.com/projectdiscovery/retryablehttp-go v1.0.2 h1:LV1/KAQU+yeWhNVlvveaYFsjBYRwXlNEq0PvrezMV0U= github.com/projectdiscovery/retryablehttp-go v1.0.2/go.mod h1:dx//aY9V247qHdsRf0vdWHTBZuBQ2vm6Dq5dagxrDYI= +github.com/projectdiscovery/utils v0.0.3 h1:pAjZTGYpnATRc6uaNACdiHv4joZ0Ml7Wpu0dudpcGfM= +github.com/projectdiscovery/utils v0.0.3/go.mod h1:ne3eSlZlUKuhjHr8FfsfGcGteCzxcbJvFBx4VDBCxK0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/net v0.0.0-20210521195947-fe42d452be8f h1:Si4U+UcgJzya9kpiEUJKQvjr512OLli+gL4poHrz93U= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= golang.org/x/net v0.0.0-20210521195947-fe42d452be8f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/runner/banner.go b/internal/runner/banner.go index d927b2d..b1cffe8 100644 --- a/internal/runner/banner.go +++ b/internal/runner/banner.go @@ -7,11 +7,11 @@ const banner = ` _____/ /_ ____ _____ _____ / ___/ __ \/ __ / __ \/ ___/ / /__/ / / / /_/ / /_/ (__ ) -\___/_/ /_/\__,_/\____/____/ v0.3.0 +\___/_/ /_/\__,_/\____/____/ v0.4.0 ` // Version is the current version of chaos -const Version = `0.3.0` +const Version = `0.4.0` // showBanner is used to show the banner to the user func showBanner() { diff --git a/pkg/chaos/chaos.go b/pkg/chaos/chaos.go index 6bcf0fd..2af4f86 100644 --- a/pkg/chaos/chaos.go +++ b/pkg/chaos/chaos.go @@ -2,23 +2,47 @@ package chaos import ( "bufio" + "context" "encoding/json" "fmt" "io" - "io/ioutil" "net/http" + "strconv" "strings" + "time" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" - pdhttputil "github.com/projectdiscovery/httputil" + "github.com/projectdiscovery/ratelimit" "github.com/projectdiscovery/retryablehttp-go" + pdhttputil "github.com/projectdiscovery/utils/http" ) // Client is a client for making requests to chaos API type Client struct { apiKey string httpClient *retryablehttp.Client + ratelimit *ratelimit.Limiter +} + +// do adds apiKey and implements rate limit +func (c *Client) do(request *retryablehttp.Request) (*http.Response, error) { + request.Header.Set("Authorization", c.apiKey) + if c.ratelimit != nil { + c.ratelimit.Take() + } + resp, err := c.httpClient.Do(request) + if err != nil { + if c.ratelimit == nil { + rl := resp.Header.Get("X-Ratelimit-Limit") + rlMax, er := strconv.Atoi(rl) + if er == nil { + // if er then ratelimit header is not present. Hence no rate limit + c.ratelimit = ratelimit.New(context.Background(), int64(rlMax), time.Minute) + } + } + } + return resp, err } // New creates a new client for chaos API communication @@ -43,15 +67,14 @@ func (c *Client) GetStatistics(req *GetStatisticsRequest) (*GetStatisticsRespons if err != nil { return nil, errors.Wrap(err, "could not create request.") } - request.Header.Set("Authorization", c.apiKey) - resp, err := c.httpClient.Do(request) + resp, err := c.do(request) if err != nil { return nil, errors.Wrap(err, "could not make request.") } if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, errors.Wrap(err, "could not read response.") } @@ -93,16 +116,15 @@ func (c *Client) GetSubdomains(req *SubdomainsRequest) chan *Result { results <- &Result{Error: errors.Wrap(err, "could not create request.")} return } - request.Header.Set("Authorization", c.apiKey) - resp, err := c.httpClient.Do(request) + resp, err := c.do(request) if err != nil { results <- &Result{Error: errors.Wrap(err, "could not make request.")} return } if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { results <- &Result{Error: errors.Wrap(err, "could not read response.")} return @@ -186,16 +208,15 @@ func (c *Client) GetBBQSubdomains(req *SubdomainsRequest) chan *BBQResult { results <- &BBQResult{Error: errors.Wrap(err, "could not create request.")} return } - request.Header.Set("Authorization", c.apiKey) - resp, err := c.httpClient.Do(request) + resp, err := c.do(request) if err != nil { results <- &BBQResult{Error: errors.Wrap(err, "could not make request.")} return } if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { results <- &BBQResult{Error: errors.Wrap(err, "could not read response.")} return @@ -238,22 +259,21 @@ func (c *Client) PutSubdomains(req *PutSubdomainsRequest) (*PutSubdomainsRespons if err != nil { return nil, errors.Wrap(err, "could not create request.") } - request.Header.Set("Authorization", c.apiKey) - resp, err := c.httpClient.Do(request) + resp, err := c.do(request) if err != nil { return nil, errors.Wrap(err, "could not make request.") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, errors.Wrap(err, "could not read response.") } return nil, InvalidStatusCodeError{StatusCode: resp.StatusCode, Message: body} } - _, _ = io.Copy(ioutil.Discard, resp.Body) + _, _ = io.Copy(io.Discard, resp.Body) return &PutSubdomainsResponse{}, nil }