-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathscan.go
120 lines (112 loc) · 3.43 KB
/
scan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2023 Google LLC
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
package analyzer
import (
"embed"
"fmt"
"go/types"
"os"
"sort"
"strings"
"text/template"
"github.com/fatih/color"
"golang.org/x/tools/go/packages"
"google.golang.org/protobuf/encoding/protojson"
)
//go:embed static/*
var staticContent embed.FS
// DifferenceFoundError indicates that a comparison was successfully run, and
// a difference was found.
type DifferenceFoundError struct{}
func (d DifferenceFoundError) Error() string {
return "difference found"
}
func RunCapslock(args []string, output string, pkgs []*packages.Package, queriedPackages map[*types.Package]struct{},
config *Config) error {
if output == "compare" {
if len(args) != 1 {
return fmt.Errorf("Usage: %s -output=compare <filename>; provided %v args", programName(), len(args))
}
different, err := compare(args[0], pkgs, queriedPackages, config)
if err != nil {
return err
}
if different {
return DifferenceFoundError{}
}
return nil
} else if len(args) >= 1 {
return fmt.Errorf("%s: unknown command", args)
}
templateFuncMap := template.FuncMap{
"format": templateFormat,
}
if output == "json" || output == "j" {
cil := GetCapabilityInfo(pkgs, queriedPackages, config)
b, err := protojson.MarshalOptions{Multiline: true, Indent: "\t"}.Marshal(cil)
if err != nil {
return fmt.Errorf("internal error: couldn't marshal protocol buffer: %s", err.Error())
}
fmt.Println(string(b))
return nil
} else if output == "m" || output == "machine" {
var cs []string
cil := GetCapabilityCounts(pkgs, queriedPackages, config)
for c := range cil.CapabilityCounts {
cs = append(cs, c)
}
sort.Strings(cs)
for _, c := range cs {
fmt.Println(c)
}
return nil
} else if output == "v" || output == "verbose" {
cil := GetCapabilityStats(pkgs, queriedPackages, config)
ctm := template.Must(template.New("verbose.tmpl").Funcs(templateFuncMap).ParseFS(staticContent, "static/verbose.tmpl"))
return ctm.Execute(os.Stdout, cil)
} else if output == "g" || output == "graph" {
return graphOutput(pkgs, queriedPackages, config)
}
cil := GetCapabilityCounts(pkgs, queriedPackages, config)
ctm := template.Must(template.New("default.tmpl").Funcs(templateFuncMap).ParseFS(staticContent, "static/default.tmpl"))
return ctm.Execute(os.Stdout, cil)
}
func templateFormat(args ...interface{}) string {
var format string
if len(args) != 0 {
format = args[0].(string)
}
var w strings.Builder
switch format {
case "":
// "{{format}}" without arguments resets format.
color.New(color.FgHiBlack).UnsetWriter(&w)
case "intro", "callpath", "callpath-site":
color.New(color.FgHiBlack).SetWriter(&w)
case "highlight":
color.New(color.FgCyan).SetWriter(&w)
case "heading":
color.New(color.FgHiWhite).SetWriter(&w)
case "nocap":
color.New(color.FgHiGreen).SetWriter(&w)
case "capability":
var capability string
if s, ok := args[1].(fmt.Stringer); ok {
capability = s.String()
} else {
capability, ok = args[1].(string)
}
switch capability {
case "CAPABILITY_SAFE":
color.New(color.FgHiGreen).SetWriter(&w)
case "CAPABILITY_ARBITRARY_EXECUTION", "CAPABILITY_CGO", "CAPABILITY_UNSAFE_POINTER", "CAPABILITY_EXEC":
color.New(color.FgHiRed).SetWriter(&w)
default:
color.New(color.FgHiYellow).SetWriter(&w)
}
}
return w.String()
}