Skip to content

Commit

Permalink
Merge pull request #42 from cerberauth/scan-http-headers
Browse files Browse the repository at this point in the history
feat: add http headers best practices scan
  • Loading branch information
emmanuelgautier authored Feb 12, 2024
2 parents 77b2005 + 0eabfbe commit 991cb93
Show file tree
Hide file tree
Showing 6 changed files with 457 additions and 13 deletions.
12 changes: 7 additions & 5 deletions cmd/scan/curl.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,19 @@ func NewCURLScanCmd() (scanCmd *cobra.Command) {
log.Fatal(err)
}

rpr, _, err := scan.WithAllVulnsScans().Execute()
rpr, _, err := scan.WithAllVulnsScans().WithAllBestPracticesScans().Execute()
if err != nil {
log.Fatal(err)
}

if !rpr.HasVulnerability() {
log.Println("Congratulations! No vulnerability has been discovered!")
for _, r := range rpr.GetVulnerabilityReports() {
log.Println(r)
}

for _, r := range rpr.GetVulnerabilityReports() {
log.Fatalln(r)
if !rpr.HasVulnerability() {
log.Println("Congratulations! No vulnerability has been discovered!")
} else {
log.Fatalln("There is one or more vulnerabilies you should know.")
}
},
}
Expand Down
12 changes: 7 additions & 5 deletions cmd/scan/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,19 @@ func NewOpenAPIScanCmd() (scanCmd *cobra.Command) {
log.Fatal(err)
}

rpr, _, err := scan.WithAllVulnsScans().Execute()
rpr, _, err := scan.WithAllVulnsScans().WithAllBestPracticesScans().Execute()
if err != nil {
log.Fatal(err)
}

if !rpr.HasVulnerability() {
log.Println("Congratulations! No vulnerability has been discovered!")
for _, r := range rpr.GetVulnerabilityReports() {
log.Println(r)
}

for _, r := range rpr.GetVulnerabilityReports() {
log.Fatalln(r)
if !rpr.HasVulnerability() {
log.Println("Congratulations! No vulnerability has been discovered!")
} else {
log.Fatalln("There is one or more vulnerabilies you should know.")
}
},
}
Expand Down
13 changes: 13 additions & 0 deletions scan/best_practices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package scan

import (
bestpractices "github.com/cerberauth/vulnapi/scan/best_practices"
)

func (s *Scan) WithHTTPHeadersBestPracticesScan() *Scan {
return s.AddScanHandler(bestpractices.HTTPHeadersBestPracticesScanHandler)
}

func (s *Scan) WithAllBestPracticesScans() *Scan {
return s.WithHTTPHeadersBestPracticesScan()
}
151 changes: 151 additions & 0 deletions scan/best_practices/http_headers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package bestpractices

import (
"net/http"
"strings"

"github.com/cerberauth/vulnapi/internal/auth"
restapi "github.com/cerberauth/vulnapi/internal/rest_api"
"github.com/cerberauth/vulnapi/report"
)

const (
CSPHTTPHeader = "Content-Security-Policy"
HSTSHTTPHeader = "Strict-Transport-Security"
CORSOriginHTTPHeader = "Access-Control-Allow-Origin"
XContentTypeOptionsHTTPHeader = "X-Content-Type-Options"
XFrameOptionsHTTPHeader = "X-Frame-Options"
)

const (
CSPHTTPHeaderSeverityLevel = 1
CSPHTTPHeaderIsNotSetVulnerabilityName = "CSP Header is not set"
CSPHTTPHeaderIsNotSetVulnerabilityDescription = "No Content Security Policy (CSP) Header has been detected in HTTP Response."
CSPHTTPHeaderFrameAncestorsIsNotSetVulnerabilityName = "CSP frame-ancestors policy is not set"
CSPHTTPHeaderFrameAncestorsIsNotSetVulnerabilityDescription = "No frame-ancestors policy has been set in CSP HTTP Response Header."

HSTSHTTPHeaderSeverityLevel = 1
HSTSHTTPHeaderIsNotSetVulnerabilityName = "HSTS Header is not set"
HSTSHTTPHeaderIsNotSetVulnerabilityDescription = "No HSTS Header has been detected in HTTP Response."

CORSHTTPHeaderSeverityLevel = 1
CORSHTTPHeaderIsNotSetVulnerabilityName = "CORS Header is not set"
CORSHTTPHeaderIsNotSetVulnerabilityDescription = "No CORS Header has been detected in HTTP Response."
CORSHTTPHeaderIsPermisiveVulnerabilityName = "CORS Header is set but permissive"
CORSHTTPHeaderIsPermisiveVulnerabilityDescription = "CORS Header has been detected in HTTP Response but is permissive."

XContentTypeOptionsHTTPHeaderIsNotSetSeverityLevel = 1
XContentTypeOptionsHTTPHeaderIsNotSetVulnerabilityName = "X-Content-Type-Options Header is not set"
XContentTypeOptionsHTTPHeaderIsNotSetVulnerabilityDescription = "No X-Content-Type-Options Header has been detected in HTTP Response."

XFrameOptionsHTTPHeaderIsNotSetSeverityLevel = 1
XFrameOptionsHTTPHeaderIsNotSetVulnerabilityName = "X-Frame-Options Header is not set"
XFrameOptionsHTTPHeaderIsNotSetVulnerabilityDescription = "No X-Frame-Options Header has been detected in HTTP Response."
)

