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: catch username password inside urls #169

Merged
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
31 changes: 31 additions & 0 deletions secrets/rules/authenticated_url.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package rules

import (
"regexp"

"github.com/zricethezav/gitleaks/v8/config"
)

func AuthenticatedURL() *config.Rule {
regex, _ := regexp.Compile(`:\/\/(.+:.+)?@`)
rule := config.Rule{
Description: "Identify username:password inside URLS",
RuleID: "authenticated-url",
Regex: regex,
Keywords: []string{},
SecretGroup: 1,
}

tPositives := []string{
"mongodb+srv://radar:mytoken@io.dbb.mongodb.net/?retryWrites=true&w=majority",
"--output=https://elastic:bF21iC0bfTVXo3qhpJqTGs78@c22f5bc9787c4c268d3b069ad866bdc2.eu-central-1.aws.cloud.es.io:9243/tfs",
"https://abc:123@google.com",
}

fPositives := []string{
"https://google.com",
"https://google.com?user=abc&password=123",
}

return validate(rule, tPositives, fPositives)
}
37 changes: 37 additions & 0 deletions secrets/rules/rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package rules

import (
"strings"

"github.com/rs/zerolog/log"
"github.com/zricethezav/gitleaks/v8/config"
"github.com/zricethezav/gitleaks/v8/detect"
)

// Copied from https://github.com/gitleaks/gitleaks/blob/463d24618fa42fc7629dc30c9744ebe36c5df1ab/cmd/generate/config/rules/rule.go
func validate(r config.Rule, truePositives []string, falsePositives []string) *config.Rule {
// normalize keywords like in the config package
var keywords []string
for _, k := range r.Keywords {
keywords = append(keywords, strings.ToLower(k))
}
r.Keywords = keywords

rules := make(map[string]config.Rule)
rules[r.RuleID] = r
d := detect.NewDetector(config.Config{
Rules: rules,
Keywords: keywords,
})
for _, tp := range truePositives {
if len(d.DetectString(tp)) != 1 {
log.Fatal().Msgf("Failed to validate. For rule ID [%s], true positive [%s] was not detected by regexp [%s]", r.RuleID, tp, r.Regex)
}
}
for _, fp := range falsePositives {
if len(d.DetectString(fp)) != 0 {
log.Fatal().Msgf("Failed to validate. For rule ID [%s], false positive [%s] was detected by regexp [%s]", r.RuleID, fp, r.Regex)
}
}
return &r
}
hagarfisher marked this conversation as resolved.
Show resolved Hide resolved
28 changes: 28 additions & 0 deletions secrets/rules/rule_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package rules_test

import (
"testing"

"github.com/checkmarx/2ms/secrets/rules"
"github.com/zricethezav/gitleaks/v8/config"
)

func Test2msRules(t *testing.T) {
t.Parallel()

testsRules := []struct {
name string
validate func() *config.Rule
}{
{name: "AuthenticatedURL", validate: rules.AuthenticatedURL},
}
baruchiro marked this conversation as resolved.
Show resolved Hide resolved

for _, tRule := range testsRules {
testRule := tRule // fix for loop variable being captured by func literal
t.Run(testRule.name, func(t *testing.T) {
t.Parallel()

testRule.validate()
})
}
}
2 changes: 2 additions & 0 deletions secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/checkmarx/2ms/plugins"
"github.com/checkmarx/2ms/reporting"
internalRules "github.com/checkmarx/2ms/secrets/rules"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/zricethezav/gitleaks/v8/cmd/generate/config/rules"
Expand Down Expand Up @@ -377,6 +378,7 @@ func loadAllRules() ([]Rule, error) {
allRules = append(allRules, Rule{Rule: *rules.YandexAWSAccessToken(), Tags: []string{TagAccessToken}})
allRules = append(allRules, Rule{Rule: *rules.YandexAccessToken(), Tags: []string{TagAccessToken}})
allRules = append(allRules, Rule{Rule: *rules.ZendeskSecretKey(), Tags: []string{TagSecretKey}})
allRules = append(allRules, Rule{Rule: *internalRules.AuthenticatedURL(), Tags: []string{TagSensitiveUrl}})

return allRules, nil
}
Expand Down
25 changes: 25 additions & 0 deletions secrets/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,31 @@ func TestSecrets(t *testing.T) {
Name string
ShouldFind bool
}{
{
Content: "",
Name: "empty",
ShouldFind: false,
},
{
Content: "mongodb+srv://radar:mytoken@io.dbb.mongodb.net/?retryWrites=true&w=majority",
Name: "Authenticated URL",
ShouldFind: true,
},
{
Content: "--output=https://elastic:bF21iC0bfTVXo3qhpJqTGs78@c22f5bc9787c4c268d3b069ad866bdc2.eu-central-1.aws.cloud.es.io:9243/tfs",
Name: "Authenticated URL",
ShouldFind: true,
},
{
Content: "https://abc:123@google.com",
Name: "Basic Authenticated URL",
ShouldFind: true,
},
{
Content: "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",
Name: "GitHub Personal Access Token",
ShouldFind: true,
},
{
Content: "AKCp8jRRiQSAbghbuZmHKZcaKGEqbAASGH2SAb3rxXJQsSq9dGga8gFXe6aHpcRmzuHxN6oaT",
Name: "JFROG Secret without keyword",
Expand Down