Skip to content

Commit

Permalink
feat: feedback from scs engine (#228)
Browse files Browse the repository at this point in the history
This pull request fixes the regex pattern in the check_new_rules.go file
to include commented rules.

It also updates the authenticated_url.go file to ignore variable in
passwords.
  • Loading branch information
Baruch Odem (Rothkoff) committed Apr 2, 2024
1 parent 0f12983 commit b77600c
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .ci/check_new_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

var (
regexGitleaksRules = regexp.MustCompile(`(?m)^[^/\n\r]\s*rules\.([a-zA-Z0-9_]+)\(`)
regex2msRules = regexp.MustCompile(`(?m)^[^/\n\r]\s*{Rule:\s*\*rules\.([a-zA-Z0-9_]+)\(\),`)
regex2msRules = regexp.MustCompile(`(?m)^[^/\n\r]\s*(?:// )?{Rule:\s*\*rules\.([a-zA-Z0-9_]+)\(\),`)
)

func main() {
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/new-rules.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: New Rules from Gitleaks

on:
workflow_dispatch:
schedule:
- cron: "0 2 * * 6" # At 02:00 on Saturday

Expand Down
4 changes: 4 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ var channels = plugins.Channels{

var report = reporting.Init()
var secretsChan = make(chan *secrets.Secret)
var secretsExtrasChan = make(chan *secrets.Secret)
var validationChan = make(chan *secrets.Secret)

func Execute() (int, error) {
Expand Down Expand Up @@ -140,6 +141,9 @@ func preRun(cmd *cobra.Command, args []string) error {
channels.WaitGroup.Add(1)
go processSecrets()

channels.WaitGroup.Add(1)
go processSecretsExtras()

if validateVar {
channels.WaitGroup.Add(1)
go processValidation(engine)
Expand Down
14 changes: 14 additions & 0 deletions cmd/workers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"sync"

"github.com/checkmarx/2ms/engine"
"github.com/checkmarx/2ms/engine/extra"
)

func processItems(engine *engine.Engine) {
Expand All @@ -24,14 +25,27 @@ func processSecrets() {

for secret := range secretsChan {
report.TotalSecretsFound++
secretsExtrasChan <- secret
if validateVar {
validationChan <- secret
}
report.Results[secret.ID] = append(report.Results[secret.ID], secret)
}
close(secretsExtrasChan)
close(validationChan)
}

func processSecretsExtras() {
defer channels.WaitGroup.Done()

wgExtras := &sync.WaitGroup{}
for secret := range secretsExtrasChan {
wgExtras.Add(1)
go extra.AddExtraToSecret(secret, wgExtras)
}
wgExtras.Wait()
}

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

Expand Down
4 changes: 2 additions & 2 deletions docs/list-of-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ Here is a complete list of all the rules that are currently implemented.
| flutterwave-encryption-key | Uncovered a Flutterwave Encryption Key, which may compromise payment processing and sensitive financial information. | encryption-key | |
| frameio-api-token | Found a Frame.io API token, potentially compromising video collaboration and project management. | api-token | |
| freshbooks-access-token | Discovered a Freshbooks Access Token, posing a risk to accounting software access and sensitive financial data exposure. | access-token | |
| gcp-api-key | Uncovered a GCP API key, which could lead to unauthorized access to Google Cloud services and data breaches. | api-key | |
| gcp-api-key | Uncovered a GCP API key, which could lead to unauthorized access to Google Cloud services and data breaches. | api-key | V |
| generic-api-key | Detected a Generic API Key, potentially exposing access to various services and sensitive operations. | api-key | |
| github-pat | Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure. | access-token | V |
| github-fine-grained-pat | Found a GitHub Fine-Grained Personal Access Token, risking unauthorized repository access and code manipulation. | access-token | V |
| github-oauth | Discovered a GitHub OAuth Access Token, posing a risk of compromised GitHub account integrations and data leaks. | access-token | |
| github-app-token | Identified a GitHub App Token, which may compromise GitHub application integrations and source code security. | access-token | |
| github-refresh-token | Detected a GitHub Refresh Token, which could allow prolonged unauthorized access to GitHub services. | refresh-token | |
| gitlab-pat | Identified a GitLab Personal Access Token, risking unauthorized access to GitLab repositories and codebase exposure. | access-token | |
| gitlab-pat | Identified a GitLab Personal Access Token, risking unauthorized access to GitLab repositories and codebase exposure. | access-token | V |
| gitlab-ptt | Found a GitLab Pipeline Trigger Token, potentially compromising continuous integration workflows and project security. | trigger-token | |
| gitlab-rrt | Discovered a GitLab Runner Registration Token, posing a risk to CI/CD pipeline integrity and unauthorized access. | registration-token | |
| gitter-access-token | Uncovered a Gitter Access Token, which may lead to unauthorized access to chat and communication services. | access-token | |
Expand Down
61 changes: 61 additions & 0 deletions engine/extra/extra.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package extra

import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"sync"

"github.com/checkmarx/2ms/lib/secrets"
)

type addExtraFunc = func(*secrets.Secret) interface{}

var ruleIDToFunction = map[string]addExtraFunc{
"jwt": addExtraJWT,
}

func AddExtraToSecret(secret *secrets.Secret, wg *sync.WaitGroup) {
defer wg.Done()
if addExtra, ok := ruleIDToFunction[secret.RuleID]; ok {
extraData := addExtra(secret)
if extraData != nil && extraData != "" {
UpdateExtraField(secret, "secretDetails", extraData)
}
}
}

var mtxs = &NamedMutex{}

func UpdateExtraField(secret *secrets.Secret, extraName string, extraData interface{}) {
mtxs.Lock(secret.ID)
defer mtxs.Unlock(secret.ID)

if secret.ExtraDetails == nil {
secret.ExtraDetails = make(map[string]interface{})
}
secret.ExtraDetails[extraName] = extraData
}

func addExtraJWT(secret *secrets.Secret) interface{} {
tokenString := secret.Value

parts := strings.Split(tokenString, ".")
if len(parts) != 3 {
return "Invalid JWT token"
}

payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return fmt.Sprintf("Failed to decode JWT payload: %s", err)
}

var claims map[string]interface{}
err = json.Unmarshal(payload, &claims)
if err != nil {
return fmt.Sprintf("Failed to unmarshal JWT payload: %s", string(payload))
}

return claims
}
21 changes: 21 additions & 0 deletions engine/extra/mutex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package extra

import (
"sync"
)

type NamedMutex struct {
mutexes sync.Map
}

func (n *NamedMutex) Lock(key string) {
mu, _ := n.mutexes.LoadOrStore(key, &sync.Mutex{})
mu.(*sync.Mutex).Lock()
}

func (n *NamedMutex) Unlock(key string) {
mu, ok := n.mutexes.Load(key)
if ok {
mu.(*sync.Mutex).Unlock()
}
}
10 changes: 8 additions & 2 deletions engine/rules/authenticated_url.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import (
)

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

tPositives := []string{
Expand All @@ -28,6 +31,9 @@ func AuthenticatedURL() *config.Rule {
`<img src="https://img.shields.io/static/v1?label=Threads&message=Follow&color=101010&link=https://threads.net/@mathrunet" alt="Follow on Threads" />`,
`my [Linkedin](https://www.linkedin.com/in/rodriguesjeffdev/) or email: rodriguesjeff.dev@gmail.com`,
`[![Gmail Badge](https://img.shields.io/badge/-VaibhavHariramani-d54b3d?style=flat-circle&labelColor=d54b3d&logo=gmail&logoColor=white&link=mailto:vaibhav.hariramani01@gmail.com)](mailto:vaibhav.hariramani01@gmail.com)`,
`https://situmops:$(github_token)@github.com/$(Build.Repository.Name).git`,
`'$cmd "unilinks://@@malformed.invalid.url/path?"$cmdSuffix',`,
`Uri.parse('http://login:password@192.168.0.1:8888'),`,
}

return validate(rule, tPositives, fPositives)
Expand Down
21 changes: 21 additions & 0 deletions engine/validation/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package validation

import (
"net/http"
)

func sendValidationRequest(endpoint string, authorization string) (*http.Response, error) {
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", authorization)

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}

return resp, nil
}
81 changes: 81 additions & 0 deletions engine/validation/gcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package validation

import (
"encoding/json"
"io"
"net/http"
"strings"

"github.com/checkmarx/2ms/lib/secrets"
"github.com/rs/zerolog/log"
)

type errorResponse struct {
Error struct {
Message string `json:"message"`
Details []struct {
Type string `json:"@type"`
Metadata struct {
Consumer string `json:"consumer"`
} `json:"metadata,omitempty"`
} `json:"details"`
} `json:"error"`
}

func validateGCP(s *secrets.Secret) (secrets.ValidationResult, string) {
testURL := "https://youtube.googleapis.com/youtube/v3/search?part=snippet&key=" + s.Value

req, err := http.NewRequest("GET", testURL, nil)
if err != nil {
log.Warn().Err(err).Msg("Failed to validate secret")
return secrets.UnknownResult, ""
}

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Warn().Err(err).Msg("Failed to validate secret")
return secrets.UnknownResult, ""
}

result, extra, err := checkGCPErrorResponse(resp)
if err != nil {
log.Warn().Err(err).Msg("Failed to validate secret")
}
return result, extra
}

func checkGCPErrorResponse(resp *http.Response) (secrets.ValidationResult, string, error) {
if resp.StatusCode == http.StatusOK {
return secrets.ValidResult, "", nil
}

if resp.StatusCode != http.StatusForbidden {
return secrets.RevokedResult, "", nil
}

bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return secrets.UnknownResult, "", err
}

// Unmarshal the response body into the ErrorResponse struct
var errorResponse errorResponse
err = json.Unmarshal(bodyBytes, &errorResponse)
if err != nil {
return secrets.UnknownResult, "", err
}

if strings.Contains(errorResponse.Error.Message, "YouTube Data API v3 has not been used in project") {
extra := ""
for _, detail := range errorResponse.Error.Details {
if detail.Type == "type.googleapis.com/google.rpc.ErrorInfo" {
extra = detail.Metadata.Consumer
}
}
return secrets.ValidResult, extra, nil
}

return secrets.UnknownResult, "", nil

}
17 changes: 5 additions & 12 deletions engine/validation/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,18 @@ import (
"github.com/rs/zerolog/log"
)

func validateGithub(s *secrets.Secret) secrets.ValidationResult {
func validateGithub(s *secrets.Secret) (secrets.ValidationResult, string) {
const githubURL = "https://api.github.com/"

req, err := http.NewRequest("GET", githubURL, nil)
if err != nil {
log.Warn().Err(err).Msg("Failed to validate secret")
return secrets.UnknownResult
}
req.Header.Set("Authorization", fmt.Sprintf("token %s", s.Value))
resp, err := sendValidationRequest(githubURL, fmt.Sprintf("token %s", s.Value))

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Warn().Err(err).Msg("Failed to validate secret")
return secrets.UnknownResult
return secrets.UnknownResult, ""
}

if resp.StatusCode == http.StatusOK {
return secrets.ValidResult
return secrets.ValidResult, ""
}
return secrets.RevokedResult
return secrets.RevokedResult, ""
}
43 changes: 43 additions & 0 deletions engine/validation/gitlab.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package validation

import (
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/checkmarx/2ms/lib/secrets"
"github.com/rs/zerolog/log"
)

type userResponse struct {
WebURL string `json:"web_url"`
}

func validateGitlab(s *secrets.Secret) (secrets.ValidationResult, string) {
const gitlabURL = "https://gitlab.com/api/v4/user"

resp, err := sendValidationRequest(gitlabURL, fmt.Sprintf("Bearer %s", s.Value))

if err != nil {
log.Warn().Err(err).Msg("Failed to validate secret")
return secrets.UnknownResult, ""
}

if resp.StatusCode == http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Warn().Err(err).Msg("Failed to read response body for Gitlab validation")
return secrets.ValidResult, ""
}

var user userResponse
if err := json.Unmarshal(bodyBytes, &user); err != nil {
log.Warn().Err(err).Msg("Failed to unmarshal response body for Gitlab validation")
return secrets.ValidResult, ""
}

return secrets.ValidResult, user.WebURL
}
return secrets.RevokedResult, ""
}

0 comments on commit b77600c

Please sign in to comment.