func checkCSPHeader(o *auth.Operation, headers http.Header, r *report.ScanReport) bool {
cspHeader := headers.Get(CSPHTTPHeader)
if cspHeader == "" {
r.AddVulnerabilityReport(&report.VulnerabilityReport{
SeverityLevel: CSPHTTPHeaderSeverityLevel,
Name: CSPHTTPHeaderIsNotSetVulnerabilityName,
Description: CSPHTTPHeaderIsNotSetVulnerabilityDescription,
Url: o.Url,
})

return false
}

directives := strings.Split(cspHeader, ";")
for _, directive := range directives {
directive = strings.TrimSpace(directive)
if strings.HasPrefix(directive, "frame-ancestors") {
// Check if frame-ancestors directive is not equal to "none"
if strings.Contains(directive, "none") {
return true
}
}
}

r.AddVulnerabilityReport(&report.VulnerabilityReport{
SeverityLevel: CSPHTTPHeaderSeverityLevel,
Name: CSPHTTPHeaderFrameAncestorsIsNotSetVulnerabilityName,
Description: CSPHTTPHeaderFrameAncestorsIsNotSetVulnerabilityDescription,
Url: o.Url,
})

return false
}

func CheckCORSAllowOrigin(o *auth.Operation, headers http.Header, r *report.ScanReport) bool {
allowOrigin := headers.Get(CORSOriginHTTPHeader)
if allowOrigin == "" {
r.AddVulnerabilityReport(&report.VulnerabilityReport{
SeverityLevel: CORSHTTPHeaderSeverityLevel,
Name: CORSHTTPHeaderIsNotSetVulnerabilityName,
Description: CORSHTTPHeaderIsNotSetVulnerabilityDescription,
Url: o.Url,
})

return false
}

// Check if the Access-Control-Allow-Origin header is not "*" (wildcard)
if allowOrigin != "*" {
return true
}

r.AddVulnerabilityReport(&report.VulnerabilityReport{
SeverityLevel: CORSHTTPHeaderSeverityLevel,
Name: CORSHTTPHeaderIsPermisiveVulnerabilityName,
Description: CORSHTTPHeaderIsPermisiveVulnerabilityDescription,
Url: o.Url,
})

return false
}

func HTTPHeadersBestPracticesScanHandler(o *auth.Operation, ss auth.SecurityScheme) (*report.ScanReport, error) {
r := report.NewScanReport()
token := ss.GetValidValue().(string)

ss.SetAttackValue(token)
vsa := restapi.ScanRestAPI(o, ss)
r.AddScanAttempt(vsa).End()

if vsa.Err != nil {
return r, vsa.Err
}

checkCSPHeader(o, vsa.Response.Header, r)
CheckCORSAllowOrigin(o, vsa.Response.Header, r)

if hstsHeader := vsa.Response.Header.Get(HSTSHTTPHeader); hstsHeader == "" {
r.AddVulnerabilityReport(&report.VulnerabilityReport{
SeverityLevel: HSTSHTTPHeaderSeverityLevel,
Name: HSTSHTTPHeaderIsNotSetVulnerabilityName,
Description: HSTSHTTPHeaderIsNotSetVulnerabilityDescription,
Url: o.Url,
})
}

if xContentTypeOptionsHeader := vsa.Response.Header.Get(XContentTypeOptionsHTTPHeader); xContentTypeOptionsHeader == "" {
r.AddVulnerabilityReport(&report.VulnerabilityReport{
SeverityLevel: XContentTypeOptionsHTTPHeaderIsNotSetSeverityLevel,
Name: XContentTypeOptionsHTTPHeaderIsNotSetVulnerabilityName,
Description: XContentTypeOptionsHTTPHeaderIsNotSetVulnerabilityDescription,
Url: o.Url,
})
}

if xFrameOptionsHeader := vsa.Response.Header.Get(XFrameOptionsHTTPHeader); xFrameOptionsHeader == "" {
r.AddVulnerabilityReport(&report.VulnerabilityReport{
SeverityLevel: XFrameOptionsHTTPHeaderIsNotSetSeverityLevel,
Name: XFrameOptionsHTTPHeaderIsNotSetVulnerabilityName,
Description: XFrameOptionsHTTPHeaderIsNotSetVulnerabilityDescription,
Url: o.Url,
})
}

return r, nil
}
Loading

0 comments on commit 991cb93

Please sign in to comment.