diff --git a/.2ms.yml b/.2ms.yml index b5be3ea8..66352079 100644 --- a/.2ms.yml +++ b/.2ms.yml @@ -2815,3 +2815,59 @@ ignore-result: - e41df94f30916531be27748175d28f851f0bacad # test data from only_custom_rules.json - faf4f974fad32cc5fb928a6d94adfe141007e925 # test data from custom_rules_secrets.txt - 20734a351c9be94da9240871fa50cf0959cf94ba # test data from default_plus_non_override_rules.json +- 05602cbb737e019ceaed1842a1fafa048f057e39 # test data from expectedReportWithIgnoredResults.json +- 0911ff5aa4fb801ef72e9aec41274ff6642fb6cb # test data from defaultPlusNonOverrideRules.json +- 09b533109d917e12c1474d6f6d7783561fb58d41 # test data from generic-api-keys.txt +- 0a7fc5f23b68249d5c3aa5486c1a2caebeaac45e # test data from onlyCustomRules.json +- 0bb0f0b115e826fc22e3058062fa5fb4a50f143b # test data from defaultPlusNonOverrideRules.json +- 0d741fcc834c41f5650c3bd30abd750dca238aa9 # test data from onlyCustomRules.json +- 10d99d86045759663429f5b5357877319fb62afb # test data from defaultPlusNonOverrideRules.json +- 1150ca30a572ee85c486f042afbd078d677be261 # test data from defaultPlusNonOverrideRules.json +- 1458d3fa01ddbd4681338b1f35dccb0b84160a02 # test data from onlyOverrideRules.json +- 1577a912b253138103b9aad14d8a97c827750eaf # test data from expectedReportWithIgnoredResults.json +- 16f450b8ed7bee3163796ccae2a86aff4516c740 # test data from defaultPlusAllCustomRules.json +- 1cbf4baac00a479cf5fad6bdba794b5f4b33e984 # test data from defaultPlusNonOverrideRules.json +- 1e29c09cf8f3a599a96f7d19659543a1587a0ee5 # test data from defaultPlusAllCustomRules.json +- 241414fa731c9f86094d79292244c46f13e8f0e5 # test data from onlyDefaultIgnoreCustomRules.json +- 2e021a1d6ea1b7c72177dae61918faf7f0ae902b # test data from generic-api-keys.txt +- 310b6939ef5aa98f3a84130b777565470f5a03d6 # test data from onlyOverrideRules.json +- 3881ff90e41473873bfbe2b9de1165c3bc642b3c # test data from expectedReportWithIgnoredResults.json +- 429cd5eb6407c41b08eb4955f9b1775abe50bd5c # test data from defaultPlusAllCustomRules.json +- 42d8be555f4cf15d452f3bd9b0b2e9549a012597 # test data from onlyOverrideRules.json +- 4303f8965b423d7e652a58d480582cc4ba4a54a8 # test data from defaultPlusAllCustomRules.json +- 4607cbe90fbc7552b0977298a96063ef3aec39c9 # test data from onlyCustomRules.json +- 4ddac4f4b08377912a5de2323b6e376d63d80f3f # test data from expectedReportWithIgnoredResults.json +- 5376ee01f078e3e9e2f94a174fef9a72e09caaaf # test data from defaultPlusAllCustomRules.json +- 53fceac899c26c7ef5f008eb5693f95ffe07e7c4 # test data from expectedReportWithIgnoredResults.json +- 5a4af3cfa60282d73f0864b2c8acc1403c832a33 # test data from onlyCustomRules.json +- 5b7ff548f191f5437026d0e507f6b43cd037a320 # test data from defaultPlusAllCustomRules.json +- 5ba90ad878a6770da26c654005e875aa4a373190 # test data from onlyOverrideRules.json +- 5d1f1d3f613ae7862047c7c83170fa05fd244e43 # test data from defaultPlusAllCustomRules.json +- 6ae0b6fa63c9827e787c4994107ee0cac421eb60 # test data from expectedReportWithIgnoredResults.json +- 6b2b89be362c7d0ec4f06c615304b032f41250d0 # test data from expectedReportWithIgnoredResults.json +- 6c0d8ef2eb9f751288ecf36a1024d612c1e27ab3 # test data from onlyCustomRules.json +- 6c14ac093ccfdf9d878c79b4ea6376f0e4815a82 # test data from onlyCustomRules.json +- 7345100bea8e537d8aa434d739a3c799ce4d2a62 # test data from onlyOverrideRules.json +- 74eb842cf07b07fbe25093536654e7288b69c49a # test data from defaultPlusNonOverrideRules.json +- 7903a5376d2c1179125c726e72dc96637dd49256 # test data from defaultPlusAllCustomRules.json +- 8314bdcfa48aa5e09eca4ddc30c0d4715d5388dc # test data from expectedReportWithIgnoredResults.json +- 86963a531def427b48e008d4333803a1ed857094 # test data from onlyOverrideRules.json +- 8dfcd4aa7d4620bb02493b37ef82373dd940a662 # test data from onlyDefaultIgnoreCustomRules.json +- 8f0baeb46dc7357b2ad677811cefe24bf27a45ed # test data from onlyOverrideRules.json +- 9850a1398255e717ec5a76e08b861b0d3da87402 # test data from expectedReportWithIgnoredResults.json +- 9afb2c9aece1398d6286625ef3a06e15b26147d4 # test data from defaultPlusNonOverrideRules.json +- a415ed511dec492ee023608d76bc4e66934381de # test data from defaultPlusAllCustomRules.json +- a49f52bcec28c23b51d9082c2001ed2ef401edc3 # test data from onlyCustomRules.json +- b061d2c11bea74a82eb3fec7830843a2b3572d0d # test data from expectedReportWithIgnoredResults.json +- bdcfaa5f2c91bf942042a507925e320dd43816ea # test data from onlyCustomRules.json +- c028d45a6be26e44b7cf71ed25dd11ca0100800c # test data from expectedReportWithIgnoredResults.json +- c66c28ce94415cd6f3efe40130f2ab08a7214087 # test data from onlyCustomNoOverrideRules.json +- c6b5416e40ffd8bb1cf7d9d0c7965431144e00f8 # test data from defaultPlusAllCustomRules.json +- ced63354eaca48df7cdf8ac571fdb6e25cefbaae # test data from onlyOverrideRules.json +- d07771fdf69f59c61607f7994e30694f4ba25177 # test data from onlyCustomNoOverrideRules.json +- d53aff50117f07d339749ebdc5556b117d82e5f5 # test data from defaultPlusNonOverrideRules.json +- dac4cd38432590cee87d46e3448c874b7e2c70c5 # test data from defaultPlusAllCustomRules.json +- dfbbddd73932c7210bfc953917c5b485e3ee7535 # test data from defaultPlusNonOverrideRules.json +- f0544e7e9e25a6223cd10c37a445bfd4a4641337 # test data from defaultPlusAllCustomRules.json +- f93dd5ab91efe7b70381afe3d4ebd1b26e628ac9 # test data from defaultPlusNonOverrideRules.json +- ff81ccd6553feaf5fbbf573eac2a0042d05aaee4 # test data from defaultPlusNonOverrideRules.json diff --git a/README.md b/README.md index bfbf6e93..95ea38d2 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,71 @@ docker run -v $(pwd)/.2ms.yml:/app/.2ms.yml checkmarx/2ms \ [![asciicast](https://asciinema.org/a/n8RHL4v6vI87uiUPZ9I7CgfYy.svg)](https://asciinema.org/a/n8RHL4v6vI87uiUPZ9I7CgfYy) +## Custom Rules File + +We support custom rules, which are user defined rules that can be passed via a custom rules file using the `--custom-rules-path` flag. The custom rules file format and extension can be YAML or JSON. + +Custom rules can be: + +- **Overrides** - if a rule present in the file shares the same ruleId as a default rule of 2ms, the rule present in the file will replace (override) the default rule in the scan. + - Note: If a rule is overridden, it will simply take all fields from the rule as defined in the file. You must include all fields that you want to be defined, otherwise they will be nil/empty. + +- **New rules** - if a rule does not share ruleId with a default rule, it will be appended to the list of rules used in the scan. + +Custom rules work properly with --rule and --ignore-rule flags. Rules can be selected/ignored by ruleId, ruleName and tag + +Regardless of being an override or new rule, a custom rule has the following required fields: +- ruleId - unique identifier of the rule +- ruleName - human readable name of the rule +- regex - regex pattern used to identify the secret + +Other fields are optional and can be seen in the example bellow of a file with a custom rule + +**YAML Example:** +```yaml +- ruleId: 01ab7659-d25a-4a1c-9f98-dee9d0cf2e70 # REQUIRED: unique id, must match default rule id to override that default rule. Rule ids can be used as values in --rule and --ignore-rule flags + ruleName: Custom-Api-Key # should be human-readable name. If left empty for new rule, ruleName will take the value of ruleId. If left empty for override, default rule name will be considered. Rule names can be used as values in --rule and --ignore-rule flags + description: Custom rule + regex: (?i)\b\w*secret\w*\b\s*:?=\s*["']?([A-Za-z0-9/_+=-]{8,150})["']? # REQUIRED: golang regular expression used to find secrets. If capture group is present in regex, it used to find the secret, otherwise whole regex is used. which group is considered the secret can be defined with secretGroup + keywords: # Keywords are used for pre-regex check filtering. Rules that contain keywords will perform a quick string compare check to make sure the keyword(s) are in the content being scanned. + - access + - api + entropy: 3.5 # shannon entropy, measures how random a string is. The value will be higher the more random a string is. Default rules that use entropy have values between 2.0 and 4.5. Leave empty to consider matches regardless of entropy + secretGroup: 1 # defines which capture group of regex match is considered the secret. Is also used as the group that will have its entropy checked if `entropy` is set. Can be left empty, in which case the first capture group to match will be considered the secret + path: (?i)\.(?:tf|hcl)$ # regex to limit the rule to specific file paths. For example, only .tf and .hcl files + severity: High # severity, can only be one of [Critical, High, Medium, Low, Info] + tags: # identifiers for the rule, tags can be used as values of --rule and --ignore-rule flags + - api-key + scoreParameters: # scoreParameters can be omitted for overrides, in which case the respective default rule scoreParameters will be considered + category: General # category of the rule, should be a string of type ruledefine.RuleCategory. Impacts cvss score + ruleType: 4 # can go from 4 to 0, 4 being most severe. For overrides, if Category is defined, ruleType also needs to be defined, or otherwise it will be considered 0. Impacts cvss score + disableValidation: false # if true, disables validity check for this rule, regardless of --validate flag + deprecated: false # if true, the rule will not be used in the scan, regardless of --rule flag + allowLists: # allowed values to ignore if matched + - description: Allowlist for Custom Rule + matchCondition: OR # determines whether all criteria in the allowList must match. Can be AND or OR. Defaults to OR if not specified + regexTarget: match - # determines whether the regexes in allowList are tested against the rule.Regex match or the full line being scanned. Can be 'match' or 'line'. Defaults to 'match' if not specified + regexes: # allowed regex patterns + - (?i)(?:access(?:ibility|or)|access[_.-]?id|random[_.-]?access|api[_.-]?(?:id|name|version)|rapid|capital|[a-z0-9-]*?api[a-z0-9-]*?:jar:|author|X-MS-Exchange-Organization-Auth|Authentication-Results|(?:credentials?[_.-]?id|withCredentials)|(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|1?\d?\d)){3}|(?:bucket|foreign|hot|idx|natural|primary|pub(?:lic)?|schema|sequence)[_.-]?key|(?:turkey)|key[_.-]?(?:alias|board|code|frame|id|length|mesh|name|pair|press(?:ed)?|ring|selector|signature|size|stone|storetype|word|up|down|left|right)|KeyVault(?:[A-Za-z]*?(?:Administrator|Reader|Contributor|Owner|Operator|User|Officer))\s*[:=]\s*['"]?[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}['"]?|key[_.-]?vault[_.-]?(?:id|name)|keyVaultToStoreSecrets|key(?:store|tab)[_.-]?(?:file|path)|issuerkeyhash|(?-i:[DdMm]onkey|[DM]ONKEY)|keying|(?:secret)[_.-]?(?:length|name|size)|UserSecretsId|(?:csrf)[_.-]?token|(?:io\.jsonwebtoken[ + \t]?:[ + \t]?[\w-]+)|(?:api|credentials|token)[_.-]?(?:endpoint|ur[il])|public[_.-]?token|(?:key|token)[_.-]?file|(?-i:(?:[A-Z_]+=\n[A-Z_]+=|[a-z_]+=\n[a-z_]+=)(?:\n|\z))|(?-i:(?:[A-Z.]+=\n[A-Z.]+=|[a-z.]+=\n[a-z.]+=)(?:\n|\z))) + stopWords: # stop words that if found in the secret, will discard the finding. Stop words are searched on the secret, which can be either the full regex match or the capture group if any is defined in the rule regex + - 000000, + - 6fe4476ee5a1832882e326b506d14126 + paths: # paths that can be ignored for this allowList + - \.bb$ + - \.bbappend$ + - \.bbclass$ + - \.inc$ + - matchCondition: AND + regexTarget: line + regexes: + - LICENSE[^=]*=\s*"[^"]+ + - LIC_FILES_CHKSUM[^=]*=\s*"[^"]+ + - SRC[^=]*=\s*"[a-zA-Z0-9]+ +``` + + ## Scan Commands The following sections describe the arguments used for scanning each of the supported platforms. diff --git a/cmd/config_test.go b/cmd/config_test.go index bfaed795..b2174fc0 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -299,29 +299,25 @@ func TestCustomRulesFlag(t *testing.T) { }, Tags: []string{"security", "credentials"}, ScoreParameters: ruledefine.ScoreParameters{ - Category: "Secrets", + Category: "General", RuleType: 2, }, DisableValidation: true, Deprecated: true, }, { - RuleID: "b47a1995-6572-41bb-b01d-d215b43ab089", - RuleName: "mock-rule2", - Description: "Match API keys", - Regex: "[A-Za-z0-9]{40}", - Keywords: []string{"api", "key"}, - Entropy: 4.0, - Path: "config/api_keys.yaml", - SecretGroup: 0, - Severity: "Medium", - OldSeverity: "High", - AllowLists: []*ruledefine.AllowList{}, - Tags: []string{"api", "custom"}, - ScoreParameters: ruledefine.ScoreParameters{ - Category: "API", - RuleType: 1, - }, + RuleID: "b47a1995-6572-41bb-b01d-d215b43ab089", + RuleName: "mock-rule2", + Description: "Match API keys", + Regex: "[A-Za-z0-9]{40}", + Keywords: []string{"api", "key"}, + Entropy: 4.0, + Path: "config/api_keys.yaml", + SecretGroup: 0, + Severity: "Medium", + OldSeverity: "High", + AllowLists: []*ruledefine.AllowList{}, + Tags: []string{"api", "custom"}, DisableValidation: false, Deprecated: false, }, diff --git a/cmd/main_test.go b/cmd/main_test.go index 32ea615b..2e970848 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -58,7 +58,7 @@ func TestPreRun(t *testing.T) { expectedPreRunErr: nil, }, { - name: "errors on custom rules, rule name, id, regex missing", + name: "errors on custom rules, rule id and regex missing", engineConfigVar: engine.EngineConfig{ CustomRules: []*ruledefine.Rule{ { @@ -75,12 +75,11 @@ func TestPreRun(t *testing.T) { expectedPreRunErr: nil, expectedContainsInitErrs: []error{ fmt.Errorf("rule#0: missing ruleID"), - fmt.Errorf("rule#0: missing ruleName"), fmt.Errorf("rule#0: missing regex"), }, }, { - name: "errors on custom rules, regex and severity invalid", + name: "errors on custom rules, regex, severity and score parameters invalid", engineConfigVar: engine.EngineConfig{ CustomRules: []*ruledefine.Rule{ { @@ -89,6 +88,10 @@ func TestPreRun(t *testing.T) { Description: "Match passwords", Regex: "[A-Za-z0-9]{32})", Severity: "mockSeverity", + ScoreParameters: ruledefine.ScoreParameters{ + Category: "mockCategory", + RuleType: 10, + }, }, { RuleID: "b47a1995-6572-41bb-b01d-d215b43ab089", @@ -103,6 +106,9 @@ func TestPreRun(t *testing.T) { fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid regex"), fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid severity:" + " mockSeverity not one of ([Critical High Medium Low Info])"), + fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid category:" + + " mockCategory not an acceptable category of type RuleCategory"), + fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid rule type: 10 not an acceptable uint8 value, maximum is 4"), }, }, { diff --git a/cmd/testData/customRulesValid.json b/cmd/testData/customRulesValid.json index 0fadfe6d..86750bb2 100644 --- a/cmd/testData/customRulesValid.json +++ b/cmd/testData/customRulesValid.json @@ -22,7 +22,7 @@ ], "tags": ["security", "credentials"], "scoreParameters": { - "category": "Secrets", + "category": "General", "ruleType": 2 }, "disableValidation": true, @@ -41,10 +41,6 @@ "oldSeverity": "High", "allowLists": [], "tags": ["api", "custom"], - "scoreParameters": { - "category": "API", - "ruleType": 1 - }, "disableValidation": false, "deprecated": false } diff --git a/cmd/testData/customRulesValid.yaml b/cmd/testData/customRulesValid.yaml index 5c11d1c5..ab49ca8a 100644 --- a/cmd/testData/customRulesValid.yaml +++ b/cmd/testData/customRulesValid.yaml @@ -26,7 +26,7 @@ - security - credentials scoreParameters: - category: Secrets + category: General ruleType: 2 disableValidation: true deprecated: true @@ -47,8 +47,5 @@ tags: - api - custom - scoreParameters: - category: API - ruleType: 1 disableValidation: false deprecated: false \ No newline at end of file diff --git a/engine/engine.go b/engine/engine.go index ecc83d6f..6b62c2a4 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -6,6 +6,7 @@ import ( "crypto/hkdf" "crypto/sha256" "encoding/hex" + "errors" "fmt" "io" "os" @@ -45,6 +46,12 @@ var ( ErrNoRulesSelected = fmt.Errorf("no rules were selected") ErrFailedToCompileRegexRule = fmt.Errorf("failed to compile regex rule") + errMissingRuleID = fmt.Errorf("missing ruleID") + errMissingRegex = fmt.Errorf("missing regex") + errInvalidRegex = fmt.Errorf("invalid regex") + errInvalidSeverity = fmt.Errorf("invalid severity") + errInvalidCategory = fmt.Errorf("invalid category") + errInvalidRuleType = fmt.Errorf("invalid rule type") ) type DetectorConfig struct { @@ -149,7 +156,7 @@ func Init(engineConfig *EngineConfig, opts ...EngineOption) (IEngine, error) { } func initEngine(engineConfig *EngineConfig, opts ...EngineOption) (*Engine, error) { - err := rules.CheckRulesRequiredFields(engineConfig.CustomRules) + err := CheckRulesRequiredFields(engineConfig.CustomRules) if err != nil { return nil, fmt.Errorf("failed to load custom rules: %w", err) } @@ -827,3 +834,63 @@ func isSecretFromConfluenceResourceIdentifier(secretRuleID, secretLine, secretMa re := regexp.MustCompile(pat) return re.MatchString(secretLine) } + +// CheckRulesRequiredFields checks that required fields are present in the Rule. +// This is meant for user defined rules, default rules have more strict checks in unit tests +func CheckRulesRequiredFields(rulesToCheck []*ruledefine.Rule) error { + var err error + for i, rule := range rulesToCheck { + if rule.RuleID == "" { + err = errors.Join(err, buildCustomRuleError(i, rule, errMissingRuleID)) + } + + if rule.Regex == "" { + err = errors.Join(err, buildCustomRuleError(i, rule, errMissingRegex)) + } else { + if _, errRegex := regexp.Compile(rule.Regex); errRegex != nil { + invalidRegexError := fmt.Errorf("%w: %v", errInvalidRegex, errRegex) + err = errors.Join(err, buildCustomRuleError(i, rule, invalidRegexError)) + } + } + + if rule.Severity != "" { + if !slices.Contains(ruledefine.SeverityOrder, rule.Severity) { + invalidSeverityError := fmt.Errorf("%w: %s not one of (%s)", errInvalidSeverity, rule.Severity, ruledefine.SeverityOrder) + err = errors.Join(err, buildCustomRuleError(i, rule, invalidSeverityError)) + } + } + + if rule.ScoreParameters.Category != "" { + if _, ok := score.CategoryScoreMap[rule.ScoreParameters.Category]; !ok { + invalidCategoryError := fmt.Errorf("%w: %s not an acceptable category of type RuleCategory", + errInvalidCategory, rule.ScoreParameters.Category) + err = errors.Join(err, buildCustomRuleError(i, rule, invalidCategoryError)) + } + } + + if rule.ScoreParameters.RuleType != 0 { + if rule.ScoreParameters.RuleType > score.RuleTypeMaxValue { + invalidRuleTypeError := fmt.Errorf("%w: %d not an acceptable uint8 value, maximum is %d", + errInvalidRuleType, rule.ScoreParameters.RuleType, score.RuleTypeMaxValue) + err = errors.Join(err, buildCustomRuleError(i, rule, invalidRuleTypeError)) + } + } + } + + // Add a newline at start of error if it's not nil, for better presentation in output + if err != nil { + err = fmt.Errorf("\n%w", err) + } + + return err +} + +func buildCustomRuleError(ruleIndex int, rule *ruledefine.Rule, issue error) error { + if rule.RuleID == "" { + if rule.RuleName == "" { + return fmt.Errorf("rule#%d: %w", ruleIndex, issue) + } + return fmt.Errorf("rule#%d;RuleName-%s: %w", ruleIndex, rule.RuleName, issue) + } + return fmt.Errorf("rule#%d;RuleID-%s: %w", ruleIndex, rule.RuleID, issue) +} diff --git a/engine/rules/ruledefine/rule.go b/engine/rules/ruledefine/rule.go index cf24f1c9..bd5ccd12 100644 --- a/engine/rules/ruledefine/rule.go +++ b/engine/rules/ruledefine/rule.go @@ -91,7 +91,7 @@ type Rule struct { Path string `json:"path" yaml:"path"` // present in some gitleaks secrets (regex) SecretGroup int `json:"secretGroup" yaml:"secretGroup"` //nolint:lll // SecretGroup is used to extract secret from regex match and used as the group that will have its entropy checked if `entropy` is set. Severity Severity `json:"severity" yaml:"severity"` - OldSeverity string `json:"oldSeverity" yaml:"oldSeverity"` // fallback for when critical is not enabled + OldSeverity string `json:"oldSeverity" yaml:"oldSeverity"` //nolint:lll // fallback for when critical is not enabled, has no effect on open source AllowLists []*AllowList `json:"allowLists" yaml:"allowLists"` Tags []string `json:"tags" yaml:"tags"` ScoreParameters ScoreParameters `json:"scoreParameters" yaml:"scoreParameters"` // used for ASPM @@ -107,3 +107,7 @@ type AllowList struct { // For patterns that are allowed to be ignored Regexes []string `json:"regexes" yaml:"regexes"` StopWords []string `json:"stopWords" yaml:"stopWords"` // stop words that are allowed to be ignored } + +func (r *Rule) CreateRuleNameFromRuleID() { + r.RuleName = r.RuleID +} diff --git a/engine/rules/rules.go b/engine/rules/rules.go index aa86df63..16c416bc 100644 --- a/engine/rules/rules.go +++ b/engine/rules/rules.go @@ -1,25 +1,12 @@ package rules import ( - "errors" - "fmt" "strings" - "regexp" - "slices" - "github.com/checkmarx/2ms/v4/engine/rules/ruledefine" "github.com/rs/zerolog/log" ) -var ( - errMissingRuleID = fmt.Errorf("missing ruleID") - errMissingRuleName = fmt.Errorf("missing ruleName") - errMissingRegex = fmt.Errorf("missing regex") - errInvalidRegex = fmt.Errorf("invalid regex") - errInvalidSeverity = fmt.Errorf("invalid severity") -) - func GetDefaultRules(includeDeprecated bool) []*ruledefine.Rule { //nolint:funlen // This function contains all rule definitions allRules := []*ruledefine.Rule{ ruledefine.AdafruitAPIKey(), @@ -308,6 +295,10 @@ func FilterRules(selectedList, ignoreList, specialList []string, selectedRules = GetDefaultRules(false) + // Fill in missing fields in custom rules if they match default rules. + // Needs to run before selection/ignoring so that if rule names are used in selected/ignored, overrides will be selected/ignored properly + completeOverridesWithDefaultFields(customRules, selectedRules) + if len(selectedList) > 0 { selectedRules = selectRules(selectedRules, selectedList) customRules = selectRules(customRules, selectedList) @@ -317,6 +308,8 @@ func FilterRules(selectedList, ignoreList, specialList []string, customRules = ignoreRules(customRules, ignoreList) } + // Adding custom rules needs to happen after selection/ignoring so that if tags of overrides are used in ignore, + // the default Rule can still run (if not ignored itself) selectedRules = addCustomRules(selectedRules, customRules) if len(specialList) > 0 { @@ -349,55 +342,33 @@ func addCustomRules(selectedRules, customRules []*ruledefine.Rule) []*ruledefine } } if !ruleMatch { + if customRule.RuleName == "" { + customRule.CreateRuleNameFromRuleID() + } selectedRules = append(selectedRules, customRule) } } return selectedRules } -// CheckRulesRequiredFields checks that required fields are present in the Rule. -// This is meant for user defined rules, default rules have more strict checks in unit tests -func CheckRulesRequiredFields(rulesToCheck []*ruledefine.Rule) error { - var err error - for i, rule := range rulesToCheck { - if rule.RuleID == "" { - err = errors.Join(err, buildCustomRuleError(i, rule, errMissingRuleID)) - } - if rule.RuleName == "" { - err = errors.Join(err, buildCustomRuleError(i, rule, errMissingRuleName)) - } - - if rule.Regex == "" { - err = errors.Join(err, buildCustomRuleError(i, rule, errMissingRegex)) - } else { - if _, errRegex := regexp.Compile(rule.Regex); errRegex != nil { - invalidRegexError := fmt.Errorf("%w: %v", errInvalidRegex, errRegex) - err = errors.Join(err, buildCustomRuleError(i, rule, invalidRegexError)) - } - } +// completeOverridesWithDefaultFields fills in some missing fields in custom rules if they match default rules by ruleID +func completeOverridesWithDefaultFields(customRules, defaultRules []*ruledefine.Rule) { + for _, customRule := range customRules { + for _, defaultRule := range defaultRules { + if defaultRule.RuleID == customRule.RuleID { + if customRule.RuleName == "" { + customRule.RuleName = defaultRule.RuleName + } - if rule.Severity != "" { - if !slices.Contains(ruledefine.SeverityOrder, rule.Severity) { - invalidSeverityError := fmt.Errorf("%w: %s not one of (%s)", errInvalidSeverity, rule.Severity, ruledefine.SeverityOrder) - err = errors.Join(err, buildCustomRuleError(i, rule, invalidSeverityError)) + if customRule.ScoreParameters.Category == "" { + customRule.ScoreParameters.Category = defaultRule.ScoreParameters.Category + // only replace with default ruleType if category wasn't defined, otherwise assume user set RuleType at 0 intentionally + if customRule.ScoreParameters.RuleType == 0 { + customRule.ScoreParameters.RuleType = defaultRule.ScoreParameters.RuleType + } + } + break } } } - - // Add a newline at start of error if it's not nil, for better presentation in output - if err != nil { - err = fmt.Errorf("\n%w", err) - } - - return err -} - -func buildCustomRuleError(ruleIndex int, rule *ruledefine.Rule, issue error) error { - if rule.RuleID == "" { - if rule.RuleName == "" { - return fmt.Errorf("rule#%d: %w", ruleIndex, issue) - } - return fmt.Errorf("rule#%d;RuleName-%s: %w", ruleIndex, rule.RuleName, issue) - } - return fmt.Errorf("rule#%d;RuleID-%s: %w", ruleIndex, rule.RuleID, issue) } diff --git a/engine/rules/rules_test.go b/engine/rules/rules_test.go index be97f727..f0086103 100644 --- a/engine/rules/rules_test.go +++ b/engine/rules/rules_test.go @@ -6,6 +6,7 @@ import ( "github.com/checkmarx/2ms/v4/engine/rules/ruledefine" "github.com/google/uuid" + "github.com/jinzhu/copier" "github.com/stretchr/testify/assert" "github.com/zricethezav/gitleaks/v8/config" "github.com/zricethezav/gitleaks/v8/regexp" @@ -161,15 +162,35 @@ func Test_CustomRules(t *testing.T) { genericCredentialOverride := &ruledefine.Rule{ RuleID: ruledefine.GenericCredential().RuleID, - RuleName: ruledefine.GenericCredential().RuleID, Description: "Custom rule for Generic API Key", Regex: "[A-Za-z0-9]{32}", Tags: []string{"custom"}, } + // expected rule should have rule name and score parameters with values, copied from default rule into override + expectedGenericCredentialOverride := &ruledefine.Rule{ + RuleID: ruledefine.GenericCredential().RuleID, + RuleName: ruledefine.GenericCredential().RuleName, + Description: "Custom rule for Generic API Key", + Regex: "[A-Za-z0-9]{32}", + Tags: []string{"custom"}, + ScoreParameters: ruledefine.GenericCredential().ScoreParameters, + } + + completeGenericCredentialOverride := &ruledefine.Rule{ + RuleID: ruledefine.GenericCredential().RuleID, + RuleName: "Custom Generic API Key", + Description: "Custom rule for Generic API Key", + Regex: "[A-Za-z0-9]{32}", + Tags: []string{"custom"}, + ScoreParameters: ruledefine.ScoreParameters{ + Category: ruledefine.CategorySaaS, + RuleType: 1, + }, + } deprecatedGenericCredentialOverride := &ruledefine.Rule{ RuleID: ruledefine.GenericCredential().RuleID, - RuleName: ruledefine.GenericCredential().RuleID, + RuleName: ruledefine.GenericCredential().RuleName, Description: "Custom rule for Generic API Key", Regex: "[A-Za-z0-9]{40}", // this regex is different to be able to distinguish with non deprecated rule Tags: []string{"custom"}, @@ -202,7 +223,6 @@ func Test_CustomRules(t *testing.T) { ignoreList []string specialList []string customRules []*ruledefine.Rule - onlyCustomRules bool expectedPresentRules []*ruledefine.Rule expectedMissingRules []*ruledefine.Rule expectedLen int @@ -212,11 +232,11 @@ func Test_CustomRules(t *testing.T) { selectedList: []string{}, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule}, + cloneRule(genericCredentialOverride), + cloneRule(customRule)}, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -228,11 +248,27 @@ func Test_CustomRules(t *testing.T) { selectedList: []string{genericCredentialOverride.RuleID}, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), + }, + expectedPresentRules: []*ruledefine.Rule{ + cloneRule(expectedGenericCredentialOverride), + }, + expectedMissingRules: []*ruledefine.Rule{ + ruledefine.GenericCredential(), + }, + expectedLen: 1, + }, + { + name: "select only complete custom generic-api-key with ruleID", + selectedList: []string{completeGenericCredentialOverride.RuleID}, + ignoreList: []string{}, + customRules: []*ruledefine.Rule{ + cloneRule(completeGenericCredentialOverride), + cloneRule(customRule), }, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, + cloneRule(completeGenericCredentialOverride), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -240,15 +276,31 @@ func Test_CustomRules(t *testing.T) { expectedLen: 1, }, { - name: "select only custom generic-api-key with ruleName", - selectedList: []string{genericCredentialOverride.RuleName}, + name: "select only custom generic-api-key with ruleName, even if missing ruleName in custom rule definition", + selectedList: []string{ruledefine.GenericCredential().RuleName}, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), }, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, + cloneRule(expectedGenericCredentialOverride), + }, + expectedMissingRules: []*ruledefine.Rule{ + ruledefine.GenericCredential(), + }, + expectedLen: 1, + }, + { + name: "select only complete custom generic-api-key with ruleName", + selectedList: []string{expectedGenericCredentialOverride.RuleName}, + ignoreList: []string{}, + customRules: []*ruledefine.Rule{ + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), + }, + expectedPresentRules: []*ruledefine.Rule{ + cloneRule(expectedGenericCredentialOverride), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -257,13 +309,14 @@ func Test_CustomRules(t *testing.T) { }, { name: "select only custom generic-api-key with ruleName in lowercase", - selectedList: []string{strings.ToLower(genericCredentialOverride.RuleName)}, + selectedList: []string{strings.ToLower(ruledefine.GenericCredential().RuleName)}, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule}, + cloneRule(genericCredentialOverride), + cloneRule(customRule), + }, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, + cloneRule(expectedGenericCredentialOverride), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -275,10 +328,11 @@ func Test_CustomRules(t *testing.T) { selectedList: []string{genericCredentialOverride.Tags[0]}, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule}, + cloneRule(genericCredentialOverride), + cloneRule(customRule), + }, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, + cloneRule(expectedGenericCredentialOverride), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -293,11 +347,11 @@ func Test_CustomRules(t *testing.T) { }, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule}, + cloneRule(genericCredentialOverride), + cloneRule(customRule)}, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -312,11 +366,12 @@ func Test_CustomRules(t *testing.T) { }, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule}, + cloneRule(genericCredentialOverride), + cloneRule(customRule), + }, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -332,12 +387,13 @@ func Test_CustomRules(t *testing.T) { }, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule}, + cloneRule(genericCredentialOverride), + cloneRule(customRule), + }, expectedPresentRules: []*ruledefine.Rule{ ruledefine.CurlBasicAuth(), - genericCredentialOverride, - customRule, + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -353,12 +409,13 @@ func Test_CustomRules(t *testing.T) { }, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule}, + cloneRule(genericCredentialOverride), + cloneRule(customRule), + }, expectedPresentRules: []*ruledefine.Rule{ ruledefine.CurlBasicAuth(), - genericCredentialOverride, - customRule, + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -370,10 +427,11 @@ func Test_CustomRules(t *testing.T) { selectedList: []string{genericCredentialOverride.RuleID}, ignoreList: []string{}, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - deprecatedGenericCredentialOverride}, + cloneRule(genericCredentialOverride), + cloneRule(deprecatedGenericCredentialOverride), + }, expectedPresentRules: []*ruledefine.Rule{ - genericCredentialOverride, + cloneRule(expectedGenericCredentialOverride), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), @@ -388,13 +446,13 @@ func Test_CustomRules(t *testing.T) { customRule.RuleID, }, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), - genericCredentialOverride, - customRule, + cloneRule(deprecatedGenericCredentialOverride), + cloneRule(customRule), }, expectedLen: rulesCount - 1, }, @@ -405,15 +463,16 @@ func Test_CustomRules(t *testing.T) { genericCredentialOverride.RuleID, }, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), }, expectedPresentRules: []*ruledefine.Rule{ - customRule, + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), - genericCredentialOverride, + cloneRule(genericCredentialOverride), + cloneRule(expectedGenericCredentialOverride), }, expectedLen: rulesCount, }, @@ -428,13 +487,14 @@ func Test_CustomRules(t *testing.T) { customRule.RuleID, }, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedLen: 0, }, @@ -449,13 +509,14 @@ func Test_CustomRules(t *testing.T) { customRule.RuleName, }, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedLen: 0, }, @@ -470,13 +531,14 @@ func Test_CustomRules(t *testing.T) { customRule.RuleName, }, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedLen: 0, }, @@ -491,15 +553,16 @@ func Test_CustomRules(t *testing.T) { customRule.Tags[0], }, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), }, expectedPresentRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), }, expectedMissingRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedLen: 1, }, @@ -514,8 +577,8 @@ func Test_CustomRules(t *testing.T) { customRule.RuleID, }, customRules: []*ruledefine.Rule{ - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(customRule), }, specialList: []string{ ruledefine.HardcodedPassword().RuleName, @@ -525,8 +588,9 @@ func Test_CustomRules(t *testing.T) { }, expectedMissingRules: []*ruledefine.Rule{ ruledefine.GenericCredential(), - genericCredentialOverride, - customRule, + cloneRule(genericCredentialOverride), + cloneRule(expectedGenericCredentialOverride), + cloneRule(customRule), }, expectedLen: 1, }, @@ -538,11 +602,11 @@ func Test_CustomRules(t *testing.T) { assert.Equal(t, tt.expectedLen, len(rules)) for _, expectedRule := range tt.expectedPresentRules { - assert.Contains(t, rules, expectedRule, "can't find expected rule") + assert.Contains(t, rules, expectedRule, "can't find expected rule %s", expectedRule.RuleID) } for _, expectedMissingRule := range tt.expectedMissingRules { - assert.NotContains(t, rules, expectedMissingRule, "found unexpected rule") + assert.NotContains(t, rules, expectedMissingRule, "found unexpected rule %s", expectedMissingRule.RuleID) } }) } @@ -725,3 +789,9 @@ func TestIgnoreRules(t *testing.T) { }) } } + +func cloneRule(r *ruledefine.Rule) *ruledefine.Rule { + var ruleCopy ruledefine.Rule + _ = copier.CopyWithOption(&ruleCopy, r, copier.Option{DeepCopy: true}) + return &ruleCopy +} diff --git a/engine/score/score.go b/engine/score/score.go index 36b7c9a8..3b458fa4 100644 --- a/engine/score/score.go +++ b/engine/score/score.go @@ -10,47 +10,8 @@ import ( "github.com/zricethezav/gitleaks/v8/config" ) -type scorer struct { - rulesBaseRiskScore map[string]float64 - withValidation bool - keywords map[string]struct{} - twomsRulesToBeApplied map[string]ruledefine.Rule - gitleaksRulesToBeApplied map[string]config.Rule // same rules from twomsRulesToBeApplied, converted to gitleaks format -} - -func NewScorer(selectedRules []*ruledefine.Rule, withValidation bool) *scorer { - twomsRulesToBeApplied := make(map[string]ruledefine.Rule) - gitleaksRulesToBeApplied := make(map[string]config.Rule) - rulesBaseRiskScore := make(map[string]float64) - keywords := make(map[string]struct{}) - for _, rule := range selectedRules { - twomsRulesToBeApplied[rule.RuleID] = *rule - gitleaksRulesToBeApplied[rule.RuleID] = *ruledefine.TwomsToGitleaksRule(rule) - rulesBaseRiskScore[rule.RuleID] = GetBaseRiskScore(rule.ScoreParameters.Category, rule.ScoreParameters.RuleType) - for _, keyword := range rule.Keywords { - keywords[strings.ToLower(keyword)] = struct{}{} - } - } - return &scorer{ - rulesBaseRiskScore: rulesBaseRiskScore, - withValidation: withValidation, - keywords: keywords, - twomsRulesToBeApplied: twomsRulesToBeApplied, - gitleaksRulesToBeApplied: gitleaksRulesToBeApplied, - } -} - -func (s *scorer) AssignScoreAndSeverity(secret *secrets.Secret) { - validationStatus := secrets.UnknownResult // default validity - if s.withValidation { - validationStatus = secret.ValidationStatus - } - secret.Severity = getSeverity(s.twomsRulesToBeApplied[secret.RuleID].Severity, validationStatus) - secret.CvssScore = getCvssScore(s.rulesBaseRiskScore[secret.RuleID], validationStatus) -} - -func getCategoryScore(category ruledefine.RuleCategory) uint8 { - CategoryScore := map[ruledefine.RuleCategory]uint8{ +var ( + CategoryScoreMap = map[ruledefine.RuleCategory]uint8{ ruledefine.CategoryAuthenticationAndAuthorization: 4, ruledefine.CategoryCryptocurrencyExchange: 4, ruledefine.CategoryFinancialServices: 4, @@ -90,7 +51,47 @@ func getCategoryScore(category ruledefine.RuleCategory) uint8 { ruledefine.CategorySearchService: 1, ruledefine.CategorySocialMedia: 1, } - return CategoryScore[category] + + RuleTypeMaxValue uint8 = 4 +) + +type scorer struct { + rulesBaseRiskScore map[string]float64 + withValidation bool + keywords map[string]struct{} + twomsRulesToBeApplied map[string]ruledefine.Rule + gitleaksRulesToBeApplied map[string]config.Rule // same rules from twomsRulesToBeApplied, converted to gitleaks format +} + +func NewScorer(selectedRules []*ruledefine.Rule, withValidation bool) *scorer { + twomsRulesToBeApplied := make(map[string]ruledefine.Rule) + gitleaksRulesToBeApplied := make(map[string]config.Rule) + rulesBaseRiskScore := make(map[string]float64) + keywords := make(map[string]struct{}) + for _, rule := range selectedRules { + twomsRulesToBeApplied[rule.RuleID] = *rule + gitleaksRulesToBeApplied[rule.RuleID] = *ruledefine.TwomsToGitleaksRule(rule) + rulesBaseRiskScore[rule.RuleID] = GetBaseRiskScore(rule.ScoreParameters.Category, rule.ScoreParameters.RuleType) + for _, keyword := range rule.Keywords { + keywords[strings.ToLower(keyword)] = struct{}{} + } + } + return &scorer{ + rulesBaseRiskScore: rulesBaseRiskScore, + withValidation: withValidation, + keywords: keywords, + twomsRulesToBeApplied: twomsRulesToBeApplied, + gitleaksRulesToBeApplied: gitleaksRulesToBeApplied, + } +} + +func (s *scorer) AssignScoreAndSeverity(secret *secrets.Secret) { + validationStatus := secrets.UnknownResult // default validity + if s.withValidation { + validationStatus = secret.ValidationStatus + } + secret.Severity = getSeverity(s.twomsRulesToBeApplied[secret.RuleID].Severity, validationStatus) + secret.CvssScore = getCvssScore(s.rulesBaseRiskScore[secret.RuleID], validationStatus) } func getValidityScore(baseRiskScore float64, validationStatus secrets.ValidationResult) float64 { @@ -104,7 +105,7 @@ func getValidityScore(baseRiskScore float64, validationStatus secrets.Validation } func GetBaseRiskScore(category ruledefine.RuleCategory, ruleType uint8) float64 { - categoryScore := getCategoryScore(category) + categoryScore := CategoryScoreMap[category] return float64(categoryScore)*0.6 + float64(ruleType)*0.4 } diff --git a/go.mod b/go.mod index 863723de..d168450f 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/google/uuid v1.6.0 github.com/h2non/filetype v1.1.3 github.com/iancoleman/strcase v0.3.0 + github.com/jinzhu/copier v0.4.0 github.com/rs/zerolog v1.33.0 github.com/shirou/gopsutil v3.21.11+incompatible github.com/slack-go/slack v0.12.2 @@ -24,9 +25,9 @@ require ( github.com/stretchr/testify v1.10.0 github.com/zricethezav/gitleaks/v8 v8.28.0 go.uber.org/mock v0.5.2 - golang.org/x/net v0.40.0 - golang.org/x/sync v0.17.0 - golang.org/x/text v0.29.0 + golang.org/x/net v0.47.0 + golang.org/x/sync v0.18.0 + golang.org/x/text v0.31.0 golang.org/x/time v0.5.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -86,7 +87,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/crypto v0.38.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.38.0 // indirect ) diff --git a/go.sum b/go.sum index 9a49d5af..02b85d61 100644 --- a/go.sum +++ b/go.sum @@ -1014,6 +1014,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= @@ -1249,8 +1251,8 @@ golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn5 golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1384,8 +1386,8 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1408,8 +1410,8 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1504,8 +1506,8 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1555,8 +1557,8 @@ golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/scan_test.go b/pkg/scan_test.go index 24137b96..a0c46ac5 100644 --- a/pkg/scan_test.go +++ b/pkg/scan_test.go @@ -15,6 +15,7 @@ import ( "github.com/checkmarx/2ms/v4/lib/secrets" "github.com/checkmarx/2ms/v4/lib/utils" "github.com/checkmarx/2ms/v4/plugins" + "github.com/jinzhu/copier" "github.com/stretchr/testify/assert" ) @@ -40,16 +41,12 @@ var updateExpected = flag.Bool("update-test-data", false, "Update expected test // Rules to be used to test custom rules. Rules will be selected and ignored depending on the test case var customRules = []*ruledefine.Rule{ { + // this rule is missing name and score parameters to test if they are correctly inherited from the default rule RuleID: "01ab7659-d25a-4a1c-9f98-dee9d0cf2e70", - RuleName: "Generic-Api-Key-Custom", Description: "Custom Generic Api Key override, should override the default one (very specific regex just for testing purposes)", Regex: `(?i)\b\w*secret\w*\b\s*:?=\s*["']?([A-Za-z0-9/_+=-]{8,150})["']?`, Severity: "Medium", Tags: []string{"custom", "override"}, - ScoreParameters: ruledefine.ScoreParameters{ - Category: "General", - RuleType: 4, - }, }, { RuleID: "b47a1995-6572-41bb-b01d-d215b43ab089", @@ -86,15 +83,15 @@ var customRules = []*ruledefine.Rule{ }, { RuleID: "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - RuleName: "Github-Pat", + RuleName: "Github-Pat-Custom", // different from default to test if override works Description: "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", Regex: `ghp_[0-9a-zA-Z]{36}`, Severity: "Low", Tags: []string{"custom", "override"}, DisableValidation: true, ScoreParameters: ruledefine.ScoreParameters{ - Category: "Development Platform", - RuleType: 4, + Category: "Package Management", // different from default to test if override works + RuleType: 3, // different from default to test if override works }, }, } @@ -445,7 +442,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run all default + custom rules", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, }, ScanItems: scanItems, @@ -455,7 +452,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run only custom rules", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, SelectRules: []string{"custom"}, }, @@ -466,7 +463,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run only custom override rules", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, SelectRules: []string{"override"}, }, @@ -477,7 +474,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run default + non override rules", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, IgnoreRules: []string{"override"}, }, @@ -488,7 +485,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run only custom rules and ignore overrides", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, SelectRules: []string{"custom"}, IgnoreRules: []string{"override"}, @@ -500,7 +497,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run only default rules by ignoring custom rules", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, IgnoreRules: []string{"custom"}, }, @@ -511,7 +508,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run only custom rules by ignoring custom rules by id", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, SelectRules: []string{"custom"}, IgnoreRules: []string{ @@ -526,12 +523,12 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run only custom rules by ignoring custom rules by name", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, SelectRules: []string{"custom"}, IgnoreRules: []string{ - "Generic-Api-Key-Custom", - "Github-Pat", + "Generic-Api-Key", + "Github-Pat-Custom", }, }, ScanItems: scanItems, @@ -541,7 +538,7 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { { Name: "Run only custom rules by ignoring override result Ids", ScanConfig: resources.ScanConfig{ - CustomRules: customRules, + CustomRules: cloneRules(customRules), WithValidation: true, SelectRules: []string{"custom"}, IgnoreResultIds: []string{ @@ -573,12 +570,11 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { ExpectedReportPath: "", expectErrors: []error{ fmt.Errorf("rule#0: missing ruleID"), - fmt.Errorf("rule#0: missing ruleName"), fmt.Errorf("rule#0: missing regex"), }, }, { - Name: "Regex and severity invalid", + Name: "Regex, severity and score parameters invalid", ScanConfig: resources.ScanConfig{ CustomRules: []*ruledefine.Rule{ { @@ -587,6 +583,10 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { Description: "Match passwords", Regex: "[A-Za-z0-9]{32})", Severity: "mockSeverity", + ScoreParameters: ruledefine.ScoreParameters{ + Category: "mockCategory", + RuleType: 10, + }, }, { RuleID: "b47a1995-6572-41bb-b01d-d215b43ab089", @@ -602,6 +602,9 @@ func TestScanAndScanDynamicWithCustomRules(t *testing.T) { fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid regex"), fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid severity:" + " mockSeverity not one of ([Critical High Medium Low Info])"), + fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid category:" + + " mockCategory not an acceptable category of type RuleCategory"), + fmt.Errorf("rule#0;RuleID-db18ccf1-4fbf-49f6-aec1-939a2e5464c0: invalid rule type: 10 not an acceptable uint8 value, maximum is 4"), }, }, { @@ -1034,3 +1037,18 @@ func compareOrUpdateTestData(t *testing.T, actualReport reporting.IReport, expec assert.EqualValues(t, normalizedExpectedReport, normalizedActualReport) } + +func cloneRules(rulesToClone []*ruledefine.Rule) []*ruledefine.Rule { + clonedRules := make([]*ruledefine.Rule, 0, len(rulesToClone)) + for _, rule := range rulesToClone { + clonedRule := cloneRule(rule) + clonedRules = append(clonedRules, clonedRule) + } + return clonedRules +} + +func cloneRule(r *ruledefine.Rule) *ruledefine.Rule { + var ruleCopy ruledefine.Rule + _ = copier.CopyWithOption(&ruleCopy, r, copier.Option{DeepCopy: true}) + return &ruleCopy +} diff --git a/pkg/testData/expectedReports/customRules/defaultPlusAllCustomRules.json b/pkg/testData/expectedReports/customRules/defaultPlusAllCustomRules.json index 8f448995..bfc4cc6a 100644 --- a/pkg/testData/expectedReports/customRules/defaultPlusAllCustomRules.json +++ b/pkg/testData/expectedReports/customRules/defaultPlusAllCustomRules.json @@ -45,8 +45,8 @@ "id": "63139b45c38f502bbbe15115a7995003d76b2a81", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 0, "endLine": 0, "lineContent": "TextExampleghp_1234567890abcdefghijklmnopqrstuvwxyzTextExampleghp_abcdefghijklmnopqrstuvwxyz1234567890TextExample", @@ -56,7 +56,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "9346cf95d4513bb2861d1ecfc3e82421bd14cbd8": [ @@ -89,8 +89,8 @@ "id": "993b789425c810d4956c5ed8c84f02f90b0531ee", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 1, "endLine": 1, "lineContent": " Text_Example = ghp_9876543210zyxwvutsrqponmlkjihgfedcba", @@ -100,7 +100,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "ab72977b91e213ab4d91eaa2dc89e0b5d54856d5": [ @@ -127,8 +127,8 @@ "id": "c31705d99e835e4ac7bc3f688bd9558309e056ed", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 0, "endLine": 0, "lineContent": "TextExampleghp_1234567890abcdefghijklmnopqrstuvwxyzTextExampleghp_abcdefghijklmnopqrstuvwxyz1234567890TextExample", @@ -138,7 +138,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "efc9a9ee89f1d732c7321067eb701b9656e91f15": [ diff --git a/pkg/testData/expectedReports/customRules/onlyCustomRules.json b/pkg/testData/expectedReports/customRules/onlyCustomRules.json index 33d820f6..ee158f7b 100644 --- a/pkg/testData/expectedReports/customRules/onlyCustomRules.json +++ b/pkg/testData/expectedReports/customRules/onlyCustomRules.json @@ -45,8 +45,8 @@ "id": "63139b45c38f502bbbe15115a7995003d76b2a81", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 0, "endLine": 0, "lineContent": "TextExampleghp_1234567890abcdefghijklmnopqrstuvwxyzTextExampleghp_abcdefghijklmnopqrstuvwxyz1234567890TextExample", @@ -56,7 +56,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "993b789425c810d4956c5ed8c84f02f90b0531ee": [ @@ -64,8 +64,8 @@ "id": "993b789425c810d4956c5ed8c84f02f90b0531ee", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 1, "endLine": 1, "lineContent": " Text_Example = ghp_9876543210zyxwvutsrqponmlkjihgfedcba", @@ -75,7 +75,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "ab72977b91e213ab4d91eaa2dc89e0b5d54856d5": [ @@ -102,8 +102,8 @@ "id": "c31705d99e835e4ac7bc3f688bd9558309e056ed", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 0, "endLine": 0, "lineContent": "TextExampleghp_1234567890abcdefghijklmnopqrstuvwxyzTextExampleghp_abcdefghijklmnopqrstuvwxyz1234567890TextExample", @@ -113,7 +113,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ] } diff --git a/pkg/testData/expectedReports/customRules/onlyOverrideRules.json b/pkg/testData/expectedReports/customRules/onlyOverrideRules.json index f469d9b8..55576912 100644 --- a/pkg/testData/expectedReports/customRules/onlyOverrideRules.json +++ b/pkg/testData/expectedReports/customRules/onlyOverrideRules.json @@ -7,8 +7,8 @@ "id": "63139b45c38f502bbbe15115a7995003d76b2a81", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 0, "endLine": 0, "lineContent": "TextExampleghp_1234567890abcdefghijklmnopqrstuvwxyzTextExampleghp_abcdefghijklmnopqrstuvwxyz1234567890TextExample", @@ -18,7 +18,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "7035eccf9686b12491a9dceae28f3bdf2dbdd9e6": [ @@ -26,7 +26,7 @@ "id": "7035eccf9686b12491a9dceae28f3bdf2dbdd9e6", "source": "testData/secrets/generic-api-keys.txt", "ruleId": "01ab7659-d25a-4a1c-9f98-dee9d0cf2e70", - "ruleName": "Generic-Api-Key-Custom", + "ruleName": "Generic-Api-Key", "ruleCategory": "General", "startLine": 0, "endLine": 0, @@ -45,8 +45,8 @@ "id": "993b789425c810d4956c5ed8c84f02f90b0531ee", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 1, "endLine": 1, "lineContent": " Text_Example = ghp_9876543210zyxwvutsrqponmlkjihgfedcba", @@ -56,7 +56,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "c31705d99e835e4ac7bc3f688bd9558309e056ed": [ @@ -64,8 +64,8 @@ "id": "c31705d99e835e4ac7bc3f688bd9558309e056ed", "source": "testData/secrets/github-pat.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 0, "endLine": 0, "lineContent": "TextExampleghp_1234567890abcdefghijklmnopqrstuvwxyzTextExampleghp_abcdefghijklmnopqrstuvwxyz1234567890TextExample", @@ -75,7 +75,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "f8658be0ab4dfb26464e388a21fa915e14f59da6": [ @@ -83,7 +83,7 @@ "id": "f8658be0ab4dfb26464e388a21fa915e14f59da6", "source": "testData/secrets/generic-api-keys.txt", "ruleId": "01ab7659-d25a-4a1c-9f98-dee9d0cf2e70", - "ruleName": "Generic-Api-Key-Custom", + "ruleName": "Generic-Api-Key", "ruleCategory": "General", "startLine": 1, "endLine": 1, diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 9b97ee06..0bdc4ece 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -309,7 +309,7 @@ func TestSecretsScans(t *testing.T) { "--rule", "custom", "--ignore-rule", - "Generic-Api-Key-Custom,Github-Pat", + "Generic-Api-Key,Github-Pat-Custom", "--ignore-on-exit", "results", }, diff --git a/tests/testData/customRuleConfig/customRules.json b/tests/testData/customRuleConfig/customRules.json index 059e2670..2a6e6663 100644 --- a/tests/testData/customRuleConfig/customRules.json +++ b/tests/testData/customRuleConfig/customRules.json @@ -1,7 +1,7 @@ [ { "ruleId": "01ab7659-d25a-4a1c-9f98-dee9d0cf2e70", - "ruleName": "Generic-Api-Key-Custom", + "ruleName": "Generic-Api-Key", "description": "Custom Generic Api Key override, should override the default one (very specific regex just for testing purposes, shouldn't match with aws-access-token", "regex": "(?i)\\b\\w*secret\\w*\\b\\s*:?=\\s*[\"']?([A-Za-z0-9/_+=-]{8,150})[\"']?", "severity": "Medium", @@ -46,15 +46,15 @@ }, { "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", + "ruleName": "Github-Pat-Custom", "description": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "regex": "ghp_[0-9a-zA-Z]{36}", "severity": "Low", "tags": ["custom", "override"], "disableValidation": true, "scoreParameters": { - "category": "Development Platform", - "ruleType": 4 + "category": "Package Management", + "ruleType": 3 } } ] \ No newline at end of file diff --git a/tests/testData/customRuleConfig/customRules.yaml b/tests/testData/customRuleConfig/customRules.yaml index 9b54c9b7..11ba95ab 100644 --- a/tests/testData/customRuleConfig/customRules.yaml +++ b/tests/testData/customRuleConfig/customRules.yaml @@ -1,5 +1,5 @@ - ruleId: 01ab7659-d25a-4a1c-9f98-dee9d0cf2e70 - ruleName: Generic-Api-Key-Custom + ruleName: Generic-Api-Key description: >- Custom Generic Api Key override, should override the default one (very specific regex just for testing purposes, shouldn't match with aws-access-token regex: "(?i)\\b\\w*secret\\w*\\b\\s*:?=\\s*[\"']?([A-Za-z0-9/_+=-]{8,150})[\"']?" @@ -47,7 +47,7 @@ ruleType: 4 - ruleId: 9f24ac30-9e04-4dc2-bc32-26da201f87e5 - ruleName: Github-Pat + ruleName: Github-Pat-Custom description: >- Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid regex: "ghp_[0-9a-zA-Z]{36}" @@ -56,6 +56,6 @@ - custom - override scoreParameters: - category: Development Platform - ruleType: 4 + category: Package Management + ruleType: 3 disableValidation: true \ No newline at end of file diff --git a/tests/testData/expectedReport/customRules/default_plus_all_custom_rules.json b/tests/testData/expectedReport/customRules/default_plus_all_custom_rules.json index a6017dba..79d815ce 100644 --- a/tests/testData/expectedReport/customRules/default_plus_all_custom_rules.json +++ b/tests/testData/expectedReport/customRules/default_plus_all_custom_rules.json @@ -45,8 +45,8 @@ "id": "4431f6f38c1a36156f0486df95b0436810272bfb", "source": "testData/input/custom_rules_secrets.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 1, "endLine": 1, "lineContent": "ghp_1234567890abcdefghijklmnopqrstuvwxyz123", @@ -56,7 +56,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "b72e2b9140a555724a63bbb8e8e6689b81ebaf28": [ diff --git a/tests/testData/expectedReport/customRules/only_custom_rules.json b/tests/testData/expectedReport/customRules/only_custom_rules.json index d4e0063a..46909ca4 100644 --- a/tests/testData/expectedReport/customRules/only_custom_rules.json +++ b/tests/testData/expectedReport/customRules/only_custom_rules.json @@ -45,8 +45,8 @@ "id": "4431f6f38c1a36156f0486df95b0436810272bfb", "source": "testData/input/custom_rules_secrets.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 1, "endLine": 1, "lineContent": "ghp_1234567890abcdefghijklmnopqrstuvwxyz123", @@ -56,7 +56,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "b72e2b9140a555724a63bbb8e8e6689b81ebaf28": [ diff --git a/tests/testData/expectedReport/customRules/only_override_rules.json b/tests/testData/expectedReport/customRules/only_override_rules.json index e0a906f6..2bdc1631 100644 --- a/tests/testData/expectedReport/customRules/only_override_rules.json +++ b/tests/testData/expectedReport/customRules/only_override_rules.json @@ -7,8 +7,8 @@ "id": "4431f6f38c1a36156f0486df95b0436810272bfb", "source": "testData/input/custom_rules_secrets.txt", "ruleId": "9f24ac30-9e04-4dc2-bc32-26da201f87e5", - "ruleName": "Github-Pat", - "ruleCategory": "Development Platform", + "ruleName": "Github-Pat-Custom", + "ruleCategory": "Package Management", "startLine": 1, "endLine": 1, "lineContent": "ghp_1234567890abcdefghijklmnopqrstuvwxyz123", @@ -18,7 +18,7 @@ "validationStatus": "Unknown", "ruleDescription": "Github-Pat with DisableValidation set to true to test if validation is correctly disabled, resulting in Unknown validity instead of Invalid", "severity": "Low", - "cvssScore": 8.2 + "cvssScore": 7 } ], "58a104ec1b079f2e63d3794d374587048ef6548a": [ @@ -26,7 +26,7 @@ "id": "58a104ec1b079f2e63d3794d374587048ef6548a", "source": "testData/input/custom_rules_secrets.txt", "ruleId": "01ab7659-d25a-4a1c-9f98-dee9d0cf2e70", - "ruleName": "Generic-Api-Key-Custom", + "ruleName": "Generic-Api-Key", "ruleCategory": "General", "startLine": 3, "endLine": 3, @@ -45,7 +45,7 @@ "id": "b69313d6d65da942f1b91a5f12dca17637bff7d9", "source": "testData/input/custom_rules_secrets.txt", "ruleId": "01ab7659-d25a-4a1c-9f98-dee9d0cf2e70", - "ruleName": "Generic-Api-Key-Custom", + "ruleName": "Generic-Api-Key", "ruleCategory": "General", "startLine": 4, "endLine": 4,