mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-22 11:55:40 +01:00
Merge pull request #506 from csaf-poc/support-legacy-security-txt
PMD: Support legacy security.txt location as fallback.
This commit is contained in:
commit
d4ef21531a
3 changed files with 96 additions and 58 deletions
|
|
@ -1262,10 +1262,26 @@ func (p *processor) checkProviderMetadata(domain string) bool {
|
||||||
// It checks the existence of the CSAF field in the file content and tries to fetch
|
// It checks the existence of the CSAF field in the file content and tries to fetch
|
||||||
// the value of this field. Returns an empty string if no error was encountered,
|
// the value of this field. Returns an empty string if no error was encountered,
|
||||||
// the errormessage otherwise.
|
// the errormessage otherwise.
|
||||||
func (p *processor) checkSecurity(domain string) string {
|
func (p *processor) checkSecurity(domain string, legacy bool) (int, string) {
|
||||||
|
folder := "https://" + domain + "/"
|
||||||
|
if !legacy {
|
||||||
|
folder = folder + ".well-known/"
|
||||||
|
}
|
||||||
|
msg := p.checkSecurityFolder(folder)
|
||||||
|
if msg == "" {
|
||||||
|
if !legacy {
|
||||||
|
return 0, "Found valid security.txt within the well-known directory"
|
||||||
|
}
|
||||||
|
return 2, "Found valid security.txt in the legacy location"
|
||||||
|
}
|
||||||
|
return 1, folder + "security.txt: " + msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkSecurityFolder checks the security.txt in a given folder.
|
||||||
|
func (p *processor) checkSecurityFolder(folder string) string {
|
||||||
|
|
||||||
client := p.httpClient()
|
client := p.httpClient()
|
||||||
path := "https://" + domain + "/.well-known/security.txt"
|
path := folder + "security.txt"
|
||||||
res, err := client.Get(path)
|
res, err := client.Get(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprintf("Fetching %s failed: %v", path, err)
|
return fmt.Sprintf("Fetching %s failed: %v", path, err)
|
||||||
|
|
@ -1298,7 +1314,7 @@ func (p *processor) checkSecurity(domain string) string {
|
||||||
return fmt.Sprintf("CSAF URL '%s' invalid: %v", u, err)
|
return fmt.Sprintf("CSAF URL '%s' invalid: %v", u, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
base, err := url.Parse("https://" + domain + "/.well-known/")
|
base, err := url.Parse(folder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
@ -1391,7 +1407,14 @@ func (p *processor) checkWellknown(domain string) string {
|
||||||
func (p *processor) checkWellknownSecurityDNS(domain string) error {
|
func (p *processor) checkWellknownSecurityDNS(domain string) error {
|
||||||
|
|
||||||
warningsW := p.checkWellknown(domain)
|
warningsW := p.checkWellknown(domain)
|
||||||
warningsS := p.checkSecurity(domain)
|
// Security check for well known (default) and legacy location
|
||||||
|
warningsS, sDMessage := p.checkSecurity(domain, false)
|
||||||
|
// if the security.txt under .well-known was not okay
|
||||||
|
// check for a security.txt within its legacy location
|
||||||
|
sLMessage := ""
|
||||||
|
if warningsS == 1 {
|
||||||
|
warningsS, sLMessage = p.checkSecurity(domain, true)
|
||||||
|
}
|
||||||
warningsD := p.checkDNS(domain)
|
warningsD := p.checkDNS(domain)
|
||||||
|
|
||||||
p.badWellknownMetadata.use()
|
p.badWellknownMetadata.use()
|
||||||
|
|
@ -1399,17 +1422,30 @@ func (p *processor) checkWellknownSecurityDNS(domain string) error {
|
||||||
p.badDNSPath.use()
|
p.badDNSPath.use()
|
||||||
|
|
||||||
var kind MessageType
|
var kind MessageType
|
||||||
if warningsS == "" || warningsD == "" || warningsW == "" {
|
if warningsS != 1 || warningsD == "" || warningsW == "" {
|
||||||
kind = WarnType
|
kind = WarnType
|
||||||
} else {
|
} else {
|
||||||
kind = ErrorType
|
kind = ErrorType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Info, Warning or Error depending on kind and warningS
|
||||||
|
kindSD := kind
|
||||||
|
if warningsS == 0 {
|
||||||
|
kindSD = InfoType
|
||||||
|
}
|
||||||
|
kindSL := kind
|
||||||
|
if warningsS == 2 {
|
||||||
|
kindSL = InfoType
|
||||||
|
}
|
||||||
|
|
||||||
if warningsW != "" {
|
if warningsW != "" {
|
||||||
p.badWellknownMetadata.add(kind, warningsW)
|
p.badWellknownMetadata.add(kind, warningsW)
|
||||||
}
|
}
|
||||||
if warningsS != "" {
|
p.badSecurity.add(kindSD, sDMessage)
|
||||||
p.badSecurity.add(kind, warningsS)
|
// only if the well-known security.txt was not successful:
|
||||||
|
// report about the legacy location
|
||||||
|
if warningsS != 0 {
|
||||||
|
p.badSecurity.add(kindSL, sLMessage)
|
||||||
}
|
}
|
||||||
if warningsD != "" {
|
if warningsD != "" {
|
||||||
p.badDNSPath.add(kind, warningsD)
|
p.badDNSPath.add(kind, warningsD)
|
||||||
|
|
|
||||||
|
|
@ -251,10 +251,6 @@ func (r *securityReporter) report(p *processor, domain *Domain) {
|
||||||
req.message(WarnType, "Performed no in-depth test of security.txt.")
|
req.message(WarnType, "Performed no in-depth test of security.txt.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(p.badSecurity) == 0 {
|
|
||||||
req.message(InfoType, "Found CSAF entry in security.txt.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Messages = p.badSecurity
|
req.Messages = p.badSecurity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,8 +132,7 @@ func (pmdl *ProviderMetadataLoader) Load(domain string) *LoadedProviderMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next load the PMDs from security.txt
|
// Next load the PMDs from security.txt
|
||||||
secURL := "https://" + domain + "/.well-known/security.txt"
|
secResults := pmdl.loadFromSecurity(domain)
|
||||||
secResults := pmdl.loadFromSecurity(secURL)
|
|
||||||
|
|
||||||
// Filter out the results which are valid.
|
// Filter out the results which are valid.
|
||||||
var secGoods []*LoadedProviderMetadata
|
var secGoods []*LoadedProviderMetadata
|
||||||
|
|
@ -199,56 +198,63 @@ func (pmdl *ProviderMetadataLoader) Load(domain string) *LoadedProviderMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadFromSecurity loads the PMDs mentioned in the security.txt.
|
// loadFromSecurity loads the PMDs mentioned in the security.txt.
|
||||||
func (pmdl *ProviderMetadataLoader) loadFromSecurity(path string) []*LoadedProviderMetadata {
|
func (pmdl *ProviderMetadataLoader) loadFromSecurity(domain string) []*LoadedProviderMetadata {
|
||||||
|
|
||||||
res, err := pmdl.client.Get(path)
|
// If .well-known fails try legacy location.
|
||||||
if err != nil {
|
for _, path := range []string{
|
||||||
pmdl.messages.Add(
|
"https://" + domain + "/.well-known/security.txt",
|
||||||
HTTPFailed,
|
"https://" + domain + "/security.txt",
|
||||||
fmt.Sprintf("Fetching %q failed: %v", path, err))
|
} {
|
||||||
return nil
|
res, err := pmdl.client.Get(path)
|
||||||
}
|
if err != nil {
|
||||||
if res.StatusCode != http.StatusOK {
|
pmdl.messages.Add(
|
||||||
pmdl.messages.Add(
|
HTTPFailed,
|
||||||
HTTPFailed,
|
fmt.Sprintf("Fetching %q failed: %v", path, err))
|
||||||
fmt.Sprintf("Fetching %q failed: %s (%d)", path, res.Status, res.StatusCode))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract all potential URLs from CSAF.
|
|
||||||
urls, err := func() ([]string, error) {
|
|
||||||
defer res.Body.Close()
|
|
||||||
return ExtractProviderURL(res.Body, true)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
pmdl.messages.Add(
|
|
||||||
HTTPFailed,
|
|
||||||
fmt.Sprintf("Loading %q failed: %v", path, err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var loaded []*LoadedProviderMetadata
|
|
||||||
|
|
||||||
// Load the URLs
|
|
||||||
nextURL:
|
|
||||||
for _, url := range urls {
|
|
||||||
lpmd := pmdl.loadFromURL(url)
|
|
||||||
// If loading failed note it down.
|
|
||||||
if !lpmd.Valid() {
|
|
||||||
pmdl.messages.AppendUnique(lpmd.Messages)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Check for duplicates
|
if res.StatusCode != http.StatusOK {
|
||||||
for _, l := range loaded {
|
pmdl.messages.Add(
|
||||||
if l == lpmd {
|
HTTPFailed,
|
||||||
continue nextURL
|
fmt.Sprintf("Fetching %q failed: %s (%d)", path, res.Status, res.StatusCode))
|
||||||
}
|
continue
|
||||||
}
|
}
|
||||||
loaded = append(loaded, lpmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
return loaded
|
// Extract all potential URLs from CSAF.
|
||||||
|
urls, err := func() ([]string, error) {
|
||||||
|
defer res.Body.Close()
|
||||||
|
return ExtractProviderURL(res.Body, true)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
pmdl.messages.Add(
|
||||||
|
HTTPFailed,
|
||||||
|
fmt.Sprintf("Loading %q failed: %v", path, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var loaded []*LoadedProviderMetadata
|
||||||
|
|
||||||
|
// Load the URLs
|
||||||
|
nextURL:
|
||||||
|
for _, url := range urls {
|
||||||
|
lpmd := pmdl.loadFromURL(url)
|
||||||
|
// If loading failed note it down.
|
||||||
|
if !lpmd.Valid() {
|
||||||
|
pmdl.messages.AppendUnique(lpmd.Messages)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Check for duplicates
|
||||||
|
for _, l := range loaded {
|
||||||
|
if l == lpmd {
|
||||||
|
continue nextURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loaded = append(loaded, lpmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return loaded
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadFromURL loads a provider metadata from a given URL.
|
// loadFromURL loads a provider metadata from a given URL.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue