1
0
Fork 0
mirror of https://github.com/gocsaf/csaf.git synced 2025-12-22 05:40:11 +01:00

Remote validator output (#347)

* The validator is now able to print the details of the remote validations.
---------

Co-authored-by: JanHoefelmeyer <hoefelmeyer.jan@gmail.com>
Co-authored-by: JanHoefelmeyer <Jan Höfelmeyer jhoefelmeyer@intevation.de>
Co-authored-by: Sascha L. Teichmann <sascha.teichmann@intevation.de>
This commit is contained in:
JanHoefelmeyer 2023-03-15 11:02:06 +01:00 committed by GitHub
parent 92433c1272
commit 8f87273837
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 289 additions and 70 deletions

View file

@ -26,6 +26,7 @@ type options struct {
RemoteValidator string `long:"validator" description:"URL to validate documents remotely" value-name:"URL"`
RemoteValidatorCache string `long:"validatorcache" description:"FILE to cache remote validations" value-name:"FILE"`
RemoteValidatorPresets []string `long:"validatorpreset" description:"One or more presets to validate remotely" default:"mandatory"`
Output string `short:"o" long:"output" description:"If a remote validator was used, display AMOUNT ('all', 'important' or 'short') results" value-name:"AMOUNT"`
}
func main() {
@ -68,6 +69,21 @@ func run(opts *options, files []string) error {
defer validator.Close()
}
// Select amount level of output for remote validation.
var printResult func(*csaf.RemoteValidationResult)
switch opts.Output {
case "all":
printResult = printAll
case "short":
printResult = printShort
case "important":
printResult = printImportant
case "":
printResult = noPrint
default:
return fmt.Errorf("unknown output amount %q", opts.Output)
}
for _, file := range files {
// Check if the file name is valid.
if !util.ConformingFileName(filepath.Base(file)) {
@ -95,13 +111,14 @@ func run(opts *options, files []string) error {
}
// Validate against remote validator.
if validator != nil {
validate, err := validator.Validate(doc)
rvr, err := validator.Validate(doc)
if err != nil {
return fmt.Errorf("remote validation of %q failed: %w",
file, err)
}
printResult(rvr)
var passes string
if validate {
if rvr.Valid {
passes = "passes"
} else {
passes = "does not pass"
@ -113,6 +130,137 @@ func run(opts *options, files []string) error {
return nil
}
// noPrint suppresses the output of the validation result.
func noPrint(*csaf.RemoteValidationResult) {}
// messageInstancePaths aggregates errors, warnings and infos by their
// message.
type messageInstancePaths struct {
message string
paths []string
}
// messageInstancePathsList is a list for errors, warnings or infos.
type messageInstancePathsList []messageInstancePaths
// addAll adds all errors, warnings or infos of a test.
func (mipl *messageInstancePathsList) addAll(rtrs []csaf.RemoteTestResult) {
for _, rtr := range rtrs {
mipl.add(rtr)
}
}
// add adds a test result unless it is a duplicate.
func (mipl *messageInstancePathsList) add(rtr csaf.RemoteTestResult) {
for i := range *mipl {
m := &(*mipl)[i]
// Already have this message?
if m.message == rtr.Message {
for _, path := range m.paths {
// Avoid dupes.
if path == rtr.InstancePath {
return
}
}
m.paths = append(m.paths, rtr.InstancePath)
return
}
}
*mipl = append(*mipl, messageInstancePaths{
message: rtr.Message,
paths: []string{rtr.InstancePath},
})
}
// print prints the details of the list to stdout if there are any.
func (mipl messageInstancePathsList) print(info string) {
if len(mipl) == 0 {
return
}
fmt.Println(info)
for i := range mipl {
mip := &mipl[i]
fmt.Printf(" message: %s\n", mip.message)
fmt.Println(" instance path(s):")
for _, path := range mip.paths {
fmt.Printf(" %s\n", path)
}
}
}
// printShort outputs the validation result in an aggregated version.
func printShort(rvr *csaf.RemoteValidationResult) {
var errors, warnings, infos messageInstancePathsList
for i := range rvr.Tests {
test := &rvr.Tests[i]
errors.addAll(test.Error)
warnings.addAll(test.Warning)
infos.addAll(test.Info)
}
fmt.Printf("isValid: %t\n", rvr.Valid)
errors.print("errors:")
warnings.print("warnings:")
infos.print("infos:")
}
// printImportant displays only the test results which are really relevant.
func printImportant(rvr *csaf.RemoteValidationResult) {
printRemoteValidationResult(rvr, func(rt *csaf.RemoteTest) bool {
return !rt.Valid ||
len(rt.Info) > 0 || len(rt.Error) > 0 || len(rt.Warning) > 0
})
}
// printAll displays all test results.
func printAll(rvr *csaf.RemoteValidationResult) {
printRemoteValidationResult(rvr, func(*csaf.RemoteTest) bool {
return true
})
}
// printInstanceAndMessages prints the message and the instance path of
// a test result.
func printInstanceAndMessages(info string, me []csaf.RemoteTestResult) {
if len(me) == 0 {
return
}
fmt.Printf(" %s\n", info)
for _, test := range me {
fmt.Printf(" instance path: %s\n", test.InstancePath)
fmt.Printf(" message: %s\n", test.Message)
}
}
// printRemoteValidationResult prints a filtered output of the remote validation result.
func printRemoteValidationResult(
rvr *csaf.RemoteValidationResult,
accept func(*csaf.RemoteTest) bool,
) {
fmt.Printf("isValid: %t\n", rvr.Valid)
fmt.Println("tests:")
nl := false
for i := range rvr.Tests {
test := &rvr.Tests[i]
if !accept(test) {
continue
}
if nl {
fmt.Println()
} else {
nl = true
}
fmt.Printf(" name: %s\n", test.Name)
fmt.Printf(" isValid: %t\n", test.Valid)
printInstanceAndMessages("errors:", test.Error)
printInstanceAndMessages("warnings:", test.Warning)
printInstanceAndMessages("infos:", test.Info)
}
}
func errCheck(err error) {
if err != nil {
if flags.WroteHelp(err) {