Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add score system #260

Merged
merged 7 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This application is written in Go language and is based on the framework provide

The tool checks the content using a series of rules that are designed to identify a wide range of sensitive items such as AWS access token, Bitbucket Client ID, GitHub PAT etc. For a complete list of rules, see [docs/list-of-rules.md](docs/list-of-rules.md).

Additionally, the tool incorporates a scoring system based on the Common Vulnerability Scoring System (CVSS) to help prioritize remediation efforts.

# Installation

The following sections explain how to install 2ms using the following methods:
Expand Down Expand Up @@ -397,6 +399,8 @@ The result of the validation can be:

If the `--validate` flag is not provided, the validation field will be omitted from the output, or its value will be an empty string.

> **Note:** The validity check also impacts the score field. If the flag is not provided, the validity is assumed to be "unknown" in the score formula.

### Special Rules

Special rules are rules that are configured in 2ms but are not run as part of the default ruleset, usually because they are too noisy or too specific. You can use the `--add-special-rule` flag to add special rules by rule ID.
Expand Down
6 changes: 5 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ var report = reporting.Init()
var secretsChan = make(chan *secrets.Secret)
var secretsExtrasChan = make(chan *secrets.Secret)
var validationChan = make(chan *secrets.Secret)
var cvssScoreWithoutValidationChan = make(chan *secrets.Secret)

func Execute() (int, error) {
vConfig.SetEnvPrefix(envPrefix)
Expand Down Expand Up @@ -149,9 +150,12 @@ func preRun(pluginName string, cmd *cobra.Command, args []string) error {

if validateVar {
channels.WaitGroup.Add(1)
go processValidation(engine)
go processValidationAndScoreWithValidation(engine)
}

channels.WaitGroup.Add(1)
go processScoreWithoutValidation(engine)

return nil
}

Expand Down
24 changes: 21 additions & 3 deletions cmd/workers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"github.com/checkmarx/2ms/lib/secrets"
"sync"

"github.com/checkmarx/2ms/engine"
Expand Down Expand Up @@ -28,11 +29,14 @@ func processSecrets() {
secretsExtrasChan <- secret
if validateVar {
validationChan <- secret
} else {
cvssScoreWithoutValidationChan <- secret
}
report.Results[secret.ID] = append(report.Results[secret.ID], secret)
}
close(secretsExtrasChan)
close(validationChan)
close(cvssScoreWithoutValidationChan)
}

func processSecretsExtras() {
Expand All @@ -46,15 +50,29 @@ func processSecretsExtras() {
wgExtras.Wait()
}

func processValidation(engine *engine.Engine) {
func processValidationAndScoreWithValidation(engine *engine.Engine) {
defer channels.WaitGroup.Done()

wgValidation := &sync.WaitGroup{}
for secret := range validationChan {
wgValidation.Add(1)
go engine.RegisterForValidation(secret, wgValidation)
wgValidation.Add(2)
go func(secret *secrets.Secret, wg *sync.WaitGroup) {
engine.RegisterForValidation(secret, wg)
engine.Score(secret, true, wg)
}(secret, wgValidation)
}
wgValidation.Wait()

engine.Validate()
}

func processScoreWithoutValidation(engine *engine.Engine) {
defer channels.WaitGroup.Done()

wgScore := &sync.WaitGroup{}
for secret := range cvssScoreWithoutValidationChan {
wgScore.Add(1)
go engine.Score(secret, false, wgScore)
}
wgScore.Wait()
}
30 changes: 24 additions & 6 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package engine
import (
"crypto/sha1"
"fmt"
"github.com/checkmarx/2ms/engine/score"
"os"
"regexp"
"strings"
Expand All @@ -21,9 +22,10 @@ import (
)

type Engine struct {
rules map[string]config.Rule
detector detect.Detector
validator validation.Validator
rules map[string]config.Rule
rulesBaseRiskScore map[string]float64
detector detect.Detector
validator validation.Validator

ignoredIds []string
allowedValues []string
Expand All @@ -49,9 +51,11 @@ func Init(engineConfig EngineConfig) (*Engine, error) {
}

rulesToBeApplied := make(map[string]config.Rule)
rulesBaseRiskScore := make(map[string]float64)
keywords := []string{}
for _, rule := range *selectedRules {
rulesToBeApplied[rule.Rule.RuleID] = rule.Rule
rulesBaseRiskScore[rule.Rule.RuleID] = score.GetBaseRiskScore(rule.ScoreParameters.Category, rule.ScoreParameters.RuleType)
for _, keyword := range rule.Rule.Keywords {
keywords = append(keywords, strings.ToLower(keyword))
}
Expand All @@ -63,9 +67,10 @@ func Init(engineConfig EngineConfig) (*Engine, error) {
detector.MaxTargetMegaBytes = engineConfig.MaxTargetMegabytes

return &Engine{
rules: rulesToBeApplied,
detector: *detector,
validator: *validation.NewValidator(),
rules: rulesToBeApplied,
rulesBaseRiskScore: rulesBaseRiskScore,
detector: *detector,
validator: *validation.NewValidator(),

ignoredIds: engineConfig.IgnoredIds,
allowedValues: engineConfig.AllowedValues,
Expand Down Expand Up @@ -131,6 +136,15 @@ func (s *Engine) RegisterForValidation(secret *secrets.Secret, wg *sync.WaitGrou
s.validator.RegisterForValidation(secret)
}

func (s *Engine) Score(secret *secrets.Secret, validateFlag bool, wg *sync.WaitGroup) {
defer wg.Done()
validationStatus := secrets.UnknownResult // default validity
if validateFlag {
validationStatus = secret.ValidationStatus
}
secret.CvssScore = score.GetCvssScore(s.GetRuleBaseRiskScore(secret.RuleID), validationStatus)
}

func (s *Engine) Validate() {
s.validator.Validate()
}
Expand Down Expand Up @@ -191,3 +205,7 @@ func GetRulesCommand(engineConfig *EngineConfig) *cobra.Command {
},
}
}

func (s *Engine) GetRuleBaseRiskScore(ruleId string) float64 {
return s.rulesBaseRiskScore[ruleId]
}
10 changes: 8 additions & 2 deletions engine/rules/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ import (
"github.com/zricethezav/gitleaks/v8/detect"
)

type ScoreParameters struct {
Category RuleCategory
RuleType uint8
}

type Rule struct {
Rule config.Rule
Tags []string
Rule config.Rule
Tags []string
ScoreParameters ScoreParameters
}

// Copied from https://github.com/gitleaks/gitleaks/blob/463d24618fa42fc7629dc30c9744ebe36c5df1ab/cmd/generate/config/rules/rule.go
Expand Down
Loading