From 3e7fe696b4bb59f3d998873b1d0f8f88011cd60b Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Mon, 25 May 2020 11:26:06 -0400 Subject: [PATCH 01/16] Add a -summary option to print a short summary of the linting Linting the test file `testdata/utf8ControlX88.pem` results in: ``` +-------+--------------+ | LEVEL | # OCCURANCES | +-------+--------------+ | info | 0 | | warn | 7 | | error | 15 | | fatal | 0 | +-------+--------------+ ``` --- v2/cmd/zlint/main.go | 49 +++++++++++++++++++++++++++++++++++++++++++- v2/go.mod | 1 + v2/go.sum | 4 ++++ v2/lint/result.go | 6 +++--- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 679dbed81..103470944 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -27,6 +27,7 @@ import ( "sort" "strings" + "github.com/olekukonko/tablewriter" log "github.com/sirupsen/logrus" "github.com/zmap/zcrypto/x509" "github.com/zmap/zlint/v2" @@ -36,6 +37,7 @@ import ( var ( // flags listLintsJSON bool listLintSources bool + summary bool prettyprint bool format string nameFilter string @@ -51,6 +53,7 @@ var ( // flags func init() { flag.BoolVar(&listLintsJSON, "list-lints-json", false, "Print lints in JSON format, one per line") flag.BoolVar(&listLintSources, "list-lints-source", false, "Print list of lint sources, one per line") + flag.BoolVar(&summary, "summary", false, "Prints a short human-readable summary report") flag.StringVar(&format, "format", "pem", "One of {pem, der, base64}") flag.StringVar(&nameFilter, "nameFilter", "", "Only run lints with a name matching the provided regex. (Can not be used with -includeNames/-excludeNames)") flag.StringVar(&includeNames, "includeNames", "", "Comma-separated list of lints to include by name") @@ -58,7 +61,7 @@ func init() { flag.StringVar(&includeSources, "includeSources", "", "Comma-separated list of lint sources to include") flag.StringVar(&excludeSources, "excludeSources", "", "Comma-separated list of lint sources to exclude") - flag.BoolVar(&prettyprint, "pretty", false, "Pretty-print output") + flag.BoolVar(&prettyprint, "pretty", false, "Pretty-print JSON output") flag.Usage = func() { fmt.Fprintf(os.Stderr, "ZLint version %s\n\n", version) fmt.Fprintf(os.Stderr, "Usage: %s [flags] file...\n", os.Args[0]) @@ -156,6 +159,8 @@ func doLint(inputFile *os.File, inform string, registry lint.Registry) { log.Fatalf("can't format output: %s", err) } os.Stdout.Write(out.Bytes()) + } else if summary { + outputSummary(zlintResult) } else { os.Stdout.Write(jsonBytes) } @@ -210,3 +215,45 @@ func setLints() (lint.Registry, error) { return lint.GlobalRegistry().Filter(filterOpts) } + +func outputSummary(zlintResult *zlint.ResultSet) { + var sortedLevels []int + resultCount := make(map[lint.LintStatus]int) + lintLevelsAboveThreshold := make(map[int]lint.LintStatus) + // Set the threashold under which (inclusive) events are not + // counted + threshold := lint.Pass + + // Make the list of lint levels that matter + for _, i := range lint.StatusLabelToLintStatus { + if i <= threshold { + continue + } + lintLevelsAboveThreshold[int(i)] = i + } + // Set all of the levels to 0 events to they are all displayed + // in the end + for _, level := range lintLevelsAboveThreshold { + resultCount[level] = 0 + } + // Count up the number of each event + for _, value := range zlintResult.Results { + if value.Status > threshold { + resultCount[value.Status]++ + } + } + // Sort the levels we have so we can get a nice output + for key, _ := range resultCount { + sortedLevels = append(sortedLevels, int(key)) + } + sort.Ints(sortedLevels) + // Make a table of the events and print it nicely + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Level", "# occurances"}) + + for _, level := range sortedLevels { + table.Append([]string{fmt.Sprintf("%s", lint.LintStatus(level)), + fmt.Sprintf("%d", resultCount[lint.LintStatus(level)])}) + } + table.Render() +} diff --git a/v2/go.mod b/v2/go.mod index a46b43b73..47117f2ac 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -1,6 +1,7 @@ module github.com/zmap/zlint/v2 require ( + github.com/olekukonko/tablewriter v0.0.4 github.com/sirupsen/logrus v1.3.0 github.com/weppos/publicsuffix-go v0.4.0 github.com/zmap/zcrypto v0.0.0-20200513165325-16679db567ff diff --git a/v2/go.sum b/v2/go.sum index 97bc98488..b6d326922 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -8,7 +8,11 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= +github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/v2/lint/result.go b/v2/lint/result.go index fd9a1680d..96c43ab00 100644 --- a/v2/lint/result.go +++ b/v2/lint/result.go @@ -42,10 +42,10 @@ const ( ) var ( - // statusLabelToLintStatus is used to work backwards from + // StatusLabelToLintStatus is used to work backwards from // a LintStatus.String() to the LintStatus. This is used by // LintStatus.Unmarshal. - statusLabelToLintStatus = map[string]LintStatus{ + StatusLabelToLintStatus = map[string]LintStatus{ Reserved.String(): Reserved, NA.String(): NA, NE.String(): NE, @@ -73,7 +73,7 @@ func (e LintStatus) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the json.Unmarshaler interface. func (e *LintStatus) UnmarshalJSON(data []byte) error { key := strings.ReplaceAll(string(data), `"`, "") - if status, ok := statusLabelToLintStatus[key]; ok { + if status, ok := StatusLabelToLintStatus[key]; ok { *e = status } else { return fmt.Errorf("bad LintStatus JSON value: %s", string(data)) From dbb33329c96a0618c3ec6b88853aaed74201418f Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Mon, 25 May 2020 15:55:32 -0400 Subject: [PATCH 02/16] Added -longsummary option and output Running: ```sh testdata/indivValAllBad.pem | ./zlint -longsummary ``` the output is: ``` +-------+--------------+------------------------------------------+ | LEVEL | # OCCURANCES | DETAILS | +-------+--------------+------------------------------------------+ | info | 0 | - | | warn | 1 | w_ext_san_critical_with_subject_dn | | error | 7 | e_ca_crl_sign_not_set | | | | e_sub_ca_crl_distribution_points_missing | | | | e_ca_country_name_missing | | | | e_cert_policy_iv_requires_country | | | | e_sub_cert_not_is_ca | | | | e_ca_key_cert_sign_not_set | | | | e_ca_organization_name_missing | | fatal | 0 | - | +-------+--------------+------------------------------------------+ ``` --- v2/cmd/zlint/main.go | 65 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 103470944..7866a5e43 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -25,6 +25,7 @@ import ( "os" "regexp" "sort" + "strconv" "strings" "github.com/olekukonko/tablewriter" @@ -38,6 +39,7 @@ var ( // flags listLintsJSON bool listLintSources bool summary bool + longsummary bool prettyprint bool format string nameFilter string @@ -54,6 +56,7 @@ func init() { flag.BoolVar(&listLintsJSON, "list-lints-json", false, "Print lints in JSON format, one per line") flag.BoolVar(&listLintSources, "list-lints-source", false, "Print list of lint sources, one per line") flag.BoolVar(&summary, "summary", false, "Prints a short human-readable summary report") + flag.BoolVar(&longsummary, "longsummary", false, "Prints a human-readable summary report with details") flag.StringVar(&format, "format", "pem", "One of {pem, der, base64}") flag.StringVar(&nameFilter, "nameFilter", "", "Only run lints with a name matching the provided regex. (Can not be used with -includeNames/-excludeNames)") flag.StringVar(&includeNames, "includeNames", "", "Comma-separated list of lints to include by name") @@ -160,7 +163,9 @@ func doLint(inputFile *os.File, inform string, registry lint.Registry) { } os.Stdout.Write(out.Bytes()) } else if summary { - outputSummary(zlintResult) + outputSummary(zlintResult, false) + } else if longsummary { + outputSummary(zlintResult, true) } else { os.Stdout.Write(jsonBytes) } @@ -216,9 +221,10 @@ func setLints() (lint.Registry, error) { return lint.GlobalRegistry().Filter(filterOpts) } -func outputSummary(zlintResult *zlint.ResultSet) { +func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { var sortedLevels []int resultCount := make(map[lint.LintStatus]int) + resultDetails := make(map[lint.LintStatus][]string) lintLevelsAboveThreshold := make(map[int]lint.LintStatus) // Set the threashold under which (inclusive) events are not // counted @@ -232,14 +238,20 @@ func outputSummary(zlintResult *zlint.ResultSet) { lintLevelsAboveThreshold[int(i)] = i } // Set all of the levels to 0 events to they are all displayed - // in the end + // in the -summary table for _, level := range lintLevelsAboveThreshold { resultCount[level] = 0 } // Count up the number of each event - for _, value := range zlintResult.Results { + for key, value := range zlintResult.Results { if value.Status > threshold { resultCount[value.Status]++ + if longSummary { + resultDetails[value.Status] = append( + resultDetails[value.Status], + string(key), + ) + } } } // Sort the levels we have so we can get a nice output @@ -247,13 +259,44 @@ func outputSummary(zlintResult *zlint.ResultSet) { sortedLevels = append(sortedLevels, int(key)) } sort.Ints(sortedLevels) - // Make a table of the events and print it nicely - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Level", "# occurances"}) + // make the requested table type + if longSummary { + // make a table with the internal lint names grouped + // by type + longTable := tablewriter.NewWriter(os.Stdout) + longTable.SetHeader([]string{"Level", "# occurances", "Details"}) + longTable.SetAutoMergeCells(true) + for _, level := range sortedLevels { + foundDetail := false + for _, detail := range resultDetails[lint.LintStatus(level)] { + longTable.Append([]string{ + fmt.Sprintf("%s", lint.LintStatus(level)), + strconv.Itoa(resultCount[lint.LintStatus(level)]), + detail, + }) + foundDetail = true + } + if !foundDetail { + longTable.Append([]string{ + fmt.Sprintf("%s", lint.LintStatus(level)), + strconv.Itoa(resultCount[lint.LintStatus(level)]), + " - ", + }) + } + } + longTable.Render() + } else { + // Make a table of the count of each error type and + // print it nicely + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Level", "# occurances"}) - for _, level := range sortedLevels { - table.Append([]string{fmt.Sprintf("%s", lint.LintStatus(level)), - fmt.Sprintf("%d", resultCount[lint.LintStatus(level)])}) + for _, level := range sortedLevels { + table.Append([]string{ + fmt.Sprintf("%s", lint.LintStatus(level)), + strconv.Itoa(resultCount[lint.LintStatus(level)]), + }) + } + table.Render() } - table.Render() } From eef788b245c9a86feca890774c7a60115201dd4b Mon Sep 17 00:00:00 2001 From: Zakir Durumeric Date: Mon, 25 May 2020 15:27:51 -0500 Subject: [PATCH 03/16] spelling fix --- v2/cmd/zlint/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 7866a5e43..2c102201b 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -264,7 +264,7 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { // make a table with the internal lint names grouped // by type longTable := tablewriter.NewWriter(os.Stdout) - longTable.SetHeader([]string{"Level", "# occurances", "Details"}) + longTable.SetHeader([]string{"Level", "# occurrences", "Details"}) longTable.SetAutoMergeCells(true) for _, level := range sortedLevels { foundDetail := false @@ -289,7 +289,7 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { // Make a table of the count of each error type and // print it nicely table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Level", "# occurances"}) + table.SetHeader([]string{"Level", "# occurrences"}) for _, level := range sortedLevels { table.Append([]string{ From 31c7bdc75a501d0f9745316de7dc9dd48df879f4 Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Wed, 27 May 2020 21:24:26 -0400 Subject: [PATCH 04/16] Remove tablewriter dependency and reimplement the good parts --- v2/cmd/zlint/main.go | 96 +++++++++++++++++++++++++++++++++++--------- v2/go.mod | 1 - v2/go.sum | 4 -- 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 7866a5e43..02798ea05 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -27,8 +27,8 @@ import ( "sort" "strconv" "strings" + "unicode/utf8" - "github.com/olekukonko/tablewriter" log "github.com/sirupsen/logrus" "github.com/zmap/zcrypto/x509" "github.com/zmap/zlint/v2" @@ -263,40 +263,96 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { if longSummary { // make a table with the internal lint names grouped // by type - longTable := tablewriter.NewWriter(os.Stdout) - longTable.SetHeader([]string{"Level", "# occurances", "Details"}) - longTable.SetAutoMergeCells(true) + var olsl string + var orescount int + headings := []string{ + "Level", + "# occurrences", + " Details ", + } + lines := [][]string{} + lsl := "" + rescount := "" + + hlengths := printTableHeadings(headings) + // Construct the table lines, but don't repeat + // LintStatus(level) or the results count. Also, just + // because a level wasn't seen doesn't mean it isn't + // important; display "empty" levels, too for _, level := range sortedLevels { foundDetail := false for _, detail := range resultDetails[lint.LintStatus(level)] { - longTable.Append([]string{ - fmt.Sprintf("%s", lint.LintStatus(level)), - strconv.Itoa(resultCount[lint.LintStatus(level)]), - detail, - }) + if fmt.Sprintf("%s", lint.LintStatus(level)) != olsl { + olsl = fmt.Sprintf("%s", lint.LintStatus(level)) + lsl = olsl + } else { + lsl = "" + } + if resultCount[lint.LintStatus(level)] != orescount { + orescount = resultCount[lint.LintStatus(level)] + rescount = strconv.Itoa(orescount) + } else { + rescount = "" + } + lines = append(lines, ([]string{lsl, rescount, detail})) foundDetail = true } if !foundDetail { - longTable.Append([]string{ + lines = append(lines, []string{ fmt.Sprintf("%s", lint.LintStatus(level)), strconv.Itoa(resultCount[lint.LintStatus(level)]), " - ", }) } } - longTable.Render() + printTableBody(hlengths, lines) } else { - // Make a table of the count of each error type and - // print it nicely - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Level", "# occurances"}) - + headings := []string{"Level", "# occurrences"} + hlengths := printTableHeadings(headings) + lines := [][]string{} for _, level := range sortedLevels { - table.Append([]string{ + lines = append(lines, []string{ fmt.Sprintf("%s", lint.LintStatus(level)), - strconv.Itoa(resultCount[lint.LintStatus(level)]), - }) + strconv.Itoa(resultCount[lint.LintStatus(level)])}) + } + printTableBody(hlengths, lines) + } +} + +func printTableHeadings(headings []string) []int { + hlengths := []int{} + for i, h := range headings { + hlengths = append( + hlengths, + utf8.RuneCountInString(h)+1) + fmt.Printf("| %s ", strings.ToUpper(h)) + if i == len(headings)-1 { + fmt.Printf("|\n") + for ii, j := range hlengths { + fmt.Printf("+%s", strings.Repeat("-", j+1)) + if ii == len(headings)-1 { + fmt.Printf("+\n") + } + } } - table.Render() } + return hlengths +} + +func printTableBody(hlengths []int, lines [][]string) { + + for _, line := range lines { + for i, hlen := range hlengths { + // This makes a format string with the + // right widths, e.g. "%7.7s" + fmtstring := fmt.Sprintf("|%%%[1]d.%[1]ds", hlen) + fmt.Printf(fmtstring, line[i]) + if i == len(hlengths)-1 { + fmt.Printf(" |\n") + } else { + fmt.Printf(" ") + } + } + } + } diff --git a/v2/go.mod b/v2/go.mod index 47117f2ac..a46b43b73 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -1,7 +1,6 @@ module github.com/zmap/zlint/v2 require ( - github.com/olekukonko/tablewriter v0.0.4 github.com/sirupsen/logrus v1.3.0 github.com/weppos/publicsuffix-go v0.4.0 github.com/zmap/zcrypto v0.0.0-20200513165325-16679db567ff diff --git a/v2/go.sum b/v2/go.sum index b6d326922..97bc98488 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -8,11 +8,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= -github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= From eefe940cce7db4f046c3baee83731aa5629ebc91 Mon Sep 17 00:00:00 2001 From: Zakir Durumeric Date: Mon, 25 May 2020 15:27:51 -0500 Subject: [PATCH 05/16] spelling fix --- v2/cmd/zlint/main.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 02798ea05..ee7328ef4 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -263,6 +263,7 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { if longSummary { // make a table with the internal lint names grouped // by type +<<<<<<< HEAD var olsl string var orescount int headings := []string{ @@ -279,6 +280,11 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { // LintStatus(level) or the results count. Also, just // because a level wasn't seen doesn't mean it isn't // important; display "empty" levels, too +======= + longTable := tablewriter.NewWriter(os.Stdout) + longTable.SetHeader([]string{"Level", "# occurrences", "Details"}) + longTable.SetAutoMergeCells(true) +>>>>>>> eef788b... spelling fix for _, level := range sortedLevels { foundDetail := false for _, detail := range resultDetails[lint.LintStatus(level)] { From b2858820c1da74087cdc8bdfa6d1a9403cd8bcba Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Wed, 27 May 2020 21:46:31 -0400 Subject: [PATCH 06/16] Fixed a missed merge :( --- v2/cmd/zlint/main.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index ee7328ef4..02798ea05 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -263,7 +263,6 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { if longSummary { // make a table with the internal lint names grouped // by type -<<<<<<< HEAD var olsl string var orescount int headings := []string{ @@ -280,11 +279,6 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { // LintStatus(level) or the results count. Also, just // because a level wasn't seen doesn't mean it isn't // important; display "empty" levels, too -======= - longTable := tablewriter.NewWriter(os.Stdout) - longTable.SetHeader([]string{"Level", "# occurrences", "Details"}) - longTable.SetAutoMergeCells(true) ->>>>>>> eef788b... spelling fix for _, level := range sortedLevels { foundDetail := false for _, detail := range resultDetails[lint.LintStatus(level)] { From 832bc81bbbe56c1597f98dbe1c639d5cec3eb6c4 Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Tue, 2 Jun 2020 17:47:35 -0400 Subject: [PATCH 07/16] switched longsummary to longSummary; fixed output bug - switched `-longsummary` option to `-longSummary` to be more consistent with existing options - fixed an embarrassing output bug when two categories had the same number of errors --- v2/cmd/zlint/main.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 02798ea05..74188962f 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -39,7 +39,7 @@ var ( // flags listLintsJSON bool listLintSources bool summary bool - longsummary bool + longSummary bool prettyprint bool format string nameFilter string @@ -56,7 +56,7 @@ func init() { flag.BoolVar(&listLintsJSON, "list-lints-json", false, "Print lints in JSON format, one per line") flag.BoolVar(&listLintSources, "list-lints-source", false, "Print list of lint sources, one per line") flag.BoolVar(&summary, "summary", false, "Prints a short human-readable summary report") - flag.BoolVar(&longsummary, "longsummary", false, "Prints a human-readable summary report with details") + flag.BoolVar(&longSummary, "longSummary", false, "Prints a human-readable summary report with details") flag.StringVar(&format, "format", "pem", "One of {pem, der, base64}") flag.StringVar(&nameFilter, "nameFilter", "", "Only run lints with a name matching the provided regex. (Can not be used with -includeNames/-excludeNames)") flag.StringVar(&includeNames, "includeNames", "", "Comma-separated list of lints to include by name") @@ -164,7 +164,7 @@ func doLint(inputFile *os.File, inform string, registry lint.Registry) { os.Stdout.Write(out.Bytes()) } else if summary { outputSummary(zlintResult, false) - } else if longsummary { + } else if longSummary { outputSummary(zlintResult, true) } else { os.Stdout.Write(jsonBytes) @@ -264,15 +264,14 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { // make a table with the internal lint names grouped // by type var olsl string - var orescount int headings := []string{ "Level", "# occurrences", - " Details ", + " Details ", } lines := [][]string{} lsl := "" - rescount := "" + rescount:= "" hlengths := printTableHeadings(headings) // Construct the table lines, but don't repeat @@ -285,13 +284,9 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { if fmt.Sprintf("%s", lint.LintStatus(level)) != olsl { olsl = fmt.Sprintf("%s", lint.LintStatus(level)) lsl = olsl + rescount = strconv.Itoa(resultCount[lint.LintStatus(level)]) } else { lsl = "" - } - if resultCount[lint.LintStatus(level)] != orescount { - orescount = resultCount[lint.LintStatus(level)] - rescount = strconv.Itoa(orescount) - } else { rescount = "" } lines = append(lines, ([]string{lsl, rescount, detail})) From b3c3f698dbec2e25ff625c6119852e3de1d2bbf4 Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Tue, 2 Jun 2020 17:55:27 -0400 Subject: [PATCH 08/16] Cleaned up typos, variable names, formatting --- v2/cmd/zlint/main.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 74188962f..b7b4088cf 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -237,19 +237,19 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { } lintLevelsAboveThreshold[int(i)] = i } - // Set all of the levels to 0 events to they are all displayed + // Set all of the levels to 0 events so they are all displayed // in the -summary table for _, level := range lintLevelsAboveThreshold { resultCount[level] = 0 } // Count up the number of each event - for key, value := range zlintResult.Results { - if value.Status > threshold { - resultCount[value.Status]++ + for lintName, lintResult := range zlintResult.Results { + if lintResult.Status > threshold { + resultCount[lintResult.Status]++ if longSummary { - resultDetails[value.Status] = append( - resultDetails[value.Status], - string(key), + resultDetails[lintResult.Status] = append( + resultDetails[lintResult.Status], + string(lintName), ) } } @@ -335,7 +335,6 @@ func printTableHeadings(headings []string) []int { } func printTableBody(hlengths []int, lines [][]string) { - for _, line := range lines { for i, hlen := range hlengths { // This makes a format string with the From 796d10fdae3831d783e08d7c3bbcb2ddeed45209 Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Mon, 25 May 2020 11:26:06 -0400 Subject: [PATCH 09/16] parent 99579098a16c10d1d704b8b8149dd8c35329107f author Andrew Caird 1590420366 -0400 committer Andrew Caird 1593372751 -0400 Add a -summary option to print a short summary of the linting Linting the test file `testdata/utf8ControlX88.pem` results in: ``` +-------+--------------+ | LEVEL | # OCCURANCES | +-------+--------------+ | info | 0 | | warn | 7 | | error | 15 | | fatal | 0 | +-------+--------------+ ``` and a -longSummary option and output Running: ```sh testdata/indivValAllBad.pem | ./zlint -longsummary ``` the output is: ``` +-------+--------------+------------------------------------------+ | LEVEL | # OCCURANCES | DETAILS | +-------+--------------+------------------------------------------+ | info | 0 | - | | warn | 1 | w_ext_san_critical_with_subject_dn | | error | 7 | e_ca_crl_sign_not_set | | | | e_sub_ca_crl_distribution_points_missing | | | | e_ca_country_name_missing | | | | e_cert_policy_iv_requires_country | | | | e_sub_cert_not_is_ca | | | | e_ca_key_cert_sign_not_set | | | | e_ca_organization_name_missing | | fatal | 0 | - | +-------+--------------+------------------------------------------+ ``` --- v2/cmd/zlint/main.go | 142 ++++++++++++++++++++++++++++++++++++++++++- v2/lint/result.go | 6 +- 2 files changed, 144 insertions(+), 4 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index 679dbed81..b7b4088cf 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -25,7 +25,9 @@ import ( "os" "regexp" "sort" + "strconv" "strings" + "unicode/utf8" log "github.com/sirupsen/logrus" "github.com/zmap/zcrypto/x509" @@ -36,6 +38,8 @@ import ( var ( // flags listLintsJSON bool listLintSources bool + summary bool + longSummary bool prettyprint bool format string nameFilter string @@ -51,6 +55,8 @@ var ( // flags func init() { flag.BoolVar(&listLintsJSON, "list-lints-json", false, "Print lints in JSON format, one per line") flag.BoolVar(&listLintSources, "list-lints-source", false, "Print list of lint sources, one per line") + flag.BoolVar(&summary, "summary", false, "Prints a short human-readable summary report") + flag.BoolVar(&longSummary, "longSummary", false, "Prints a human-readable summary report with details") flag.StringVar(&format, "format", "pem", "One of {pem, der, base64}") flag.StringVar(&nameFilter, "nameFilter", "", "Only run lints with a name matching the provided regex. (Can not be used with -includeNames/-excludeNames)") flag.StringVar(&includeNames, "includeNames", "", "Comma-separated list of lints to include by name") @@ -58,7 +64,7 @@ func init() { flag.StringVar(&includeSources, "includeSources", "", "Comma-separated list of lint sources to include") flag.StringVar(&excludeSources, "excludeSources", "", "Comma-separated list of lint sources to exclude") - flag.BoolVar(&prettyprint, "pretty", false, "Pretty-print output") + flag.BoolVar(&prettyprint, "pretty", false, "Pretty-print JSON output") flag.Usage = func() { fmt.Fprintf(os.Stderr, "ZLint version %s\n\n", version) fmt.Fprintf(os.Stderr, "Usage: %s [flags] file...\n", os.Args[0]) @@ -156,6 +162,10 @@ func doLint(inputFile *os.File, inform string, registry lint.Registry) { log.Fatalf("can't format output: %s", err) } os.Stdout.Write(out.Bytes()) + } else if summary { + outputSummary(zlintResult, false) + } else if longSummary { + outputSummary(zlintResult, true) } else { os.Stdout.Write(jsonBytes) } @@ -210,3 +220,133 @@ func setLints() (lint.Registry, error) { return lint.GlobalRegistry().Filter(filterOpts) } + +func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { + var sortedLevels []int + resultCount := make(map[lint.LintStatus]int) + resultDetails := make(map[lint.LintStatus][]string) + lintLevelsAboveThreshold := make(map[int]lint.LintStatus) + // Set the threashold under which (inclusive) events are not + // counted + threshold := lint.Pass + + // Make the list of lint levels that matter + for _, i := range lint.StatusLabelToLintStatus { + if i <= threshold { + continue + } + lintLevelsAboveThreshold[int(i)] = i + } + // Set all of the levels to 0 events so they are all displayed + // in the -summary table + for _, level := range lintLevelsAboveThreshold { + resultCount[level] = 0 + } + // Count up the number of each event + for lintName, lintResult := range zlintResult.Results { + if lintResult.Status > threshold { + resultCount[lintResult.Status]++ + if longSummary { + resultDetails[lintResult.Status] = append( + resultDetails[lintResult.Status], + string(lintName), + ) + } + } + } + // Sort the levels we have so we can get a nice output + for key, _ := range resultCount { + sortedLevels = append(sortedLevels, int(key)) + } + sort.Ints(sortedLevels) + // make the requested table type + if longSummary { + // make a table with the internal lint names grouped + // by type + var olsl string + headings := []string{ + "Level", + "# occurrences", + " Details ", + } + lines := [][]string{} + lsl := "" + rescount:= "" + + hlengths := printTableHeadings(headings) + // Construct the table lines, but don't repeat + // LintStatus(level) or the results count. Also, just + // because a level wasn't seen doesn't mean it isn't + // important; display "empty" levels, too + for _, level := range sortedLevels { + foundDetail := false + for _, detail := range resultDetails[lint.LintStatus(level)] { + if fmt.Sprintf("%s", lint.LintStatus(level)) != olsl { + olsl = fmt.Sprintf("%s", lint.LintStatus(level)) + lsl = olsl + rescount = strconv.Itoa(resultCount[lint.LintStatus(level)]) + } else { + lsl = "" + rescount = "" + } + lines = append(lines, ([]string{lsl, rescount, detail})) + foundDetail = true + } + if !foundDetail { + lines = append(lines, []string{ + fmt.Sprintf("%s", lint.LintStatus(level)), + strconv.Itoa(resultCount[lint.LintStatus(level)]), + " - ", + }) + } + } + printTableBody(hlengths, lines) + } else { + headings := []string{"Level", "# occurrences"} + hlengths := printTableHeadings(headings) + lines := [][]string{} + for _, level := range sortedLevels { + lines = append(lines, []string{ + fmt.Sprintf("%s", lint.LintStatus(level)), + strconv.Itoa(resultCount[lint.LintStatus(level)])}) + } + printTableBody(hlengths, lines) + } +} + +func printTableHeadings(headings []string) []int { + hlengths := []int{} + for i, h := range headings { + hlengths = append( + hlengths, + utf8.RuneCountInString(h)+1) + fmt.Printf("| %s ", strings.ToUpper(h)) + if i == len(headings)-1 { + fmt.Printf("|\n") + for ii, j := range hlengths { + fmt.Printf("+%s", strings.Repeat("-", j+1)) + if ii == len(headings)-1 { + fmt.Printf("+\n") + } + } + } + } + return hlengths +} + +func printTableBody(hlengths []int, lines [][]string) { + for _, line := range lines { + for i, hlen := range hlengths { + // This makes a format string with the + // right widths, e.g. "%7.7s" + fmtstring := fmt.Sprintf("|%%%[1]d.%[1]ds", hlen) + fmt.Printf(fmtstring, line[i]) + if i == len(hlengths)-1 { + fmt.Printf(" |\n") + } else { + fmt.Printf(" ") + } + } + } + +} diff --git a/v2/lint/result.go b/v2/lint/result.go index fd9a1680d..96c43ab00 100644 --- a/v2/lint/result.go +++ b/v2/lint/result.go @@ -42,10 +42,10 @@ const ( ) var ( - // statusLabelToLintStatus is used to work backwards from + // StatusLabelToLintStatus is used to work backwards from // a LintStatus.String() to the LintStatus. This is used by // LintStatus.Unmarshal. - statusLabelToLintStatus = map[string]LintStatus{ + StatusLabelToLintStatus = map[string]LintStatus{ Reserved.String(): Reserved, NA.String(): NA, NE.String(): NE, @@ -73,7 +73,7 @@ func (e LintStatus) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the json.Unmarshaler interface. func (e *LintStatus) UnmarshalJSON(data []byte) error { key := strings.ReplaceAll(string(data), `"`, "") - if status, ok := statusLabelToLintStatus[key]; ok { + if status, ok := StatusLabelToLintStatus[key]; ok { *e = status } else { return fmt.Errorf("bad LintStatus JSON value: %s", string(data)) From 83d5bfee1adcf9dc4bd57c442458caad9117e0ad Mon Sep 17 00:00:00 2001 From: TLD Update Robot <47792085+tld-update-bot@users.noreply.github.com> Date: Wed, 27 May 2020 11:03:53 -0400 Subject: [PATCH 10/16] autopull: 2020-05-27T14:34:02Z (#441) Co-authored-by: tld-update-bot --- v2/util/gtld_map.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/util/gtld_map.go b/v2/util/gtld_map.go index 3f99ad883..c2c1e4014 100644 --- a/v2/util/gtld_map.go +++ b/v2/util/gtld_map.go @@ -2046,7 +2046,7 @@ var tldMap = map[string]GTLDPeriod{ "esurance": { GTLD: "esurance", DelegationDate: "2016-07-23", - RemovalDate: "", + RemovalDate: "2020-05-26", }, "et": { GTLD: "et", From 307f3e889676b6c061dd8a8b39267d71a33779dd Mon Sep 17 00:00:00 2001 From: TLD Update Robot <47792085+tld-update-bot@users.noreply.github.com> Date: Thu, 28 May 2020 14:51:09 -0400 Subject: [PATCH 11/16] gTLD autopull: 2020-05-28T14:35:00Z (#442) Co-authored-by: tld-update-bot --- v2/util/gtld_map.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/util/gtld_map.go b/v2/util/gtld_map.go index c2c1e4014..19ca65085 100644 --- a/v2/util/gtld_map.go +++ b/v2/util/gtld_map.go @@ -5576,7 +5576,7 @@ var tldMap = map[string]GTLDPeriod{ "scor": { GTLD: "scor", DelegationDate: "2015-06-23", - RemovalDate: "", + RemovalDate: "2020-05-27", }, "scot": { GTLD: "scot", From 3aaea14e37244dfde0d0a31640f7b959af3b4c33 Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Sun, 28 Jun 2020 15:30:42 -0400 Subject: [PATCH 12/16] Moved structure creation out of function into a method for reporting --- v2/cmd/zlint/main.go | 67 +++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index b7b4088cf..fa4819118 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -52,6 +52,13 @@ var ( // flags version = "dev" ) +type resultsTable struct { + resultCount map[lint.LintStatus]int + resultDetails map[lint.LintStatus][]string + lintLevelsAboveThreshold map[int]lint.LintStatus + sortedLevels []int +} + func init() { flag.BoolVar(&listLintsJSON, "list-lints-json", false, "Print lints in JSON format, one per line") flag.BoolVar(&listLintSources, "list-lints-source", false, "Print list of lint sources, one per line") @@ -221,45 +228,53 @@ func setLints() (lint.Registry, error) { return lint.GlobalRegistry().Filter(filterOpts) } -func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { - var sortedLevels []int - resultCount := make(map[lint.LintStatus]int) - resultDetails := make(map[lint.LintStatus][]string) - lintLevelsAboveThreshold := make(map[int]lint.LintStatus) - // Set the threashold under which (inclusive) events are not - // counted - threshold := lint.Pass +func (r resultsTable) newRT(threshold lint.LintStatus, results *zlint.ResultSet, longSummary bool) resultsTable { + + r.resultCount = make(map[lint.LintStatus]int) + r.resultDetails = make(map[lint.LintStatus][]string) + r.lintLevelsAboveThreshold = make(map[int]lint.LintStatus) // Make the list of lint levels that matter for _, i := range lint.StatusLabelToLintStatus { if i <= threshold { continue } - lintLevelsAboveThreshold[int(i)] = i + r.lintLevelsAboveThreshold[int(i)] = i } // Set all of the levels to 0 events so they are all displayed // in the -summary table - for _, level := range lintLevelsAboveThreshold { - resultCount[level] = 0 + for _, level := range r.lintLevelsAboveThreshold { + r.resultCount[level] = 0 } // Count up the number of each event - for lintName, lintResult := range zlintResult.Results { + for lintName, lintResult := range results.Results { if lintResult.Status > threshold { - resultCount[lintResult.Status]++ + r.resultCount[lintResult.Status]++ if longSummary { - resultDetails[lintResult.Status] = append( - resultDetails[lintResult.Status], + r.resultDetails[lintResult.Status] = append( + r.resultDetails[lintResult.Status], string(lintName), ) } } } // Sort the levels we have so we can get a nice output - for key, _ := range resultCount { - sortedLevels = append(sortedLevels, int(key)) + for key := range r.resultCount { + r.sortedLevels = append(r.sortedLevels, int(key)) } - sort.Ints(sortedLevels) - // make the requested table type + sort.Ints(r.sortedLevels) + + return r +} + +func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { + // Set the threashold under which (inclusive) events are not + // counted + threshold := lint.Pass + + rt := resultsTable{}.newRT(threshold, zlintResult, longSummary) + + // make and print the requested table type if longSummary { // make a table with the internal lint names grouped // by type @@ -271,20 +286,20 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { } lines := [][]string{} lsl := "" - rescount:= "" + rescount := "" hlengths := printTableHeadings(headings) // Construct the table lines, but don't repeat // LintStatus(level) or the results count. Also, just // because a level wasn't seen doesn't mean it isn't // important; display "empty" levels, too - for _, level := range sortedLevels { + for _, level := range rt.sortedLevels { foundDetail := false - for _, detail := range resultDetails[lint.LintStatus(level)] { + for _, detail := range rt.resultDetails[lint.LintStatus(level)] { if fmt.Sprintf("%s", lint.LintStatus(level)) != olsl { olsl = fmt.Sprintf("%s", lint.LintStatus(level)) lsl = olsl - rescount = strconv.Itoa(resultCount[lint.LintStatus(level)]) + rescount = strconv.Itoa(rt.resultCount[lint.LintStatus(level)]) } else { lsl = "" rescount = "" @@ -295,7 +310,7 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { if !foundDetail { lines = append(lines, []string{ fmt.Sprintf("%s", lint.LintStatus(level)), - strconv.Itoa(resultCount[lint.LintStatus(level)]), + strconv.Itoa(rt.resultCount[lint.LintStatus(level)]), " - ", }) } @@ -305,10 +320,10 @@ func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { headings := []string{"Level", "# occurrences"} hlengths := printTableHeadings(headings) lines := [][]string{} - for _, level := range sortedLevels { + for _, level := range rt.sortedLevels { lines = append(lines, []string{ fmt.Sprintf("%s", lint.LintStatus(level)), - strconv.Itoa(resultCount[lint.LintStatus(level)])}) + strconv.Itoa(rt.resultCount[lint.LintStatus(level)])}) } printTableBody(hlengths, lines) } From e12bda4275659ac88509ff877afaba53225b0eba Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Sun, 5 Jul 2020 18:51:25 -0400 Subject: [PATCH 13/16] Moved the formatted output routines out of main --- v2/cmd/zlint/main.go | 152 +----------------------- v2/formattedoutput/formattedOutput.go | 159 ++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 149 deletions(-) create mode 100644 v2/formattedoutput/formattedOutput.go diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index fa4819118..e96ccf6af 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -25,14 +25,13 @@ import ( "os" "regexp" "sort" - "strconv" "strings" - "unicode/utf8" log "github.com/sirupsen/logrus" "github.com/zmap/zcrypto/x509" "github.com/zmap/zlint/v2" "github.com/zmap/zlint/v2/lint" + "github.com/zmap/zlint/v2/formattedoutput" ) var ( // flags @@ -52,13 +51,6 @@ var ( // flags version = "dev" ) -type resultsTable struct { - resultCount map[lint.LintStatus]int - resultDetails map[lint.LintStatus][]string - lintLevelsAboveThreshold map[int]lint.LintStatus - sortedLevels []int -} - func init() { flag.BoolVar(&listLintsJSON, "list-lints-json", false, "Print lints in JSON format, one per line") flag.BoolVar(&listLintSources, "list-lints-source", false, "Print list of lint sources, one per line") @@ -170,9 +162,9 @@ func doLint(inputFile *os.File, inform string, registry lint.Registry) { } os.Stdout.Write(out.Bytes()) } else if summary { - outputSummary(zlintResult, false) + formattedoutput.OutputSummary(zlintResult, false) } else if longSummary { - outputSummary(zlintResult, true) + formattedoutput.OutputSummary(zlintResult, true) } else { os.Stdout.Write(jsonBytes) } @@ -227,141 +219,3 @@ func setLints() (lint.Registry, error) { return lint.GlobalRegistry().Filter(filterOpts) } - -func (r resultsTable) newRT(threshold lint.LintStatus, results *zlint.ResultSet, longSummary bool) resultsTable { - - r.resultCount = make(map[lint.LintStatus]int) - r.resultDetails = make(map[lint.LintStatus][]string) - r.lintLevelsAboveThreshold = make(map[int]lint.LintStatus) - - // Make the list of lint levels that matter - for _, i := range lint.StatusLabelToLintStatus { - if i <= threshold { - continue - } - r.lintLevelsAboveThreshold[int(i)] = i - } - // Set all of the levels to 0 events so they are all displayed - // in the -summary table - for _, level := range r.lintLevelsAboveThreshold { - r.resultCount[level] = 0 - } - // Count up the number of each event - for lintName, lintResult := range results.Results { - if lintResult.Status > threshold { - r.resultCount[lintResult.Status]++ - if longSummary { - r.resultDetails[lintResult.Status] = append( - r.resultDetails[lintResult.Status], - string(lintName), - ) - } - } - } - // Sort the levels we have so we can get a nice output - for key := range r.resultCount { - r.sortedLevels = append(r.sortedLevels, int(key)) - } - sort.Ints(r.sortedLevels) - - return r -} - -func outputSummary(zlintResult *zlint.ResultSet, longSummary bool) { - // Set the threashold under which (inclusive) events are not - // counted - threshold := lint.Pass - - rt := resultsTable{}.newRT(threshold, zlintResult, longSummary) - - // make and print the requested table type - if longSummary { - // make a table with the internal lint names grouped - // by type - var olsl string - headings := []string{ - "Level", - "# occurrences", - " Details ", - } - lines := [][]string{} - lsl := "" - rescount := "" - - hlengths := printTableHeadings(headings) - // Construct the table lines, but don't repeat - // LintStatus(level) or the results count. Also, just - // because a level wasn't seen doesn't mean it isn't - // important; display "empty" levels, too - for _, level := range rt.sortedLevels { - foundDetail := false - for _, detail := range rt.resultDetails[lint.LintStatus(level)] { - if fmt.Sprintf("%s", lint.LintStatus(level)) != olsl { - olsl = fmt.Sprintf("%s", lint.LintStatus(level)) - lsl = olsl - rescount = strconv.Itoa(rt.resultCount[lint.LintStatus(level)]) - } else { - lsl = "" - rescount = "" - } - lines = append(lines, ([]string{lsl, rescount, detail})) - foundDetail = true - } - if !foundDetail { - lines = append(lines, []string{ - fmt.Sprintf("%s", lint.LintStatus(level)), - strconv.Itoa(rt.resultCount[lint.LintStatus(level)]), - " - ", - }) - } - } - printTableBody(hlengths, lines) - } else { - headings := []string{"Level", "# occurrences"} - hlengths := printTableHeadings(headings) - lines := [][]string{} - for _, level := range rt.sortedLevels { - lines = append(lines, []string{ - fmt.Sprintf("%s", lint.LintStatus(level)), - strconv.Itoa(rt.resultCount[lint.LintStatus(level)])}) - } - printTableBody(hlengths, lines) - } -} - -func printTableHeadings(headings []string) []int { - hlengths := []int{} - for i, h := range headings { - hlengths = append( - hlengths, - utf8.RuneCountInString(h)+1) - fmt.Printf("| %s ", strings.ToUpper(h)) - if i == len(headings)-1 { - fmt.Printf("|\n") - for ii, j := range hlengths { - fmt.Printf("+%s", strings.Repeat("-", j+1)) - if ii == len(headings)-1 { - fmt.Printf("+\n") - } - } - } - } - return hlengths -} - -func printTableBody(hlengths []int, lines [][]string) { - for _, line := range lines { - for i, hlen := range hlengths { - // This makes a format string with the - // right widths, e.g. "%7.7s" - fmtstring := fmt.Sprintf("|%%%[1]d.%[1]ds", hlen) - fmt.Printf(fmtstring, line[i]) - if i == len(hlengths)-1 { - fmt.Printf(" |\n") - } else { - fmt.Printf(" ") - } - } - } - -} diff --git a/v2/formattedoutput/formattedOutput.go b/v2/formattedoutput/formattedOutput.go new file mode 100644 index 000000000..eb30f7295 --- /dev/null +++ b/v2/formattedoutput/formattedOutput.go @@ -0,0 +1,159 @@ +package formattedoutput + +import ( + "fmt" + "strconv" + "strings" + "unicode/utf8" + "sort" + + "github.com/zmap/zlint/v2" + "github.com/zmap/zlint/v2/lint" + +) + +type resultsTable struct { + resultCount map[lint.LintStatus]int + resultDetails map[lint.LintStatus][]string + lintLevelsAboveThreshold map[int]lint.LintStatus + sortedLevels []int +} + +func (r resultsTable) newRT(threshold lint.LintStatus, results *zlint.ResultSet, longSummary bool) resultsTable { + + r.resultCount = make(map[lint.LintStatus]int) + r.resultDetails = make(map[lint.LintStatus][]string) + r.lintLevelsAboveThreshold = make(map[int]lint.LintStatus) + + // Make the list of lint levels that matter + for _, i := range lint.StatusLabelToLintStatus { + if i <= threshold { + continue + } + r.lintLevelsAboveThreshold[int(i)] = i + } + // Set all of the levels to 0 events so they are all displayed + // in the -summary table + for _, level := range r.lintLevelsAboveThreshold { + r.resultCount[level] = 0 + } + // Count up the number of each event + for lintName, lintResult := range results.Results { + if lintResult.Status > threshold { + r.resultCount[lintResult.Status]++ + if longSummary { + r.resultDetails[lintResult.Status] = append( + r.resultDetails[lintResult.Status], + string(lintName), + ) + } + } + } + // Sort the levels we have so we can get a nice output + for key := range r.resultCount { + r.sortedLevels = append(r.sortedLevels, int(key)) + } + sort.Ints(r.sortedLevels) + + return r +} + + +func OutputSummary(zlintResult *zlint.ResultSet, longSummary bool) { + // Set the threashold under which (inclusive) events are not + // counted + threshold := lint.Pass + + rt := resultsTable{}.newRT(threshold, zlintResult, longSummary) + + // make and print the requested table type + if longSummary { + // make a table with the internal lint names grouped + // by type + var olsl string + headings := []string{ + "Level", + "# occurrences", + " Details ", + } + lines := [][]string{} + lsl := "" + rescount := "" + + hlengths := printTableHeadings(headings) + // Construct the table lines, but don't repeat + // LintStatus(level) or the results count. Also, just + // because a level wasn't seen doesn't mean it isn't + // important; display "empty" levels, too + for _, level := range rt.sortedLevels { + foundDetail := false + for _, detail := range rt.resultDetails[lint.LintStatus(level)] { + if fmt.Sprintf("%s", lint.LintStatus(level)) != olsl { + olsl = fmt.Sprintf("%s", lint.LintStatus(level)) + lsl = olsl + rescount = strconv.Itoa(rt.resultCount[lint.LintStatus(level)]) + } else { + lsl = "" + rescount = "" + } + lines = append(lines, ([]string{lsl, rescount, detail})) + foundDetail = true + } + if !foundDetail { + lines = append(lines, []string{ + fmt.Sprintf("%s", lint.LintStatus(level)), + strconv.Itoa(rt.resultCount[lint.LintStatus(level)]), + " - ", + }) + } + } + printTableBody(hlengths, lines) + } else { + headings := []string{"Level", "# occurrences"} + hlengths := printTableHeadings(headings) + lines := [][]string{} + for _, level := range rt.sortedLevels { + lines = append(lines, []string{ + fmt.Sprintf("%s", lint.LintStatus(level)), + strconv.Itoa(rt.resultCount[lint.LintStatus(level)])}) + } + printTableBody(hlengths, lines) + } +} + +func printTableHeadings(headings []string) []int { + hlengths := []int{} + for i, h := range headings { + hlengths = append( + hlengths, + utf8.RuneCountInString(h)+1) + fmt.Printf("| %s ", strings.ToUpper(h)) + if i == len(headings)-1 { + fmt.Printf("|\n") + for ii, j := range hlengths { + fmt.Printf("+%s", strings.Repeat("-", j+1)) + if ii == len(headings)-1 { + fmt.Printf("+\n") + } + } + } + } + return hlengths +} + +func printTableBody(hlengths []int, lines [][]string) { + for _, line := range lines { + for i, hlen := range hlengths { + // This makes a format string with the + // right widths, e.g. "%7.7s" + fmtstring := fmt.Sprintf("|%%%[1]d.%[1]ds", hlen) + fmt.Printf(fmtstring, line[i]) + if i == len(hlengths)-1 { + fmt.Printf(" |\n") + } else { + fmt.Printf(" ") + } + } + } + +} From d2079c503160c30c0c0b925c45cd9d64e413580d Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Tue, 7 Jul 2020 21:48:32 -0400 Subject: [PATCH 14/16] Changed newRT to a pointer receiver --- v2/formattedoutput/formattedOutput.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/formattedoutput/formattedOutput.go b/v2/formattedoutput/formattedOutput.go index eb30f7295..df7f894f8 100644 --- a/v2/formattedoutput/formattedOutput.go +++ b/v2/formattedoutput/formattedOutput.go @@ -19,7 +19,7 @@ type resultsTable struct { sortedLevels []int } -func (r resultsTable) newRT(threshold lint.LintStatus, results *zlint.ResultSet, longSummary bool) resultsTable { +func (r *resultsTable) newRT(threshold lint.LintStatus, results *zlint.ResultSet, longSummary bool) resultsTable { r.resultCount = make(map[lint.LintStatus]int) r.resultDetails = make(map[lint.LintStatus][]string) @@ -55,7 +55,7 @@ func (r resultsTable) newRT(threshold lint.LintStatus, results *zlint.ResultSet, } sort.Ints(r.sortedLevels) - return r + return *r } @@ -64,7 +64,7 @@ func OutputSummary(zlintResult *zlint.ResultSet, longSummary bool) { // counted threshold := lint.Pass - rt := resultsTable{}.newRT(threshold, zlintResult, longSummary) + rt := (&resultsTable{}).newRT(threshold, zlintResult, longSummary) // make and print the requested table type if longSummary { From 9798f3f149aeb9a6abb4ae31a870bcdacb926dfc Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Sat, 11 Jul 2020 10:53:15 -0400 Subject: [PATCH 15/16] Changed output options to all them all; newlines for nice output --- v2/cmd/zlint/main.go | 10 +++++++--- v2/formattedoutput/formattedOutput.go | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index e96ccf6af..7fe8f8f5d 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -161,11 +161,15 @@ func doLint(inputFile *os.File, inform string, registry lint.Registry) { log.Fatalf("can't format output: %s", err) } os.Stdout.Write(out.Bytes()) - } else if summary { + fmt.Printf("\n\n") + } + if summary { formattedoutput.OutputSummary(zlintResult, false) - } else if longSummary { + } + if longSummary { formattedoutput.OutputSummary(zlintResult, true) - } else { + } + if !prettyprint && !summary && !longSummary { os.Stdout.Write(jsonBytes) } os.Stdout.Write([]byte{'\n'}) diff --git a/v2/formattedoutput/formattedOutput.go b/v2/formattedoutput/formattedOutput.go index df7f894f8..ea3f31fae 100644 --- a/v2/formattedoutput/formattedOutput.go +++ b/v2/formattedoutput/formattedOutput.go @@ -118,6 +118,7 @@ func OutputSummary(zlintResult *zlint.ResultSet, longSummary bool) { strconv.Itoa(rt.resultCount[lint.LintStatus(level)])}) } printTableBody(hlengths, lines) + fmt.Printf("\n") } } From c21f46cdac16c1044d5b07479bdd853291c0a5be Mon Sep 17 00:00:00 2001 From: Andrew Caird Date: Sat, 11 Jul 2020 10:53:15 -0400 Subject: [PATCH 16/16] Changed output options to allow printing of them all; newlines for nice output --- v2/cmd/zlint/main.go | 10 +++++++--- v2/formattedoutput/formattedOutput.go | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/v2/cmd/zlint/main.go b/v2/cmd/zlint/main.go index e96ccf6af..7fe8f8f5d 100644 --- a/v2/cmd/zlint/main.go +++ b/v2/cmd/zlint/main.go @@ -161,11 +161,15 @@ func doLint(inputFile *os.File, inform string, registry lint.Registry) { log.Fatalf("can't format output: %s", err) } os.Stdout.Write(out.Bytes()) - } else if summary { + fmt.Printf("\n\n") + } + if summary { formattedoutput.OutputSummary(zlintResult, false) - } else if longSummary { + } + if longSummary { formattedoutput.OutputSummary(zlintResult, true) - } else { + } + if !prettyprint && !summary && !longSummary { os.Stdout.Write(jsonBytes) } os.Stdout.Write([]byte{'\n'}) diff --git a/v2/formattedoutput/formattedOutput.go b/v2/formattedoutput/formattedOutput.go index df7f894f8..ea3f31fae 100644 --- a/v2/formattedoutput/formattedOutput.go +++ b/v2/formattedoutput/formattedOutput.go @@ -118,6 +118,7 @@ func OutputSummary(zlintResult *zlint.ResultSet, longSummary bool) { strconv.Itoa(rt.resultCount[lint.LintStatus(level)])}) } printTableBody(hlengths, lines) + fmt.Printf("\n") } }