Skip to content

Commit 3bb8ea0

Browse files
JanHoefelmeyerJan Höfelmeyers-l-teichmannbernhardreiter
authored
Improve checker regarding PMD location problems
* Change checking to test for Security, wellknown and DNS requirement at once and only throws error if all three fail. * Use security.txt parser from csaf/util to extract provider url. * Improve code comments and messages for the reports. Co-authored-by: Jan Höfelmeyer <Jan Höfelmeyer jhoefelmeyer@intevation.de> Co-authored-by: Sascha L. Teichmann <sascha.teichmann@intevation.de> Co-authored-by: Bernhard Reiter <bernhard@intevation.de>
1 parent a62484c commit 3bb8ea0

3 files changed

Lines changed: 112 additions & 104 deletions

File tree

cmd/csaf_checker/processor.go

Lines changed: 108 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ func (p *processor) domainChecks(domain string) []func(*processor, string) error
262262
}
263263

264264
if !direct {
265-
checks = append(checks, (*processor).checkSecurity)
265+
checks = append(checks, (*processor).checkWellknownSecurityDNS)
266266
}
267267

268268
checks = append(checks,
@@ -272,13 +272,6 @@ func (p *processor) domainChecks(domain string) []func(*processor, string) error
272272
(*processor).checkListing,
273273
)
274274

275-
if !direct {
276-
checks = append(checks,
277-
(*processor).checkWellknownMetadataReporter,
278-
(*processor).checkDNSPathReporter,
279-
)
280-
}
281-
282275
return checks
283276
}
284277

