Skip to content

Commit

Permalink
feat: make discoverable scans running in parallel
Browse files Browse the repository at this point in the history
ci: add discovery scan tests
  • Loading branch information
emmanuelgautier committed Dec 20, 2024
1 parent 7d548a8 commit 3afb431
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
run: go build -v ./...

- name: Test
run: go test -race -coverprofile=coverage.out -covermode=atomic ./...
run: go test -coverprofile=coverage.out -covermode=atomic ./...

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/scans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,37 @@ permissions:
packages: read

jobs:
run-api-discovery:
name: API Discovery
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/discoverable:latest

- name: Setup Go environment
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}

- name: VulnAPI
id: vulnapi
run: |
go run main.go discover api http://localhost:8080 --sqa-opt-out
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/discoverable:latest)

run-jwt-scans:
name: JWT Scans
runs-on: ubuntu-latest
Expand Down
7 changes: 4 additions & 3 deletions internal/operation/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ func getBody(body io.Reader) ([]byte, error) {
}

func NewOperation(method string, operationUrl string, body io.Reader, client *request.Client) (*Operation, error) {
if client == nil {
client = request.GetDefaultClient()
operationClient := client
if operationClient == nil {
operationClient = request.GetDefaultClient()
}

parsedUrl, err := url.Parse(operationUrl)
Expand All @@ -81,7 +82,7 @@ func NewOperation(method string, operationUrl string, body io.Reader, client *re
}

return &Operation{
Client: client,
Client: operationClient,

Method: method,
URL: *parsedUrl,
Expand Down
56 changes: 40 additions & 16 deletions scan/discover/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,50 @@ func ExtractBaseURL(inputURL *url.URL) *url.URL {

func ScanURLs(scanUrls []string, op *operation.Operation, securityScheme *auth.SecurityScheme, r *report.ScanReport, vulnReport *report.IssueReport) (*report.ScanReport, error) {
securitySchemes := []*auth.SecurityScheme{securityScheme}

base := ExtractBaseURL(&op.URL)
for _, path := range scanUrls {
newOperation, err := operation.NewOperation(http.MethodGet, base.ResolveReference(&url.URL{Path: path}).String(), nil, op.Client)
newOperation.SetSecuritySchemes(securitySchemes)
if err != nil {
return r, err
}
chunkSize := 20
results := make(chan *scan.IssueScanAttempt, len(scanUrls))
errors := make(chan error, len(scanUrls))

attempt, err := scan.ScanURL(newOperation, securityScheme)
if err != nil {
return r, err
for i := 0; i < len(scanUrls); i += chunkSize {
end := i + chunkSize
if end > len(scanUrls) {
end = len(scanUrls)
}
chunk := scanUrls[i:end]

go func(chunk []string) {
for _, path := range chunk {
newOperation, err := operation.NewOperation(http.MethodGet, base.ResolveReference(&url.URL{Path: path}).String(), nil, op.Client)
newOperation.SetSecuritySchemes(securitySchemes)
if err != nil {
errors <- err
return
}

Check warning on line 46 in scan/discover/utils.go

View check run for this annotation

Codecov / codecov/patch

scan/discover/utils.go#L44-L46

Added lines #L44 - L46 were not covered by tests

r.AddScanAttempt(attempt)
if attempt.Response.GetStatusCode() == http.StatusOK { // TODO: check if the response contains the expected content
r.WithData(DiscoverData{
URL: attempt.Request.GetURL(),
}).AddIssueReport(vulnReport.Fail()).End()
return r, nil
attempt, err := scan.ScanURL(newOperation, securityScheme)
if err != nil {
errors <- err
return
}

results <- attempt
}
}(chunk)
}

for i := 0; i < len(scanUrls); i++ {
select {
case attempt := <-results:
r.AddScanAttempt(attempt)
if attempt.Response.GetStatusCode() == http.StatusOK { // TODO: check if the response contains the expected content
r.WithData(DiscoverData{
URL: attempt.Request.GetURL(),
}).AddIssueReport(vulnReport.Fail()).End()
return r, nil
}
case err := <-errors:
return r, err
}
}

Expand Down

0 comments on commit 3afb431

Please sign in to comment.