mirror of
https://github.com/lunchcat/sif.git
synced 2026-07-28 14:37:01 -07:00
fix(scan): grade hsts on the final response scheme after redirects (#310)
hsts grading was gated on the scheme of the originally requested url, but the client follows redirects, so an http target that redirects to https skipped the hsts check entirely and dropped a high-severity finding. decide the scheme from the final response request url instead, falling back to the requested url only when no response request is set.
This commit is contained in:
@@ -78,7 +78,7 @@ func SecurityHeaders(url string, timeout time.Duration, logdir string) (Security
|
||||
// header-only scan: drain on close so the conn is returned to the pool.
|
||||
defer httpx.DrainClose(resp)
|
||||
|
||||
results := gradeSecurityHeaders(resp.Header, strings.HasPrefix(url, "https://"))
|
||||
results := gradeSecurityHeaders(resp.Header, responseIsHTTPS(resp, url))
|
||||
|
||||
for _, r := range results {
|
||||
line := r.Header + " " + r.Note
|
||||
@@ -96,6 +96,18 @@ func SecurityHeaders(url string, timeout time.Duration, logdir string) (Security
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// responseIsHTTPS reports whether the response was actually served over
|
||||
// https. the client follows redirects, so a request that started as
|
||||
// http:// can end up served over https:// (or vice versa); the final
|
||||
// scheme lives on resp.Request.URL, not the originally requested url.
|
||||
// falls back to the requested url's scheme if that's unavailable.
|
||||
func responseIsHTTPS(resp *http.Response, requestedURL string) bool {
|
||||
if resp != nil && resp.Request != nil && resp.Request.URL != nil {
|
||||
return resp.Request.URL.Scheme == "https"
|
||||
}
|
||||
return strings.HasPrefix(requestedURL, "https://")
|
||||
}
|
||||
|
||||
func gradeSecurityHeaders(header http.Header, https bool) SecurityHeaderResults {
|
||||
var results SecurityHeaderResults
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ package scan
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -151,6 +152,59 @@ func TestGradeSecurityHeaders_Disclosure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponseIsHTTPS_UsesFinalRequestScheme(t *testing.T) {
|
||||
httpURL, err := url.Parse("http://example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
httpsURL, err := url.Parse("https://example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
resp *http.Response
|
||||
requestedURL string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
// the requested url was http, but the client followed a redirect
|
||||
// to https; hsts must be graded against the actual scheme.
|
||||
name: "redirected from http to https",
|
||||
resp: &http.Response{Request: &http.Request{URL: httpsURL}},
|
||||
requestedURL: "http://example.com",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "redirected from https to http",
|
||||
resp: &http.Response{Request: &http.Request{URL: httpURL}},
|
||||
requestedURL: "https://example.com",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no redirect, stays http",
|
||||
resp: &http.Response{Request: &http.Request{URL: httpURL}},
|
||||
requestedURL: "http://example.com",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no request on response falls back to requested url",
|
||||
resp: &http.Response{},
|
||||
requestedURL: "https://example.com",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := responseIsHTTPS(tt.resp, tt.requestedURL); got != tt.want {
|
||||
t.Errorf("responseIsHTTPS() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecurityHeaders_LiveResponse(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
|
||||
|
||||
Reference in New Issue
Block a user