Skip to content

Commit

Permalink
feat: add healthcheck endpoints discovery scan
Browse files Browse the repository at this point in the history
  • Loading branch information
emmanuelgautier committed Jan 1, 2025
1 parent ca2e1fb commit a6c61b8
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 4 deletions.
4 changes: 4 additions & 0 deletions internal/cmd/printtable/wellknown_paths_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
discoverablegraphql "github.com/cerberauth/vulnapi/scan/discover/discoverable_graphql"
discoverableopenapi "github.com/cerberauth/vulnapi/scan/discover/discoverable_openapi"
exposedfiles "github.com/cerberauth/vulnapi/scan/discover/exposed_files"
"github.com/cerberauth/vulnapi/scan/discover/healthcheck"
wellknown "github.com/cerberauth/vulnapi/scan/discover/well-known"
"github.com/olekukonko/tablewriter"
)
Expand Down Expand Up @@ -41,6 +42,9 @@ func WellKnownPathsScanReport(reporter *report.Reporter) {
exposedFiles := reporter.GetScanReportByID(exposedfiles.DiscoverableFilesScanID)
rows = append(rows, wellKnownPathsFromReport(exposedFiles, "Exposed Files")...)

healthcheckEndpoints := reporter.GetScanReportByID(healthcheck.DiscoverableHealthCheckScanID)
rows = append(rows, wellKnownPathsFromReport(healthcheckEndpoints, "Health Check")...)

Check warning on line 47 in internal/cmd/printtable/wellknown_paths_table.go

View check run for this annotation

Codecov / codecov/patch

internal/cmd/printtable/wellknown_paths_table.go#L45-L47

Added lines #L45 - L47 were not covered by tests
if len(rows) == 0 {
return
}
Expand Down
36 changes: 36 additions & 0 deletions scan/discover/healthcheck/healthcheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package healthcheck

import (
"github.com/cerberauth/vulnapi/internal/auth"
"github.com/cerberauth/vulnapi/internal/operation"
"github.com/cerberauth/vulnapi/report"
"github.com/cerberauth/vulnapi/scan/discover"
)

const (
DiscoverableHealthCheckScanID = "discover.healthcheck"
DiscoverableHealthCheckScanName = "Discoverable healthcheck endpoint"
)

var issue = report.Issue{
ID: "discover.discoverable_healthcheck",
Name: "Discoverable healthcheck endpoint",

Classifications: &report.Classifications{
OWASP: report.OWASP_2023_SSRF,
},

CVSS: report.CVSS{
Version: 4.0,
Vector: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N",
Score: 0,
},
}

var healthcheckSeclistUrl = "https://raw.githubusercontent.com/cerberauth/vulnapi/main/seclist/lists/healthcheck.txt"

func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityScheme) (*report.ScanReport, error) {
vulnReport := report.NewIssueReport(issue).WithOperation(op).WithSecurityScheme(securityScheme)
r := report.NewScanReport(DiscoverableHealthCheckScanID, DiscoverableHealthCheckScanName, op)
return discover.DownloadAndScanURLs("HealthCheck", healthcheckSeclistUrl, r, vulnReport, op, securityScheme)
}
50 changes: 50 additions & 0 deletions scan/discover/healthcheck/healthcheck_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package healthcheck_test

import (
"net/http"
"testing"

"github.com/cerberauth/vulnapi/internal/auth"
"github.com/cerberauth/vulnapi/internal/operation"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/scan/discover/healthcheck"
"github.com/jarcoal/httpmock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestDiscoverableScanner_Passed_WhenNoDiscoverableHealthCheckEndpointFound(t *testing.T) {
client := request.NewClient(request.NewClientOptions{
RateLimit: 500,
})
httpmock.ActivateNonDefault(client.Client)
defer httpmock.DeactivateAndReset()

op := operation.MustNewOperation(http.MethodGet, "http://localhost:8080/", nil, client)
httpmock.RegisterResponder(op.Method, op.URL.String(), httpmock.NewBytesResponder(http.StatusNoContent, nil))
httpmock.RegisterNoResponder(httpmock.NewBytesResponder(http.StatusNotFound, nil))

report, err := healthcheck.ScanHandler(op, auth.MustNewNoAuthSecurityScheme())

require.NoError(t, err)
assert.Greater(t, httpmock.GetTotalCallCount(), 5)
assert.True(t, report.Issues[0].HasPassed())
}

func TestDiscoverableScanner_Failed_WhenOneHealthCheckEndpointFound(t *testing.T) {
client := request.NewClient(request.NewClientOptions{
RateLimit: 500,
})
httpmock.ActivateNonDefault(client.Client)
defer httpmock.DeactivateAndReset()

operation := operation.MustNewOperation(http.MethodGet, "http://localhost:8080/healthz", nil, client)
httpmock.RegisterResponder(operation.Method, operation.URL.String(), httpmock.NewBytesResponder(http.StatusOK, nil))
httpmock.RegisterNoResponder(httpmock.NewBytesResponder(http.StatusNotFound, nil))

report, err := healthcheck.ScanHandler(operation, auth.MustNewNoAuthSecurityScheme())

require.NoError(t, err)
assert.Greater(t, httpmock.GetTotalCallCount(), 0)
assert.True(t, report.Issues[0].HasFailed())
}
2 changes: 0 additions & 2 deletions scan/discover/well-known/well_known.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ const (
DiscoverableWellKnownScanName = "Discoverable well-known path"
)

type DiscoverableGraphQLPathData = discover.DiscoverData

var issue = report.Issue{
ID: "discover.discoverable_well_known",
Name: "Discoverable well-known path",
Expand Down
4 changes: 3 additions & 1 deletion scenario/discover_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
discoverablegraphql "github.com/cerberauth/vulnapi/scan/discover/discoverable_graphql"
discoverableopenapi "github.com/cerberauth/vulnapi/scan/discover/discoverable_openapi"
exposedfiles "github.com/cerberauth/vulnapi/scan/discover/exposed_files"
fingerprint "github.com/cerberauth/vulnapi/scan/discover/fingerprint"
"github.com/cerberauth/vulnapi/scan/discover/fingerprint"
"github.com/cerberauth/vulnapi/scan/discover/healthcheck"
wellknown "github.com/cerberauth/vulnapi/scan/discover/well-known"
)

Expand Down Expand Up @@ -37,6 +38,7 @@ func NewDiscoverAPIScan(method string, url string, client *request.Client, opts
urlScan.AddScanHandler(scan.NewOperationScanHandler(discoverablegraphql.DiscoverableGraphQLPathScanID, discoverablegraphql.ScanHandler))
urlScan.AddScanHandler(scan.NewOperationScanHandler(exposedfiles.DiscoverableFilesScanID, exposedfiles.ScanHandler))
urlScan.AddScanHandler(scan.NewOperationScanHandler(wellknown.DiscoverableWellKnownScanID, wellknown.ScanHandler))
urlScan.AddScanHandler(scan.NewOperationScanHandler(healthcheck.DiscoverableHealthCheckScanID, healthcheck.ScanHandler))

return urlScan, nil
}
12 changes: 12 additions & 0 deletions seclist/lists/healthcheck.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
alive
health
health/live
health/ready
health/started
healthz
healthz/live
healthz/ready
ready
status
status/live
status/ready
2 changes: 1 addition & 1 deletion seclist/lists/well-known.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
.well-known/jwks.json
.well-known/oauth-authorization-server
.well-known/openid-configuration
.well-known/openid-federation
.well-known/openid-federation

0 comments on commit a6c61b8

Please sign in to comment.