1 | | - | package security_test |
2 | | - | |
3 | | - | import ( |
4 | | - | "testing" |
5 | | - | |
6 | | - | "github.com/bearer/bearer/pkg/commands/process/settings" |
7 | | - | "github.com/bearer/bearer/pkg/flag" |
8 | | - | "github.com/bearer/bearer/pkg/report/output/dataflow" |
9 | | - | "github.com/bearer/bearer/pkg/report/output/dataflow/types" |
10 | | - | "github.com/bearer/bearer/pkg/report/output/security" |
11 | | - | "github.com/bearer/bearer/pkg/report/schema" |
12 | | - | "github.com/bradleyjkemp/cupaloy" |
13 | | - | "github.com/hhatto/gocloc" |
14 | | - | ) |
15 | | - | |
16 | | - | func TestBuildReportString(t *testing.T) { |
17 | | - | config, err := generateConfig(flag.ReportOptions{ |
18 | | - | Report: "security", |
19 | | - | Severity: map[string]bool{ |
20 | | - | "critical": true, |
21 | | - | "high": true, |
22 | | - | "medium": true, |
23 | | - | "low": true, |
24 | | - | "warning": true, |
25 | | - | }, |
26 | | - | }) |
27 | | - | |
28 | | - | // new rules are added |
29 | | - | customRule := &settings.Rule{ |
30 | | - | Id: "custom_test_rule", |
31 | | - | Description: "Its a test!", |
32 | | - | CWEIDs: []string{}, |
33 | | - | Type: "risk", |
34 | | - | Languages: []string{"ruby"}, |
35 | | - | Severity: "low", |
36 | | - | IsLocal: false, |
37 | | - | } |
38 | | - | |
39 | | - | // limit rules so that test doesn't fail just because |
40 | | - | config.Rules = map[string]*settings.Rule{ |
41 | | - | "ruby_lang_ssl_verification": config.Rules["ruby_lang_ssl_verification"], |
42 | | - | "ruby_rails_logger": config.Rules["ruby_rails_logger"], |
43 | | - | "custom_test_rule": customRule, |
44 | | - | } |
45 | | - | |
46 | | - | if err != nil { |
47 | | - | t.Fatalf("failed to generate config:%s", err) |
48 | | - | } |
49 | | - | |
50 | | - | dataflow := dummyDataflow() |
51 | | - | |
52 | | - | results, err := security.GetOutput(&dataflow, config) |
53 | | - | if err != nil { |
54 | | - | t.Fatalf("failed to generate security output err:%s", err) |
55 | | - | } |
56 | | - | |
57 | | - | dummyGoclocLanguage := gocloc.Language{} |
58 | | - | dummyGoclocResult := gocloc.Result{ |
59 | | - | Total: &dummyGoclocLanguage, |
60 | | - | Files: map[string]*gocloc.ClocFile{}, |
61 | | - | Languages: map[string]*gocloc.Language{ |
62 | | - | "Ruby": {}, |
63 | | - | }, |
64 | | - | MaxPathLength: 0, |
65 | | - | } |
66 | | - | |
67 | | - | stringBuilder, _ := security.BuildReportString(config, results, &dummyGoclocResult, &dataflow) |
68 | | - | cupaloy.SnapshotT(t, stringBuilder.String()) |
69 | | - | } |
70 | | - | |
71 | | - | func TestGetOutput(t *testing.T) { |
72 | | - | config, err := generateConfig(flag.ReportOptions{ |
73 | | - | Report: "security", |
74 | | - | Severity: map[string]bool{ |
75 | | - | "critical": true, |
76 | | - | "high": true, |
77 | | - | "medium": true, |
78 | | - | "low": true, |
79 | | - | "warning": true, |
80 | | - | }, |
81 | | - | }) |
82 | | - | |
83 | | - | if err != nil { |
84 | | - | t.Fatalf("failed to generate config:%s", err) |
85 | | - | } |
86 | | - | |
87 | | - | dataflow := dummyDataflow() |
88 | | - | |
89 | | - | res, err := security.GetOutput(&dataflow, config) |
90 | | - | if err != nil { |
91 | | - | t.Fatalf("failed to generate security output err:%s", err) |
92 | | - | } |
93 | | - | |
94 | | - | cupaloy.SnapshotT(t, res) |
95 | | - | } |
96 | | - | |
97 | | - | func TestTestGetOutputWithSeverity(t *testing.T) { |
98 | | - | config, err := generateConfig(flag.ReportOptions{ |
99 | | - | Report: "security", |
100 | | - | Severity: map[string]bool{ |
101 | | - | "critical": true, |
102 | | - | "high": false, |
103 | | - | "medium": false, |
104 | | - | "low": false, |
105 | | - | "warning": false, |
106 | | - | }, |
107 | | - | }) |
108 | | - | |
109 | | - | if err != nil { |
110 | | - | t.Fatalf("failed to generate config:%s", err) |
111 | | - | } |
112 | | - | |
113 | | - | dataflow := dummyDataflow() |
114 | | - | |
115 | | - | res, err := security.GetOutput(&dataflow, config) |
116 | | - | if err != nil { |
117 | | - | t.Fatalf("failed to generate security output err:%s", err) |
118 | | - | } |
119 | | - | |
120 | | - | cupaloy.SnapshotT(t, res) |
121 | | - | } |
122 | | - | |
123 | | - | func TestCalculateSeverity(t *testing.T) { |
124 | | - | res := []string{ |
125 | | - | security.CalculateSeverity([]string{"PHI", "Personal Data"}, "low", true), |
126 | | - | security.CalculateSeverity([]string{"Personal Data (Sensitive)"}, "low", false), |
127 | | - | security.CalculateSeverity([]string{"Personal Data"}, "low", false), |
128 | | - | security.CalculateSeverity([]string{"Personal Data"}, "warning", false), |
129 | | - | security.CalculateSeverity([]string{}, "warning", false), |
130 | | - | } |
131 | | - | |
132 | | - | cupaloy.SnapshotT(t, res) |
133 | | - | } |
134 | | - | |
135 | | - | func generateConfig(reportOptions flag.ReportOptions) (settings.Config, error) { |
136 | | - | opts := flag.Options{ |
137 | | - | ScanOptions: flag.ScanOptions{ |
138 | | - | Scanner: []string{"sast"}, |
139 | | - | }, |
140 | | - | RuleOptions: flag.RuleOptions{}, |
141 | | - | RepoOptions: flag.RepoOptions{}, |
142 | | - | ReportOptions: reportOptions, |
143 | | - | GeneralOptions: flag.GeneralOptions{}, |
144 | | - | } |
145 | | - | |
146 | | - | return settings.FromOptions(opts) |
147 | | - | } |
148 | | - | |
149 | | - | func dummyDataflow() dataflow.DataFlow { |
150 | | - | subject := "User" |
151 | | - | riskLocation := types.RiskLocation{ |
152 | | - | Filename: "config/application.rb", |
153 | | - | LineNumber: 2, |
154 | | - | FieldName: "", |
155 | | - | ObjectName: "", |
156 | | - | SubjectName: &subject, |
157 | | - | Parent: &schema.Parent{ |
158 | | - | LineNumber: 2, |
159 | | - | Content: "http.verify_mode = OpenSSL::SSL::VERIFY_NONE", |
160 | | - | }, |
161 | | - | } |
162 | | - | lowRisk := types.RiskDetection{ |
163 | | - | DetectorID: "ruby_lang_ssl_verification", |
164 | | - | Locations: []types.RiskDetectionLocation{ |
165 | | - | { |
166 | | - | Content: "http.verify_mode = OpenSSL::SSL::VERIFY_NONE", |
167 | | - | RiskLocation: &riskLocation, |
168 | | - | }, |
169 | | - | }, |
170 | | - | } |
171 | | - | |
172 | | - | criticalRisk := types.RiskDetector{ |
173 | | - | DetectorID: "ruby_rails_logger", |
174 | | - | DataTypes: []types.RiskDatatype{ |
175 | | - | { |
176 | | - | Name: "Biometric Data", |
177 | | - | Stored: false, |
178 | | - | UUID: "85599b0c-37b6-4855-af54-5789edc27c00", |
179 | | - | CategoryUUID: "35b94efa-9b67-49b2-abb9-29b6a759a030", |
180 | | - | Locations: []types.RiskLocation{ |
181 | | - | { |
182 | | - | Filename: "pkg/datatype_leak.rb", |
183 | | - | LineNumber: 1, |
184 | | - | FieldName: "biometric_data", |
185 | | - | ObjectName: "user", |
186 | | - | SubjectName: &subject, |
187 | | - | Parent: &schema.Parent{ |
188 | | - | LineNumber: 1, |
189 | | - | Content: "Rails.logger.info(user.biometric_data)", |
190 | | - | }, |
191 | | - | }, |
192 | | - | }, |
193 | | - | }, |
194 | | - | }, |
195 | | - | } |
196 | | - | |
197 | | - | // build risk []interface |
198 | | - | risks := make([]interface{}, 2) |
199 | | - | risks[0] = criticalRisk |
200 | | - | risks[1] = lowRisk |
201 | | - | |
202 | | - | return dataflow.DataFlow{ |
203 | | - | Datatypes: []types.Datatype{ |
204 | | - | { |
205 | | - | Name: "Email Address", |
206 | | - | UUID: "02bb0d3a-2c8c-4842-be1c-c057f0dccd63", |
207 | | - | CategoryUUID: "dd88aee5-9d40-4ad2-8983-0c791ddec47c", |
208 | | - | Detectors: []types.DatatypeDetector{ |
209 | | - | { |
210 | | - | Name: "ruby", |
211 | | - | Locations: []types.DatatypeLocation{ |
212 | | - | { |
213 | | - | Filename: "app/model/user.rb", |
214 | | - | LineNumber: 1, |
215 | | - | FieldName: "email", |
216 | | - | ObjectName: "user", |
217 | | - | SubjectName: &subject, |
218 | | - | }, |
219 | | - | }, |
220 | | - | }, |
221 | | - | }, |
222 | | - | }, |
223 | | - | }, |
224 | | - | Risks: risks, |
225 | | - | Components: []types.Component{}, |
226 | | - | } |
227 | | - | } |
228 | | - | |