Przeglądaj źródła

feat(rules): Add 1Password secret key detection (#1834)

* Add `1password-secret-key` detection

* Document key details

* Update cmd/generate/config/rules/1password.go

Co-authored-by: Richard Gomez <32133502+rgmz@users.noreply.github.com>

* Generate new `.toml`

* Use `GenerateSampleSecrets` to test more scenarios

* Comment lowercase `fps`

* Decrease entropy

---------

Co-authored-by: Richard Gomez <32133502+rgmz@users.noreply.github.com>
Jordan Bradford 9 miesięcy temu
rodzic
commit
23cb69fba4

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

@@ -26,6 +26,7 @@ func main() {
 	gitleaksConfigPath := os.Args[1]
 
 	configRules := []*config.Rule{
+		rules.OnePasswordSecretKey(),
 		rules.OnePasswordServiceAccountToken(),
 		rules.AdafruitAPIKey(),
 		rules.AdobeClientID(),

+ 40 - 1
cmd/generate/config/rules/1password.go

@@ -33,10 +33,49 @@ export OP_SERVICE_ACCOUNT_TOKEN=ops_eyJzaWduSW5BZGRyZXNzIjoibXkuMXBhc3N3b3JkLmNv
           serviceAccountToken:
             fn::secret: ops_eyJzaWduSW5B..[Redacted]`,
 		`: To start using this service account, run the following command:
-: 
+:
 : export OP_SERVICE_ACCOUNT_TOKEN=ops_eyJzaWduSW5BZGRyZXNzIjoiaHR0cHM6...`,
 		// Low entropy.
 		`ops_eyJxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,
 	}
 	return utils.Validate(r, tps, fps)
 }
+
+// Reference:
+// - https://1passwordstatic.com/files/security/1password-white-paper.pdf
+func OnePasswordSecretKey() *config.Rule {
+	// 1Password secret keys include several hyphens but these are only for readability
+	// and are stripped during 1Password login. This means that the following are technically
+	// the same valid key:
+	//   - A3ASWWYB798JRYLJVD423DC286TVMH43EB
+	//   - A-3-A-S-W-W-Y-B-7-9-8-J-R-Y-L-J-V-D-4-2-3-D-C-2-8-6-T-V-M-H-4-3-E-B
+	// But in practice, when these keys are added to a vault, exported in an emergency kit, or
+	// copied, they have hyphens that follow one of two patterns I can find:
+	//   - A3-ASWWYB-798JRY-LJVD4-23DC2-86TVM-H43EB (every key I've generated has this pattern)
+	//   - A3-ASWWYB-798JRYLJVD4-23DC2-86TVM-H43EB  (the whitepaper includes this example, which could just be a typo)
+	// To avoid a complicated regex that checks for every possible situation it's probably best
+	// to scan for the these two patterns.
+	r := config.Rule{
+		Description: "Uncovered a possible 1Password secret key, potentially compromising access to secrets in vaults.",
+		RuleID:      "1password-secret-key",
+		Regex:       regexp.MustCompile(`\bA3-[A-Z0-9]{6}-(?:(?:[A-Z0-9]{11})|(?:[A-Z0-9]{6}-[A-Z0-9]{5}))-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}\b`),
+		Entropy:     3.8,
+		Keywords:    []string{"A3-"},
+	}
+
+	// validate
+	tps := utils.GenerateSampleSecrets("1password", secrets.NewSecret(`A3-[A-Z0-9]{6}-[A-Z0-9]{11}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}`))
+	tps = append(tps, utils.GenerateSampleSecrets("1password", secrets.NewSecret(`A3-[A-Z0-9]{6}-[A-Z0-9]{6}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}`))...)
+	tps = append(tps,
+		// from whitepaper
+		`A3-ASWWYB-798JRYLJVD4-23DC2-86TVM-H43EB`,
+		`A3-ASWWYB-798JRY-LJVD4-23DC2-86TVM-H43EB`,
+	)
+	fps := []string{
+		// low entropy
+		`A3-XXXXXX-XXXXXXXXXXX-XXXXX-XXXXX-XXXXX`,
+		// lowercase
+		`A3-xXXXXX-XXXXXX-XXXXX-XXXXX-XXXXX-XXXXX`,
+	}
+	return utils.Validate(r, tps, fps)
+}

+ 7 - 0
config/gitleaks.toml

@@ -62,6 +62,13 @@ stopwords = [
     "014df517-39d1-4453-b7b3-9930c563627c",
 ]
 
+[[rules]]
+id = "1password-secret-key"
+description = "Uncovered a possible 1Password secret key, potentially compromising access to secrets in vaults."
+regex = '''\bA3-[A-Z0-9]{6}-(?:(?:[A-Z0-9]{11})|(?:[A-Z0-9]{6}-[A-Z0-9]{5}))-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}\b'''
+entropy = 3.8
+keywords = ["a3-"]
+
 [[rules]]
 id = "1password-service-account-token"
 description = "Uncovered a possible 1Password service account token, potentially compromising access to secrets in vaults."

+ 23 - 0
detect/detect_test.go

@@ -68,6 +68,29 @@ func TestDetect(t *testing.T) {
 	}{
 		// General
 		"valid allow comment (1)": {
+			cfgName: "simple",
+			fragment: Fragment{
+				Raw:      `Key: A3-ASWWYB-798JRY-LJVD4-23DC2-86TVM-H43EB`,
+				FilePath: "tmp.go",
+			},
+			expectedFindings: []report.Finding{
+				{
+					Description: "1Password Secret Key",
+					Secret:      "A3-ASWWYB-798JRY-LJVD4-23DC2-86TVM-H43EB",
+					Match:       "A3-ASWWYB-798JRY-LJVD4-23DC2-86TVM-H43EB",
+					Line:        "Key: A3-ASWWYB-798JRY-LJVD4-23DC2-86TVM-H43EB",
+					File:        "tmp.go",
+					RuleID:      "1password-secret-key",
+					Tags:        []string{"1Password"},
+					StartLine:   0,
+					EndLine:     0,
+					StartColumn: 6,
+					EndColumn:   45,
+					Entropy:     4.3153114,
+				},
+			},
+		},
+		{
 			cfgName: "simple",
 			fragment: Fragment{
 				Raw:      `awsToken := \"AKIALALEMEL33243OKIA\ // gitleaks:allow"`,

+ 6 - 0
testdata/config/simple.toml

@@ -1,6 +1,12 @@
 title = "gitleaks config"
 # https://learnxinyminutes.com/docs/toml/ for toml reference
 
+[[rules]]
+    description = "1Password Secret Key"
+    id = "1password-secret-key"
+    regex = '''A3-[A-Z0-9]{6}-(?:(?:[A-Z0-9]{11})|(?:[A-Z0-9]{6}-[A-Z0-9]{5}))-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}'''
+    tags = ["1Password"]
+
 [[rules]]
     description = "AWS Access Key"
     id = "aws-access-key"