Skip to content

Commit

Permalink
feat: add severity computation details
Browse files Browse the repository at this point in the history
  • Loading branch information
cfabianski committed Aug 17, 2023
1 parent a21804b commit 60f82f3
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 34 deletions.
4 changes: 2 additions & 2 deletions pkg/report/output/privacy/privacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func GetOutput(dataflow *types.DataFlow, config settings.Config) (*types.Output[
}

// count severity
switch ruleSeverity {
switch ruleSeverity.DisplaySeverity {
case globaltypes.LevelCritical:
subjectRuleFailure.CriticalRiskFindingCount += 1
case globaltypes.LevelHigh:
Expand Down Expand Up @@ -259,7 +259,7 @@ func GetOutput(dataflow *types.DataFlow, config settings.Config) (*types.Output[
}

// count severity
switch ruleSeverity {
switch ruleSeverity.DisplaySeverity {
case globaltypes.LevelCritical:
thirdPartyDataSubject.CriticalRiskFindingCount += 1
case globaltypes.LevelHigh:
Expand Down
47 changes: 41 additions & 6 deletions pkg/report/output/security/.snapshots/TestCalculateSeverity
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
([]string) (len=5) {
(string) (len=8) "critical",
(string) (len=4) "high",
(string) (len=6) "medium",
(string) (len=7) "warning",
(string) (len=7) "warning"
([]security.SeverityWeighting) (len=5) {
(security.SeverityWeighting) {
RuleSeverity: (string) (len=3) "low",
DisplaySeverity: (string) (len=8) "critical",
SensitiveDataCategoryWeighting: (int) 3,
RuleSeverityWeighting: (int) 2,
TriggerWeighting: (bool) true,
FinalWeighting: (int) 8
},
(security.SeverityWeighting) {
RuleSeverity: (string) (len=3) "low",
DisplaySeverity: (string) (len=4) "high",
SensitiveDataCategoryWeighting: (int) 3,
RuleSeverityWeighting: (int) 2,
TriggerWeighting: (bool) false,
FinalWeighting: (int) 5
},
(security.SeverityWeighting) {
RuleSeverity: (string) (len=3) "low",
DisplaySeverity: (string) (len=6) "medium",
SensitiveDataCategoryWeighting: (int) 2,
RuleSeverityWeighting: (int) 2,
TriggerWeighting: (bool) false,
FinalWeighting: (int) 4
},
(security.SeverityWeighting) {
RuleSeverity: (string) (len=7) "warning",
DisplaySeverity: (string) (len=7) "warning",
SensitiveDataCategoryWeighting: (int) 0,
RuleSeverityWeighting: (int) 0,
TriggerWeighting: (bool) false,
FinalWeighting: (int) 0
},
(security.SeverityWeighting) {
RuleSeverity: (string) (len=7) "warning",
DisplaySeverity: (string) (len=7) "warning",
SensitiveDataCategoryWeighting: (int) 0,
RuleSeverityWeighting: (int) 0,
TriggerWeighting: (bool) false,
FinalWeighting: (int) 0
}
}
22 changes: 20 additions & 2 deletions pkg/report/output/security/.snapshots/TestGetOutput
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
Id: (string) (len=17) "ruby_rails_logger",
Title: (string) (len=46) "Sensitive data sent to Rails loggers detected.",
Description: (string) (len=608) "## Description\nLeaking sensitive data to loggers is a common cause of data leaks and can lead to data breaches. This rule looks for instances of sensitive data sent to rails loggers.\n\n## Remediations\n❌ Avoid using sensitive data in logger messages:\n\n```ruby\nRails.logger.info('User is: #{user.email}')\n```\n\n✅ If you need to identify a user, ensure to use their unique identifier instead of their personal identifiable information:\n\n```ruby\nRails.logger.info('User is: #{user.uuid}')\n```\n\n## Resources\n- [OWASP logging cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html)\n",
DocumentationUrl: (string) (len=57) "https://docs.bearer.com/reference/rules/ruby_rails_logger"
DocumentationUrl: (string) (len=57) "https://docs.bearer.com/reference/rules/ruby_rails_logger",
Severity: (string) ""
}),
LineNumber: (int) 1,
FullFilename: (string) "",
Expand Down Expand Up @@ -50,6 +51,14 @@
OldFingerprint: (string) (len=34) "80ce0185374c0975a9b2a71e9d11a4f0_0",
DetailedContext: (string) "",
CodeExtract: (string) "",
SeverityWeighting: (security.SeverityWeighting) {
RuleSeverity: (string) "",
DisplaySeverity: (string) (len=8) "critical",
SensitiveDataCategoryWeighting: (int) 3,
RuleSeverityWeighting: (int) 2,
TriggerWeighting: (bool) true,
FinalWeighting: (int) 8
},
RawCodeExtract: ([]file.Line) {
}
}
Expand All @@ -63,7 +72,8 @@
Id: (string) (len=26) "ruby_lang_ssl_verification",
Title: (string) (len=46) "Missing SSL certificate verification detected.",
Description: (string) (len=728) "## Description\n\nApplications processing sensitive data should use valid SSL certificates. This rule checks if SSL verification is enabled.\n\n## Remediations\n\n❌ By default Ruby check for SSL certificate verification but this can be bypassed when setting Open SSL verification mode to `VERIFY_NONE`:\n\n```clojure\nrequire \"net/https\"\nrequire \"uri\"\n\nuri = URI.parse(\"https://ssl-site.com/\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\nhttp.verify_mode = OpenSSL::SSL::VERIFY_NONE\n```\n\n✅ To ensure that SSL verification always happens, make sure to use the following mode:\n\n```bash\nhttp.verify_mode = OpenSSL::SSL::VERIFY_PEER\n```\n\n## Resources\n- [Ruby OpenSSL module](https://ruby.github.io/openssl/OpenSSL.html)\n",
DocumentationUrl: (string) (len=66) "https://docs.bearer.com/reference/rules/ruby_lang_ssl_verification"
DocumentationUrl: (string) (len=66) "https://docs.bearer.com/reference/rules/ruby_lang_ssl_verification",
Severity: (string) (len=6) "medium"
}),
LineNumber: (int) 2,
FullFilename: (string) "",
Expand Down Expand Up @@ -100,6 +110,14 @@
OldFingerprint: (string) (len=34) "dcc50aebb6a6da7f0a8cb06e071f2af2_0",
DetailedContext: (string) "",
CodeExtract: (string) "",
SeverityWeighting: (security.SeverityWeighting) {
RuleSeverity: (string) (len=6) "medium",
DisplaySeverity: (string) (len=4) "high",
SensitiveDataCategoryWeighting: (int) 2,
RuleSeverityWeighting: (int) 3,
TriggerWeighting: (bool) false,
FinalWeighting: (int) 5
},
RawCodeExtract: ([]file.Line) {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
Id: (string) (len=17) "ruby_rails_logger",
Title: (string) (len=46) "Sensitive data sent to Rails loggers detected.",
Description: (string) (len=608) "## Description\nLeaking sensitive data to loggers is a common cause of data leaks and can lead to data breaches. This rule looks for instances of sensitive data sent to rails loggers.\n\n## Remediations\n❌ Avoid using sensitive data in logger messages:\n\n```ruby\nRails.logger.info('User is: #{user.email}')\n```\n\n✅ If you need to identify a user, ensure to use their unique identifier instead of their personal identifiable information:\n\n```ruby\nRails.logger.info('User is: #{user.uuid}')\n```\n\n## Resources\n- [OWASP logging cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html)\n",
DocumentationUrl: (string) (len=57) "https://docs.bearer.com/reference/rules/ruby_rails_logger"
DocumentationUrl: (string) (len=57) "https://docs.bearer.com/reference/rules/ruby_rails_logger",
Severity: (string) ""
}),
LineNumber: (int) 1,
FullFilename: (string) "",
Expand Down Expand Up @@ -50,6 +51,14 @@
OldFingerprint: (string) (len=34) "80ce0185374c0975a9b2a71e9d11a4f0_0",
DetailedContext: (string) "",
CodeExtract: (string) "",
SeverityWeighting: (security.SeverityWeighting) {
RuleSeverity: (string) "",
DisplaySeverity: (string) (len=8) "critical",
SensitiveDataCategoryWeighting: (int) 3,
RuleSeverityWeighting: (int) 2,
TriggerWeighting: (bool) true,
FinalWeighting: (int) 8
},
RawCodeExtract: ([]file.Line) {
}
}
Expand Down
72 changes: 50 additions & 22 deletions pkg/report/output/security/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ type Input struct {
DataCategories []db.DataCategory `json:"data_categories" yaml:"data_categories"`
}