@@ -1084,83 +1077,159 @@ func (p *processor) checkProviderMetadata(domain string) error {
10841077

10851078
// checkSecurity checks the security.txt file by making HTTP request to fetch it.
10861079
// It checks the existence of the CSAF field in the file content and tries to fetch
1087-
// the value of this field. As a result of these a respective error messages are
1088-
// passed to the badSecurity method in case of errors.
1089-
// It returns nil if all checks are passed.
1090-
func (p *processor) checkSecurity(domain string) error {
1080+
// the value of this field. Returns an empty string if no error was encountered,
1081+
// the errormessage otherwise.
1082+
func (p *processor) checkSecurity(domain string) string {
10911083

10921084
client := p.httpClient()
1093-
p.badSecurity.use()
1094-
10951085
path := "https://" + domain + "/.well-known/security.txt"
10961086
res, err := client.Get(path)
10971087
if err != nil {
1098-
p.badSecurity.error("Fetching %s failed: %v", path, err)
1099-
return errContinue
1088+
return fmt.Sprintf("Fetching %s failed: %v", path, err)
11001089
}
11011090

11021091
if res.StatusCode != http.StatusOK {
1103-
p.badSecurity.error("Fetching %s failed. Status code %d (%s)",
1092+
return fmt.Sprintf("Fetching %s failed. Status code %d (%s)",
11041093
path, res.StatusCode, res.Status)
1105-
return errContinue
11061094
}
11071095

11081096
u, err := func() (string, error) {
11091097
defer res.Body.Close()
1110-
lines := bufio.NewScanner(res.Body)
1111-
for lines.Scan() {
1112-
line := lines.Text()
1113-
if strings.HasPrefix(line, "CSAF:") {
1114-
return strings.TrimSpace(line[6:]), nil
1115-
}
1098+
lines, err := csaf.ExtractProviderURL(res.Body, false)
1099+
var u string
1100+
if len(lines) > 0 {
1101+
u = lines[0]
11161102
}
1117-
return "", lines.Err()
1103+
return u, err
11181104
}()
11191105
if err != nil {
1120-
p.badSecurity.error("Error while reading security.txt: %v", err)
1121-
return errContinue
1106+
return fmt.Sprintf("Error while reading security.txt: %v", err)
11221107
}
11231108
if u == "" {
1124-
p.badSecurity.error("No CSAF line found in security.txt.")
1125-
return errContinue
1109+
return "No CSAF line found in security.txt."
11261110
}
11271111

11281112
// Try to load
11291113
up, err := url.Parse(u)
11301114
if err != nil {
1131-
p.badSecurity.error("CSAF URL '%s' invalid: %v", u, err)
1132-
return errContinue
1115+
return fmt.Sprintf("CSAF URL '%s' invalid: %v", u, err)
11331116
}
11341117

11351118
base, err := url.Parse("https://" + domain + "/.well-known/")
11361119
if err != nil {
1137-
return err
1120+
return err.Error()
11381121
}
11391122

11401123
u = base.ResolveReference(up).String()
11411124
p.checkTLS(u)
11421125
if res, err = client.Get(u); err != nil {
1143-
p.badSecurity.error("Cannot fetch %s from security.txt: %v", u, err)
1144-
return errContinue
1126+
return fmt.Sprintf("Cannot fetch %s from security.txt: %v", u, err)
11451127
}
11461128
if res.StatusCode != http.StatusOK {
1147-
p.badSecurity.error("Fetching %s failed. Status code %d (%s)",
1129+
return fmt.Sprintf("Fetching %s failed. Status code %d (%s)",
11481130
u, res.StatusCode, res.Status)
1149-
return errContinue
11501131
}
11511132
defer res.Body.Close()
11521133
// Compare checksums to already read provider-metadata.json.
11531134
h := sha256.New()
11541135
if _, err := io.Copy(h, res.Body); err != nil {
1155-
p.badSecurity.error("Reading %s failed: %v", u, err)
1156-
return errContinue
1136+
return fmt.Sprintf("Reading %s failed: %v", u, err)
11571137
}
11581138

11591139
if !bytes.Equal(h.Sum(nil), p.pmd256) {
1160-
p.badSecurity.error("Content of %s from security.txt is not "+
1140+
return fmt.Sprintf("Content of %s from security.txt is not "+
11611141
"identical to .well-known/csaf/provider-metadata.json", u)
11621142
}
1143+
return ""
1144+
}
1145+
1146+
// checkDNS checks if the "csaf.data.security.domain.tld" DNS record is available
1147+
// and serves the "provider-metadata.json".
1148+
// It returns an empty string if all checks are passed, otherwise the errormessage.
1149+
func (p *processor) checkDNS(domain string) string {
1150+
1151+
client := p.httpClient()
1152+
path := "https://csaf.data.security." + domain
1153+
res, err := client.Get(path)
1154+
if err != nil {
1155+
return fmt.Sprintf("Fetching %s failed: %v", path, err)
1156+
}
1157+
if res.StatusCode != http.StatusOK {
1158+
return fmt.Sprintf("Fetching %s failed. Status code %d (%s)",
1159+
path, res.StatusCode, res.Status)
1160+
1161+
}
1162+
hash := sha256.New()
1163+
defer res.Body.Close()
1164+
content, err := io.ReadAll(res.Body)
1165+
if err != nil {
1166+
return fmt.Sprintf("Error while reading the response from %s", path)
1167+
}
1168+
hash.Write(content)
1169+
if !bytes.Equal(hash.Sum(nil), p.pmd256) {
1170+
return fmt.Sprintf("%s does not serve the same provider-metadata.json as previously found", path)
1171+
}
1172+
return ""
1173+
}
1174+
1175+
// checkWellknownMetadataReporter checks if the provider-metadata.json file is
1176+
// available under the /.well-known/csaf/ directory. Returns the errormessage if
1177+
// an error was encountered, or an empty string otherwise
1178+
func (p *processor) checkWellknown(domain string) string {
1179+
1180+
client := p.httpClient()
1181+
path := "https://" + domain + "/.well-known/csaf/provider-metadata.json"
1182+
1183+
res, err := client.Get(path)
1184+
if err != nil {
1185+
return fmt.Sprintf("Fetching %s failed: %v", path, err)
1186+
}
1187+
if res.StatusCode != http.StatusOK {
1188+
return fmt.Sprintf("Fetching %s failed. Status code %d (%s)",
1189+
path, res.StatusCode, res.Status)
1190+
}
1191+
return ""
1192+
}
1193+
1194+
// checkWellknownSecurityDNS
1195+
// 1. checks if the provider-metadata.json file is
1196+
// available under the /.well-known/csaf/ directory.
1197+
// 2. Then it checks the security.txt file by making HTTP request to fetch it.
1198+
// 3. After that it checks the existence of the CSAF field in the file
1199+
// content and tries to fetch the value of this field.
1200+
// 4. Finally it checks if the "csaf.data.security.domain.tld" DNS record
1201+
// is available and serves the "provider-metadata.json".
1202+
///
1203+
// If all three checks fail, errors are given,
1204+
// otherwise warnings for all failed checks.
1205+
// The function returns nil, unless errors outside the checks were found.
1206+
// In that case, errors are returned.
1207+
func (p *processor) checkWellknownSecurityDNS(domain string) error {
1208+
1209+
warningsW := p.checkWellknown(domain)
1210+
warningsS := p.checkSecurity(domain)
1211+
warningsD := p.checkDNS(domain)
1212+
1213+
p.badWellknownMetadata.use()
1214+
p.badSecurity.use()
1215+
p.badDNSPath.use()
1216+
1217+
var kind MessageType
1218+
if warningsS == "" || warningsD == "" || warningsW == "" {
1219+
kind = WarnType
1220+
} else {
1221+
kind = ErrorType
1222+
}
11631223

1224+
if warningsW != "" {
1225+
p.badWellknownMetadata.add(kind, warningsW)
1226+
}
1227+
if warningsS != "" {
1228+
p.badSecurity.add(kind, warningsS)
1229+
}
1230+
if warningsD != "" {
1231+
p.badDNSPath.add(kind, warningsD)
1232+
}
11641233
return nil
11651234
}
11661235

@@ -1251,64 +1320,3 @@ func (p *processor) checkPGPKeys(domain string) error {
12511320
}
12521321
return nil
12531322
}
1254-
1255-
// checkWellknownMetadataReporter checks if the provider-metadata.json file is
1256-
// available under the /.well-known/csaf/ directory.
1257-
// It returns nil if all checks are passed, otherwise error.
1258-
func (p *processor) checkWellknownMetadataReporter(domain string) error {
1259-
1260-
client := p.httpClient()
1261-
1262-
p.badWellknownMetadata.use()
1263-
1264-
path := "https://" + domain + "/.well-known/csaf/provider-metadata.json"
1265-
1266-
res, err := client.Get(path)
1267-
if err != nil {
1268-
p.badWellknownMetadata.error("Fetching %s failed: %v", path, err)
1269-
return errContinue
1270-
}
1271-
if res.StatusCode != http.StatusOK {
1272-
p.badWellknownMetadata.error("Fetching %s failed. Status code %d (%s)",
1273-
path, res.StatusCode, res.Status)
1274-
return errContinue
1275-
}
1276-
1277-
return nil
1278-
}
1279-
1280-
// checkDNSPathReporter checks if the "csaf.data.security.domain.tld" DNS record is available
1281-
// and serves the "provider-metadata.json".
1282-
// It returns nil if all checks are passed, otherwise error.
1283-
func (p *processor) checkDNSPathReporter(domain string) error {
1284-
1285-
client := p.httpClient()
1286-
1287-
p.badDNSPath.use()
1288-
1289-
path := "https://csaf.data.security." + domain
1290-
res, err := client.Get(path)
1291-
if err != nil {
1292-
p.badDNSPath.error("Fetching %s failed: %v", path, err)
1293-
return errContinue
1294-
}
1295-
if res.StatusCode != http.StatusOK {
1296-
p.badDNSPath.error("Fetching %s failed. Status code %d (%s)",
1297-
path, res.StatusCode, res.Status)
1298-
return errContinue
1299-
}
1300-
hash := sha256.New()
1301-
defer res.Body.Close()
1302-
content, err := io.ReadAll(res.Body)
1303-
if err != nil {
1304-
p.badDNSPath.error("Error while reading the response from %s", path)
1305-
return errContinue
1306-
}
1307-
hash.Write(content)
1308-
if !bytes.Equal(hash.Sum(nil), p.pmd256) {
1309-
p.badDNSPath.error("%s does not serve the same provider-metadata.json as previously found", path)
1310-
return errContinue
1311-
}
1312-
1313-
return nil
1314-
}

cmd/csaf_checker/reporters.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,11 @@ func (r *wellknownMetadataReporter) report(p *processor, domain *Domain) {
156156
req.Messages = p.badWellknownMetadata
157157
}
158158

159-
// report tests if the "csaf.data.security.domain.tld" DNS record available and serves the "provider-metadata.json"
159+
// report outputs the result of the the explicit DNS test.
160160
func (r *dnsPathReporter) report(p *processor, domain *Domain) {
161161
req := r.requirement(domain)
162162
if !p.badDNSPath.used() {
163-
req.message(InfoType, "No download from https://csaf.data.security.DOMAIN attempted.")
163+
req.message(InfoType, "No check about contents from https://csaf.data.security.DOMAIN performed.")
164164
return
165165
}
166166
if len(p.badDNSPath) == 0 {

csaf/util.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func LoadProviderMetadataForDomain(
217217
// Valid provider metadata under well-known.
218218
var wellknownGood *LoadedProviderMetadata
219219

220-
// First try well-know path
220+
// First try the well-known path.
221221
wellknownURL := "https://" + domain + "/.well-known/csaf/provider-metadata.json"
222222
wellknownResult := LoadProviderMetadataFromURL(
223223
client, wellknownURL, already, logging)
@@ -249,7 +249,7 @@ func LoadProviderMetadataForDomain(
249249

250250
// security.txt contains good entries.
251251
if len(secGoods) > 0 {
252-
// we have a wellknown good take it.
252+
// we already have a good wellknown, take it.
253253
if wellknownGood != nil {
254254
// check if first of security urls is identical to wellknown.
255255
if bytes.Equal(wellknownGood.Hash, secGoods[0].Hash) {

0 commit comments

Comments
 (0)