Selaa lähdekoodia

Feat/nuget config password rule (#1540)

* feat: add new rule nuget-config-password

* docs: correct function names within CONTRIBUTING.md
Roger Meier 1 vuosi sitten
vanhempi
commit
f6e549901c
4 muutettua tiedostoa jossa 76 lisäystä ja 11 poistoa
  1. 11 11
      CONTRIBUTING.md
  2. 1 0
      cmd/generate/config/main.go
  3. 48 0
      cmd/generate/config/rules/nuget.go
  4. 16 0
      config/gitleaks.toml

+ 11 - 11
CONTRIBUTING.md

@@ -39,7 +39,7 @@ If you want to add a new rule to the [default Gitleaks configuration](https://gi
 
 
            // Regex used for detecting secrets. See regex section below for more details
-           Regex: generateSemiGenericRegex([]string{"beamer"}, `b_[a-z0-9=_\-]{44}`, true)
+           Regex: GenerateSemiGenericRegex([]string{"beamer"}, `b_[a-z0-9=_\-]{44}`, true)
 
            // Keywords used for string matching on fragments (think of this as a prefilter)
            Keywords: []string{"beamer"},
@@ -57,32 +57,32 @@ If you want to add a new rule to the [default Gitleaks configuration](https://gi
    This file should be fairly self-explanatory except for a few items;
    regex and secret generation. To help with maintence, _most_ rules should
    be uniform. The functions,
-   [`generateSemiGenericRegex`](https://github.com/zricethezav/gitleaks/blob/master/cmd/generate/config/rules/rule.go#L31) and [`generateUniqueTokenRegex`](https://github.com/zricethezav/gitleaks/blob/master/cmd/generate/config/rules/rule.go#L44) will generate rules
+   [`GenerateSemiGenericRegex`](https://github.com/zricethezav/gitleaks/blob/master/cmd/generate/config/rules/rule.go#L31) and [`GenerateUniqueTokenRegex`](https://github.com/zricethezav/gitleaks/blob/master/cmd/generate/config/rules/rule.go#L44) will generate rules
    that follow defined patterns.
 
    The function signatures look like this:
 
    ```golang
-   func generateSemiGenericRegex(identifiers []string, secretRegex string, isCaseInsensitive bool) *regexp.Regexp
+   func GenerateSemiGenericRegex(identifiers []string, secretRegex string, isCaseInsensitive bool) *regexp.Regexp
 
-   func generateUniqueTokenRegex(secretRegex string, isCaseInsensitive bool) *regexp.Regexp
+   func GenerateUniqueTokenRegex(secretRegex string, isCaseInsensitive bool) *regexp.Regexp
    ```
 
-   `generateSemiGenericRegex` accepts a list of identifiers, a regex, and a boolean indicating whether the pattern should be case-insensitive.
+   `GenerateSemiGenericRegex` accepts a list of identifiers, a regex, and a boolean indicating whether the pattern should be case-insensitive.
    The list of identifiers _should_ match the list of `Keywords` in the rule
-   definition above. Both `identifiers` in the `generateSemiGenericRegex`
+   definition above. Both `identifiers` in the `GenerateSemiGenericRegex`
    function _and_ `Keywords` act as filters for Gitleaks telling the program
    "_at least one of these strings must be present to be considered a leak_"
 
-   `generateUniqueToken` just accepts a regex and a boolean indicating whether the pattern should be case-insensitive. If you are writing a rule for a
+   `GenerateUniqueTokenRegex` just accepts a regex and a boolean indicating whether the pattern should be case-insensitive. If you are writing a rule for a
    token that is unique enough not to require an identifier then you can use
    this function. For example, Pulumi's API Token has the prefix `pul-` which is
-   unique enough to use `generateUniqueToken`. But something like Beamer's API
-   token that has a `b_` prefix is not unique enough to use `generateUniqueToken`,
-   so instead we use `generateSemiGenericRegex` and require a `beamer`
+   unique enough to use `GenerateUniqueTokenRegex`. But something like Beamer's API
+   token that has a `b_` prefix is not unique enough to use `GenerateUniqueTokenRegex`,
+   so instead we use `GenerateSemiGenericRegex` and require a `beamer`
    identifier is part of the rule.
    If a token's prefix has more than `3` characters then you could
-   probably get away with using `generateUniqueToken`.
+   probably get away with using `GenerateUniqueTokenRegex`.
 
    Last thing you'll want to hit before we move on from this file is the
    validation part. You can use `generateSampleSecret` to create a secret for the

+ 1 - 0
cmd/generate/config/main.go

@@ -138,6 +138,7 @@ func main() {
 		rules.NewRelicBrowserAPIKey(),
 		rules.NewRelicInsertKey(),
 		rules.NPM(),
+		rules.NugetConfigPassword(),
 		rules.NytimesAccessToken(),
 		rules.OktaAccessToken(),
 		rules.OpenAI(),

+ 48 - 0
cmd/generate/config/rules/nuget.go

@@ -0,0 +1,48 @@
+package rules
+
+import (
+	"regexp"
+
+	"github.com/zricethezav/gitleaks/v8/cmd/generate/config/utils"
+
+	"github.com/zricethezav/gitleaks/v8/config"
+)
+
+func NugetConfigPassword() *config.Rule {
+	r := config.Rule{
+		Description: "Identified a password within a Nuget config file, potentially compromising package management access.",
+		RuleID:      "nuget-config-password",
+		Regex:       regexp.MustCompile(`(?i)<add key=\"(?:(?:ClearText)?Password)\"\s*value=\"(.{8,})\"\s*/>`),
+		Path:        regexp.MustCompile(`(?i)nuget\.config$`),
+		Keywords:    []string{"<add key="},
+		Entropy:     1,
+		Allowlist: config.Allowlist{
+			Regexes: []*regexp.Regexp{
+				// samples from https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file
+				regexp.MustCompile(`33f!!lloppa`),
+				regexp.MustCompile(`hal\+9ooo_da!sY`),
+				// exclude environment variables
+				regexp.MustCompile(`^\%\S.*\%$`),
+			},
+		},
+	}
+
+	tps := map[string]string{
+		"nuget.config": `<add key="Password" value="CleartextPassword1" />`,
+		"Nuget.config": `<add key="ClearTextPassword" value="CleartextPassword1" />`,
+		"Nuget.Config": `<add key="ClearTextPassword" value="TestSourcePassword" />`,
+		"Nuget.COnfig": `<add key="ClearTextPassword" value="TestSource-Password" />`,
+		"Nuget.CONfig": `<add key="ClearTextPassword" value="TestSource%Password" />`,
+		"Nuget.CONFig": `<add key="ClearTextPassword" value="TestSource%Password%" />`,
+	}
+
+	fps := map[string]string{
+		"some.xml":     `<add key="Password" value="CleartextPassword1" />`,            // wrong filename
+		"nuget.config": `<add key="ClearTextPassword" value="XXXXXXXXXXX" />`,          // low entropy
+		"Nuget.config": `<add key="ClearTextPassword" value="abc" />`,                  // too short
+		"Nuget.Config": `<add key="ClearTextPassword" value="%TestSourcePassword%" />`, // environment variable
+		"NUget.Config": `<add key="ClearTextPassword" value="33f!!lloppa" />`,          // known sample
+		"NUGet.Config": `<add key="ClearTextPassword" value="hal+9ooo_da!sY" />`,       // known sample
+	}
+	return utils.ValidateWithPaths(r, tps, fps)
+}

+ 16 - 0
config/gitleaks.toml

@@ -2442,6 +2442,22 @@ keywords = [
     "npm_",
 ]
 
+[[rules]]
+id = "nuget-config-password"
+description = "Identified a password within a Nuget config file, potentially compromising package management access."
+regex = '''(?i)<add key=\"(?:(?:ClearText)?Password)\"\s*value=\"(.{8,})\"\s*/>'''
+path = '''(?i)nuget\.config$'''
+entropy = 1
+keywords = [
+    "<add key=",
+]
+
+[rules.allowlist]
+
+regexes = [
+    '''33f!!lloppa''','''hal\+9ooo_da!sY''','''^\%\S.*\%$''',
+]
+
 [[rules]]
 id = "nytimes-access-token"
 description = "Detected a Nytimes Access Token, risking unauthorized access to New York Times APIs and content services."