Skip to content

Commit

Permalink
Merge pull request #33 from tstromberg/main
Browse files Browse the repository at this point in the history
Stream table rendering, widen values column
  • Loading branch information
tstromberg committed Mar 6, 2024
2 parents 9f4d385 + e63b1e7 commit c3620af
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 95 deletions.
21 changes: 14 additions & 7 deletions bincapz.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ func main() {
includeDataFiles = true
}

var rf bincapz.RenderFunc
switch *formatFlag {
case "simple":
rf = bincapz.RenderSimple
case "table":
rf = bincapz.RenderTable
case "json", "yaml":
default:
fmt.Printf("what kind of format is %q?\n", *formatFlag)
os.Exit(3)
}

bc := bincapz.Config{
RuleFS: ruleFs,
ScanPaths: args,
Expand All @@ -54,6 +66,8 @@ func main() {
MinLevel: minLevel,
ThirdPartyRules: *thirdPartyFlag,
IncludeDataFiles: includeDataFiles,
RenderFunc: rf,
Output: os.Stdout,
}

fmt.Fprintf(os.Stderr, "scanning %s ...\n", strings.Join(args, " "))
Expand All @@ -76,13 +90,6 @@ func main() {
klog.Fatalf("marshal: %v", err)
}
fmt.Printf("%s\n", yaml)
case "simple":
bincapz.RenderSimple(res, os.Stdout)
case "table":
bincapz.RenderTable(res, os.Stdout)
default:
fmt.Printf("what kind of format is %q?\n", *formatFlag)
os.Exit(3)
}
if err != nil {
klog.Errorf("failed: %v", err)
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ go 1.21.6
require (
github.com/google/go-cmp v0.6.0
github.com/hillu/go-yara/v4 v4.3.2
github.com/liamg/magic v0.0.1
github.com/olekukonko/tablewriter v0.0.5
gopkg.in/yaml.v3 v3.0.1
k8s.io/klog/v2 v2.120.1
)

require (
github.com/go-logr/logr v1.4.1 // indirect
github.com/liamg/magic v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
Expand All @@ -10,9 +12,21 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
175 changes: 92 additions & 83 deletions pkg/bincapz/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import (
"strings"

"github.com/olekukonko/tablewriter"
"golang.org/x/term"
"k8s.io/klog/v2"
)

type KeyedBehavior struct {
Key string
Behavior Behavior
}

type RenderFunc func(f *FileReport, w io.Writer)

func forceWrap(s string, x int) string {
words, _ := tablewriter.WrapString(s, x)
fw := []string{}
Expand All @@ -28,106 +32,111 @@ func forceWrap(s string, x int) string {
return strings.Join(fw, "\n")
}

func RenderTable(res *Report, w io.Writer) {
files := 0
tableShown := false

for path, fr := range res.Files {
if files > 0 && tableShown {
fmt.Print("\n")
}
func terminalWidth() int {
if !term.IsTerminal(0) {
return 120
}

if fr.Error != "" {
fmt.Printf("%s - error: %s\n", path, fr.Error)
tableShown = false
continue
}
width, _, err := term.GetSize(0)
if err != nil {
klog.Errorf("term.getsize: %v", err)
return 80
}

if fr.Skipped != "" {
fmt.Printf("%s - skipped: %s\n", path, fr.Skipped)
tableShown = false
continue
}
return width
}

fmt.Printf("%s\n", path)
files++
tableShown = false
func RenderTable(fr *FileReport, w io.Writer) {
path := fr.Path
if fr.Error != "" {
fmt.Printf("%s - error: %s\n", path, fr.Error)
return
}

kbs := []KeyedBehavior{}
for k, b := range fr.Behaviors {
kbs = append(kbs, KeyedBehavior{Key: k, Behavior: b})
}
if fr.Skipped != "" {
fmt.Printf("%s - skipped: %s\n", path, fr.Skipped)
return
}

if len(kbs) == 0 {
continue
}
fmt.Printf("%s\n", path)

sort.Slice(kbs, func(i, j int) bool {
if kbs[i].Behavior.RiskScore == kbs[j].Behavior.RiskScore {
return kbs[i].Key < kbs[j].Key
}
return kbs[i].Behavior.RiskScore < kbs[j].Behavior.RiskScore
})
kbs := []KeyedBehavior{}
for k, b := range fr.Behaviors {
kbs = append(kbs, KeyedBehavior{Key: k, Behavior: b})
}

data := [][]string{}
if len(kbs) == 0 {
return
}

for k, v := range fr.Meta {
data = append(data, []string{"-", k, v, ""})
}
if len(data) > 0 {
data = append(data, []string{"", "", "", ""})
sort.Slice(kbs, func(i, j int) bool {
if kbs[i].Behavior.RiskScore == kbs[j].Behavior.RiskScore {
return kbs[i].Key < kbs[j].Key
}
return kbs[i].Behavior.RiskScore < kbs[j].Behavior.RiskScore
})

for _, k := range kbs {
val := strings.Join(k.Behavior.Strings, " ")
val = forceWrap(val, 24)
data := [][]string{}

desc := k.Behavior.Description
before, _, found := strings.Cut(desc, ". ")
if found {
desc = before
}
if k.Behavior.RuleAuthor != "" {
if desc != "" {
desc = fmt.Sprintf("%s, by %s", desc, k.Behavior.RuleAuthor)
} else {
desc = fmt.Sprintf("by %s", k.Behavior.RuleAuthor)
}
}
for k, v := range fr.Meta {
data = append(data, []string{"-", k, v, ""})
}
if len(data) > 0 {
data = append(data, []string{"", "", "", ""})
}

valWidth := 24
width := terminalWidth()
if width > 110 {
valWidth += (width - 110)
}
if valWidth > 60 {
valWidth = 60
}

klog.Infof("terminal width: %d / val width: %d", width, valWidth)

words, _ := tablewriter.WrapString(desc, 52)
desc = strings.Join(words, "\n")
for _, k := range kbs {
val := strings.Join(k.Behavior.Strings, " ")
val = forceWrap(val, valWidth)

data = append(data, []string{fmt.Sprintf("%d/%s", k.Behavior.RiskScore, k.Behavior.RiskLevel), k.Key, val, desc})
desc := k.Behavior.Description
before, _, found := strings.Cut(desc, ". ")
if found {
desc = before
}
tableShown = true
table := tablewriter.NewWriter(os.Stdout)
table.SetAutoWrapText(false)
table.SetHeader([]string{"Risk", "Key", "Values", "Description"})
//table.SetBorder(false)
table.AppendBulk(data) // Add Bulk Data
table.Render()

if fr.FilteredBehaviors > 0 {
fmt.Fprintf(w, "\n# %d behavior(s) filtered out, use --all to see more\n", fr.FilteredBehaviors)
if k.Behavior.RuleAuthor != "" {
if desc != "" {
desc = fmt.Sprintf("%s, by %s", desc, k.Behavior.RuleAuthor)
} else {
desc = fmt.Sprintf("by %s", k.Behavior.RuleAuthor)
}
}

words, _ := tablewriter.WrapString(desc, 52)
desc = strings.Join(words, "\n")

data = append(data, []string{fmt.Sprintf("%d/%s", k.Behavior.RiskScore, k.Behavior.RiskLevel), k.Key, val, desc})
}
table := tablewriter.NewWriter(os.Stdout)
table.SetAutoWrapText(false)
table.SetHeader([]string{"Risk", "Key", "Values", "Description"})
//table.SetBorder(false)
table.AppendBulk(data) // Add Bulk Data
table.Render()

fmt.Println("")
}

func RenderSimple(res *Report, w io.Writer) {
for path, fr := range res.Files {
fmt.Fprintf(w, "# %s\n", path)
keys := []string{}
for key := range fr.Behaviors {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Fprintf(w, "- %s\n", key)
}

if fr.FilteredBehaviors > 0 {
fmt.Fprintf(w, "\n# %d behavior(s) filtered out, use --all to see more\n", fr.FilteredBehaviors)
}
func RenderSimple(fr *FileReport, w io.Writer) {
path := fr.Path
fmt.Fprintf(w, "# %s\n", path)
keys := []string{}
for key := range fr.Behaviors {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Fprintf(w, "- %s\n", key)
}
}
4 changes: 3 additions & 1 deletion pkg/bincapz/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Behavior struct {
}

type FileReport struct {
Path string
// compiler -> x
Error string `json:",omitempty" yaml:",omitempty"`
Skipped string `json:",omitempty" yaml:",omitempty"`
Expand Down Expand Up @@ -194,13 +195,14 @@ func matchStrings(ms []yara.MatchString) []string {
return slices.Compact(ss)
}

func fileReport(mrs yara.MatchRules, ignoreTags []string, minLevel int) FileReport {
func fileReport(path string, mrs yara.MatchRules, ignoreTags []string, minLevel int) FileReport {
ignore := map[string]bool{}
for _, t := range ignoreTags {
ignore[t] = true
}

fr := FileReport{
Path: path,
Meta: map[string]string{},
Behaviors: map[string]Behavior{},
}
Expand Down
11 changes: 8 additions & 3 deletions pkg/bincapz/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bincapz

import (
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
Expand All @@ -19,6 +20,8 @@ type Config struct {
ThirdPartyRules bool
OmitEmpty bool
IncludeDataFiles bool
RenderFunc RenderFunc
Output io.Writer
}

// return a list of files within a path
Expand Down Expand Up @@ -73,20 +76,22 @@ func Scan(c Config) (*Report, error) {
kind := programKind(p)
if !c.IncludeDataFiles && kind == "" {
r.Files[p] = FileReport{Skipped: fmt.Sprintf("data file")}
klog.Infof("skipping %s - does not appear to be a program")
klog.Infof("not a program: %s", p)
continue
}

if err := yrs.ScanFile(p, 0, 0, &mrs); err != nil {
r.Files[p] = FileReport{Error: fmt.Sprintf("scanfile: %v", err)}
klog.Infof("skipping %s - %v", err)
r.Files[p] = FileReport{Path: p, Error: fmt.Sprintf("scanfile: %v", err)}
continue
}

fr := fileReport(mrs, c.IgnoreTags, c.MinLevel)
fr := fileReport(p, mrs, c.IgnoreTags, c.MinLevel)
if len(fr.Behaviors) == 0 && c.OmitEmpty {
continue
}
r.Files[p] = fr
c.RenderFunc(&fr, c.Output)
}
}

Expand Down

0 comments on commit c3620af

Please sign in to comment.