type SeverityWeighting struct {
RuleSeverity string `json:"rule_severity" yaml:"rule_severity"`
DisplaySeverity string `json:"display_severity" yaml:"display_severity"`
SensitiveDataCategoryWeighting int `json:"sensitive_data_category_weighting,omitempty" yaml:"sensitive_data_category_weighting,omitempty"`
RuleSeverityWeighting int `json:"rule_severity_weighting,omitempty" yaml:"rule_severity_weighting,omitempty"`
TriggerWeighting bool `json:"trigger_weighting,omitempty" yaml:"trigger_weighting,omitempty"`
FinalWeighting int `json:"final_weighting,omitempty" yaml:"final_weighting,omitempty"`
}

type RuleCounter struct {
DefaultRuleCount int
CustomRuleCount int
Expand Down Expand Up @@ -103,20 +112,21 @@ type DataType struct {

type Result struct {
*Rule
LineNumber int `json:"line_number,omitempty" yaml:"line_number,omitempty"`
FullFilename string `json:"full_filename,omitempty" yaml:"full_filename,omitempty"`
Filename string `json:"filename,omitempty" yaml:"filename,omitempty"`
DataType *DataType `json:"data_type,omitempty" yaml:"data_type,omitempty"`
CategoryGroups []string `json:"category_groups,omitempty" yaml:"category_groups,omitempty"`
Source Source `json:"source,omitempty" yaml:"source,omitempty"`
Sink Sink `json:"sink,omitempty" yaml:"sink,omitempty"`
ParentLineNumber int `json:"parent_line_number,omitempty" yaml:"parent_line_number,omitempty"`
ParentContent string `json:"snippet,omitempty" yaml:"snippet,omitempty"`
Fingerprint string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty"`
OldFingerprint string `json:"old_fingerprint,omitempty" yaml:"old_fingerprint,omitempty"`
DetailedContext string `json:"detailed_context,omitempty" yaml:"detailed_context,omitempty"`
CodeExtract string `json:"code_extract,omitempty" yaml:"code_extract,omitempty"`
RawCodeExtract []file.Line `json:"-" yaml:"-"`
LineNumber int `json:"line_number,omitempty" yaml:"line_number,omitempty"`
FullFilename string `json:"full_filename,omitempty" yaml:"full_filename,omitempty"`
Filename string `json:"filename,omitempty" yaml:"filename,omitempty"`
DataType *DataType `json:"data_type,omitempty" yaml:"data_type,omitempty"`
CategoryGroups []string `json:"category_groups,omitempty" yaml:"category_groups,omitempty"`
Source Source `json:"source,omitempty" yaml:"source,omitempty"`
Sink Sink `json:"sink,omitempty" yaml:"sink,omitempty"`
ParentLineNumber int `json:"parent_line_number,omitempty" yaml:"parent_line_number,omitempty"`
ParentContent string `json:"snippet,omitempty" yaml:"snippet,omitempty"`
Fingerprint string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty"`
OldFingerprint string `json:"old_fingerprint,omitempty" yaml:"old_fingerprint,omitempty"`
DetailedContext string `json:"detailed_context,omitempty" yaml:"detailed_context,omitempty"`
CodeExtract string `json:"code_extract,omitempty" yaml:"code_extract,omitempty"`
SeverityWeighting SeverityWeighting `json:"severity" yaml:"severity"`
RawCodeExtract []file.Line `json:"-" yaml:"-"`
}

type Rule struct {
Expand All @@ -125,6 +135,7 @@ type Rule struct {
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
DocumentationUrl string `json:"documentation_url" yaml:"documentation_url"`
Severity string `json:"severity" yaml:"severity"`
}

func GetOutput(
Expand Down Expand Up @@ -237,6 +248,7 @@ func evaluateRules(
}

ruleSummary := &Rule{
Severity: rule.Severity,
Title: rule.Description,
Description: rule.RemediationMessage,
Id: rule.Id,
Expand Down Expand Up @@ -294,9 +306,11 @@ func evaluateRules(
OldFingerprint: oldFingerprint,
}

severity := CalculateSeverity(result.CategoryGroups, rule.Severity, output.IsLocal != nil && *output.IsLocal)
severityWeighting := CalculateSeverity(result.CategoryGroups, rule.Severity, output.IsLocal != nil && *output.IsLocal)
severity := severityWeighting.DisplaySeverity

if config.Report.Severity[severity] {
result.SeverityWeighting = severityWeighting
outputResults[severity] = append(outputResults[severity], result)
}
}
Expand Down Expand Up @@ -410,9 +424,12 @@ func BuildReportString(config settings.Config, output *types.Output[Results], li
return reportStr
}

func CalculateSeverity(groups []string, severity string, hasLocalDataTypes bool) string {
func CalculateSeverity(groups []string, severity string, hasLocalDataTypes bool) SeverityWeighting {
if severity == globaltypes.LevelWarning {
return globaltypes.LevelWarning
return SeverityWeighting{
RuleSeverity: severity,
DisplaySeverity: globaltypes.LevelWarning,
}
}

// highest sensitive data category
Expand Down Expand Up @@ -444,16 +461,27 @@ func CalculateSeverity(groups []string, severity string, hasLocalDataTypes bool)
triggerWeighting = 2
}

switch finalWeighting := ruleSeverityWeighting + (sensitiveDataCategoryWeighting * triggerWeighting); {
var displaySeverity string
finalWeighting := ruleSeverityWeighting + (sensitiveDataCategoryWeighting * triggerWeighting)
switch {
case finalWeighting >= 8:
return globaltypes.LevelCritical
displaySeverity = globaltypes.LevelCritical
case finalWeighting >= 5:
return globaltypes.LevelHigh
displaySeverity = globaltypes.LevelHigh
case finalWeighting >= 3:
return globaltypes.LevelMedium
displaySeverity = globaltypes.LevelMedium
default:
displaySeverity = globaltypes.LevelLow
}

return globaltypes.LevelLow
return SeverityWeighting{
RuleSeverity: severity,
SensitiveDataCategoryWeighting: sensitiveDataCategoryWeighting,
RuleSeverityWeighting: ruleSeverityWeighting,
TriggerWeighting: hasLocalDataTypes,
FinalWeighting: finalWeighting,
DisplaySeverity: displaySeverity,
}
}

func writeStatsToString(
Expand Down
2 changes: 1 addition & 1 deletion pkg/report/output/security/security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func TestTestGetOutputWithSeverity(t *testing.T) {
}

func TestCalculateSeverity(t *testing.T) {
res := []string{
res := []security.SeverityWeighting{
security.CalculateSeverity([]string{"PHI", "Personal Data"}, "low", true),
security.CalculateSeverity([]string{"Personal Data (Sensitive)"}, "low", false),
security.CalculateSeverity([]string{"Personal Data"}, "low", false),
Expand Down

0 comments on commit 60f82f3

Please sign in to comment.