From ba0a524d6e51744d9d4e306bc57ac5d6ca5173ea Mon Sep 17 00:00:00 2001 From: dylanTruffle <52866392+dylanTruffle@users.noreply.github.com> Date: Thu, 7 May 2026 03:10:21 -0700 Subject: [PATCH] Add Pinecone API key detector (#4917) * Add Pinecone API key detector Adds a new detector for Pinecone vector database API keys (pcsk_* format). Detection: - Regex matches the pcsk_{key_id}_{secret} structure with tight bounds (4+ char key ID, 40+ char secret, word boundaries) to minimize false positives. - Extracts the embedded key_id from the token unconditionally, which maps to the key entry in the Pinecone console and aids revocation. Verification: - Uses GET /indexes (non-state-changing, read-only) with Api-Key header auth. - Validates response body structure (requires "indexes" JSON key), not just status codes, to be resilient against API changes. - 200: Verified. Extracts project_id from index host, total_indexes, and metadata for up to 5 indexes (name, host, cloud, region). - 401: Invalid key (handles both plain text and JSON error bodies). - 403: Valid key with restricted permissions (DataPlane-only roles). Marked as verified with permission=restricted metadata. Uses common.SaneHttpClient() with standard timeouts (5s response, 2s dial, 3s TLS) and io.LimitReader (1MB cap) on response body. No SDK dependencies. Registered as DetectorType Pinecone = 1048. Made-with: Cursor * Align Pinecone detector with SecretParts Migrate Pinecone verification metadata to SecretParts, address the redundant 200-response JSON parsing Bugbot flagged, and add focused regression coverage for malformed verification responses. * Refactor Pinecone detector and add tests --------- Co-authored-by: Dylan Ayrey Co-authored-by: Dustin Decker Co-authored-by: Shahzad Haider Co-authored-by: Shahzad Haider <76992801+shahzadhaider1@users.noreply.github.com> --- pkg/detectors/pinecone/models.go | 20 ++ pkg/detectors/pinecone/pinecone.go | 187 +++++++++++ .../pinecone/pinecone_integration_test.go | 161 ++++++++++ pkg/detectors/pinecone/pinecone_test.go | 304 ++++++++++++++++++ pkg/engine/defaults/defaults.go | 2 + pkg/pb/detector_typepb/detector_type.pb.go | 16 +- proto/detector_type.proto | 1 + 7 files changed, 685 insertions(+), 6 deletions(-) create mode 100644 pkg/detectors/pinecone/models.go create mode 100644 pkg/detectors/pinecone/pinecone.go create mode 100644 pkg/detectors/pinecone/pinecone_integration_test.go create mode 100644 pkg/detectors/pinecone/pinecone_test.go diff --git a/pkg/detectors/pinecone/models.go b/pkg/detectors/pinecone/models.go new file mode 100644 index 000000000..1552d8b93 --- /dev/null +++ b/pkg/detectors/pinecone/models.go @@ -0,0 +1,20 @@ +package pinecone + +type listIndexesResponse struct { + Indexes []indexInfo `json:"indexes"` +} + +type indexInfo struct { + Name string `json:"name"` + Host string `json:"host"` + Spec indexSpec `json:"spec"` +} + +type indexSpec struct { + Serverless *serverlessSpec `json:"serverless,omitempty"` +} + +type serverlessSpec struct { + Cloud string `json:"cloud"` + Region string `json:"region"` +} diff --git a/pkg/detectors/pinecone/pinecone.go b/pkg/detectors/pinecone/pinecone.go new file mode 100644 index 000000000..99e35e6e7 --- /dev/null +++ b/pkg/detectors/pinecone/pinecone.go @@ -0,0 +1,187 @@ +package pinecone + +import ( + "context" + "encoding/json" + "fmt" + "io" + "maps" + "net/http" + "strconv" + "strings" + + regexp "github.com/wasilibs/go-re2" + + "github.com/trufflesecurity/trufflehog/v3/pkg/common" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" + "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detector_typepb" +) + +type Scanner struct { + client *http.Client +} + +// Ensure the Scanner satisfies the interface at compile time. +var _ detectors.Detector = (*Scanner)(nil) + +var ( + defaultClient = common.SaneHttpClient() + + // Pinecone API keys follow the pattern: pcsk_{label}_{secret} + // where label is 5-6 alphanumeric chars and secret is exactly 63 alphanumeric + // chars (total length 74-75). Tight bounds prevent over-reading into adjacent + // text and rule out partial/malformed matches. + // Group 1 = whole token, group 2 = label (surfaced as ExtraData["key_id"]). + keyPat = regexp.MustCompile(`\b(pcsk_([A-Za-z0-9]{5,6})_[A-Za-z0-9]{63})\b`) +) + +// Keywords are used for efficiently pre-filtering chunks. +func (s Scanner) Keywords() []string { + return []string{"pcsk_"} +} + +func (s Scanner) getClient() *http.Client { + if s.client != nil { + return s.client + } + return defaultClient +} + +// FromData will find and optionally verify Pinecone secrets in a given set of bytes. +func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { + dataStr := string(data) + + uniqueMatches := make(map[string]string) + for _, match := range keyPat.FindAllStringSubmatch(dataStr, -1) { + uniqueMatches[match[1]] = match[2] + } + + for token, keyID := range uniqueMatches { + s1 := detectors.Result{ + DetectorType: s.Type(), + Raw: []byte(token), + Redacted: token[:8] + "..." + token[len(token)-4:], + ExtraData: map[string]string{"key_id": keyID}, + SecretParts: map[string]string{"key": token}, + } + + if verify { + isVerified, extraData, verificationErr := s.verifyMatch(ctx, s.getClient(), token) + s1.Verified = isVerified + maps.Copy(s1.ExtraData, extraData) + s1.SetVerificationError(verificationErr, token) + } + + results = append(results, s1) + } + + return results, nil +} + +// verifyMatch calls the Pinecone List Indexes endpoint (GET /indexes) to validate the key. +// Per the Pinecone API reference, this control-plane endpoint returns 200/401/500. +// 403 is not documented but is observed in practice for valid keys that lack +// ControlPlane permissions (e.g. DataPlaneViewer role). +func (s Scanner) verifyMatch(ctx context.Context, client *http.Client, token string) (bool, map[string]string, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.pinecone.io/indexes", http.NoBody) + if err != nil { + return false, nil, err + } + req.Header.Set("Api-Key", token) + req.Header.Set("X-Pinecone-Api-Version", "2025-10") + req.Header.Set("Accept", "application/json") + + resp, err := client.Do(req) + if err != nil { + return false, nil, err + } + defer func() { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + }() + + switch resp.StatusCode { + case http.StatusOK: + bodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) + if err != nil { + return false, nil, err + } + var apiResp listIndexesResponse + if err := json.Unmarshal(bodyBytes, &apiResp); err != nil { + return false, nil, fmt.Errorf("failed to decode 200 response: %w", err) + } + // Guard against a generic 200 that doesn't have the "indexes" key at all: + // JSON unmarshal leaves Indexes as nil in that case, which we treat as a + // malformed response rather than a legitimate empty-indexes result. + if apiResp.Indexes == nil { + return false, nil, fmt.Errorf("unexpected response body structure") + } + return true, buildIndexExtraData(apiResp.Indexes), nil + + case http.StatusUnauthorized: + return false, nil, nil + + case http.StatusForbidden: + // 403 means the key authenticated but lacks permission for this endpoint. + // Official docs do not mention this, but the apikey creation allows setting custom permissions, + // and we have observed 403 responses in practice for valid keys with insufficient permissions. + // We treat this as "verified but with limited permissions" rather than indeterminate + return true, map[string]string{"permission": "restricted"}, nil + + default: + return false, nil, fmt.Errorf("unexpected HTTP response status %d", resp.StatusCode) + } +} + +// extractProjectID pulls the project slug from a Pinecone index host string. +// Hosts follow the pattern: {index-name}-{project_id}.svc.{env}.pinecone.io +func extractProjectID(host string) string { + prefix, _, ok := strings.Cut(host, ".svc.") + if !ok { + return "" + } + lastDash := strings.LastIndex(prefix, "-") + if lastDash == -1 { + return "" + } + return prefix[lastDash+1:] +} + +// buildIndexExtraData derives human-useful context (project id, up to 5 index +// summaries) from the list-indexes response. Returns nil when there are no +// indexes to describe. +func buildIndexExtraData(indexes []indexInfo) map[string]string { + extraData := map[string]string{ + "total_indexes": strconv.Itoa(len(indexes)), + } + + for _, idx := range indexes { + if pid := extractProjectID(idx.Host); pid != "" { + extraData["project_id"] = pid + break + } + } + + for i, idx := range indexes { + if i >= 5 { + break + } + prefix := fmt.Sprintf("index_%d_", i) + extraData[prefix+"name"] = idx.Name + extraData[prefix+"host"] = idx.Host + if idx.Spec.Serverless != nil { + extraData[prefix+"cloud"] = idx.Spec.Serverless.Cloud + extraData[prefix+"region"] = idx.Spec.Serverless.Region + } + } + + return extraData +} + +func (s Scanner) Type() detector_typepb.DetectorType { + return detector_typepb.DetectorType_Pinecone +} + +func (s Scanner) Description() string { + return "Pinecone is a vector database service. API keys can be used to manage indexes and perform vector operations." +} diff --git a/pkg/detectors/pinecone/pinecone_integration_test.go b/pkg/detectors/pinecone/pinecone_integration_test.go new file mode 100644 index 000000000..c31964898 --- /dev/null +++ b/pkg/detectors/pinecone/pinecone_integration_test.go @@ -0,0 +1,161 @@ +//go:build detectors +// +build detectors + +package pinecone + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/trufflesecurity/trufflehog/v3/pkg/common" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" + "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detector_typepb" +) + +func TestPinecone_FromChunk(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors6") + if err != nil { + t.Fatalf("could not get test secrets from GCP: %s", err) + } + secret := testSecrets.MustGetField("PINECONE") + inactiveSecret := testSecrets.MustGetField("PINECONE_INACTIVE") + + type args struct { + ctx context.Context + data []byte + verify bool + } + tests := []struct { + name string + s Scanner + args args + want []detectors.Result + wantErr bool + wantVerificationErr bool + }{ + { + name: "found, verified", + s: Scanner{}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a pinecone secret %s within", secret)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detector_typepb.DetectorType_Pinecone, + Verified: true, + }, + }, + wantErr: false, + wantVerificationErr: false, + }, + { + name: "found, unverified", + s: Scanner{}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a pinecone secret %s within but not valid", inactiveSecret)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detector_typepb.DetectorType_Pinecone, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: false, + }, + { + name: "not found", + s: Scanner{}, + args: args{ + ctx: context.Background(), + data: []byte("You cannot find the secret within"), + verify: true, + }, + want: nil, + wantErr: false, + wantVerificationErr: false, + }, + { + name: "found, would be verified if not for timeout", + s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a pinecone secret %s within", secret)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detector_typepb.DetectorType_Pinecone, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, + }, + { + name: "found, unexpected api surface", + s: Scanner{client: common.ConstantResponseHttpClient(500, "")}, + args: args{ + ctx: context.Background(), + data: []byte(fmt.Sprintf("You can find a pinecone secret %s within", secret)), + verify: true, + }, + want: []detectors.Result{ + { + DetectorType: detector_typepb.DetectorType_Pinecone, + Verified: false, + }, + }, + wantErr: false, + wantVerificationErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) + if (err != nil) != tt.wantErr { + t.Errorf("Pinecone.FromData() error = %v, wantErr %v", err, tt.wantErr) + return + } + for i := range got { + if len(got[i].Raw) == 0 { + t.Fatalf("no raw secret present: \n %+v", got[i]) + } + if (got[i].VerificationError() != nil) != tt.wantVerificationErr { + t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError()) + } + } + ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "Redacted", "ExtraData", "SecretParts", "verificationError", "primarySecret") + if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { + t.Errorf("Pinecone.FromData() %s diff: (-got +want)\n%s", tt.name, diff) + } + }) + } +} + +func BenchmarkFromData(benchmark *testing.B) { + ctx := context.Background() + s := Scanner{} + for name, data := range detectors.MustGetBenchmarkData() { + benchmark.Run(name, func(b *testing.B) { + b.ResetTimer() + for n := 0; n < b.N; n++ { + _, err := s.FromData(ctx, false, data) + if err != nil { + b.Fatal(err) + } + } + }) + } +} diff --git a/pkg/detectors/pinecone/pinecone_test.go b/pkg/detectors/pinecone/pinecone_test.go new file mode 100644 index 000000000..4edc1b7cb --- /dev/null +++ b/pkg/detectors/pinecone/pinecone_test.go @@ -0,0 +1,304 @@ +package pinecone + +import ( + "context" + "io" + "net/http" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" + "github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick" +) + +var ( + // 6-char label, 63-char secret (75 chars total) + validKeyLong = "pcsk_T5Afk6_5qU9s3iLVFmaSaJtMat7gTHaT9fXa7ykiBk7iz4uUMuLGLemkdutTgwJevYhqtn" + // 5-char label, 63-char secret (74 chars total) + validKeyShort = "pcsk_wtQV4_J9qqVGjiMW81LJ9H59iajZMWMedenyLnGVR3vbCWq6V3oaEvcQYwPFQpupFUth1" + invalidKeyNoID = "pcsk__5qU9s3iLVFmaSaJtMat7gTHaT9fXa7ykiBk7iz4uUMuLGLemkdutTgwJevYhqtn" + invalidPrefix = "pineconeT5Afk6_5qU9s3iLVFmaSaJtMat7gTHaT9fXa7ykiBk7iz4uUMuLGLemkdutTgwJevYhqtn" + keyword = "pinecone" +) + +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req) +} + +func TestPinecone_Pattern(t *testing.T) { + d := Scanner{} + ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d}) + + tests := []struct { + name string + input string + want []string + }{ + { + name: "valid pattern - env var assignment", + input: keyword + "_API_KEY=" + validKeyLong, + want: []string{validKeyLong}, + }, + { + name: "valid pattern - short label", + input: "export PINECONE_API_KEY=" + validKeyShort, + want: []string{validKeyShort}, + }, + { + name: "valid pattern - config file", + input: ` + pinecone: + api_key: "` + validKeyLong + `" + environment: us-east-1-aws + `, + want: []string{validKeyLong}, + }, + { + name: "valid pattern - multiple distinct keys", + input: "primary=" + validKeyLong + " secondary=" + validKeyShort, + want: []string{validKeyLong, validKeyShort}, + }, + { + name: "invalid pattern - missing key_id", + input: invalidKeyNoID, + want: []string{}, + }, + { + name: "invalid pattern - wrong prefix", + input: invalidPrefix, + want: []string{}, + }, + { + name: "invalid pattern - secret too short", + input: "pcsk_abcd1_tooShort", + want: []string{}, + }, + { + name: "invalid pattern - label too short (4 chars)", + input: "pcsk_abcd_5qU9s3iLVFmaSaJtMat7gTHaT9fXa7ykiBk7iz4uUMuLGLemkdutTgwJevYhqtn", + want: []string{}, + }, + { + name: "invalid pattern - secret has trailing alphanumeric", + input: validKeyLong + "EXTRA", + want: []string{}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input)) + if len(matchedDetectors) == 0 && len(test.want) > 0 { + t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input) + return + } + + results, err := d.FromData(context.Background(), false, []byte(test.input)) + if err != nil { + t.Errorf("error = %v", err) + return + } + + if len(results) != len(test.want) { + t.Errorf("expected %d results, got %d", len(test.want), len(results)) + for _, r := range results { + t.Logf("got: %s", string(r.Raw)) + } + return + } + + actual := make(map[string]struct{}, len(results)) + for _, r := range results { + actual[string(r.Raw)] = struct{}{} + } + expected := make(map[string]struct{}, len(test.want)) + for _, v := range test.want { + expected[v] = struct{}{} + } + + if diff := cmp.Diff(expected, actual); diff != "" { + t.Errorf("%s diff: (-want +got)\n%s", test.name, diff) + } + }) + } +} + +func TestPinecone_ExtractProjectID(t *testing.T) { + tests := []struct { + name string + host string + want string + }{ + { + name: "serverless host", + host: "my-index-abc1234.svc.us-east1-aws.pinecone.io", + want: "abc1234", + }, + { + name: "hyphenated index name", + host: "my-cool-index-xyz9876.svc.us-west-2.pinecone.io", + want: "xyz9876", + }, + { + name: "missing .svc. segment", + host: "my-index.pinecone.io", + want: "", + }, + { + name: "missing hyphen before .svc.", + host: "abc.svc.us-east1.pinecone.io", + want: "", + }, + { + name: "empty host", + host: "", + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := extractProjectID(tt.host) + if got != tt.want { + t.Errorf("extractProjectID(%q) = %q, want %q", tt.host, got, tt.want) + } + }) + } +} + +func TestPinecone_VerifyMatchSuccess(t *testing.T) { + scanner := Scanner{client: &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + if req.Method != http.MethodGet { + t.Fatalf("expected GET request, got %s", req.Method) + } + if req.URL.String() != "https://api.pinecone.io/indexes" { + t.Fatalf("unexpected URL %s", req.URL.String()) + } + if got := req.Header.Get("Api-Key"); got != validKeyLong { + t.Fatalf("unexpected api key header %q", got) + } + if got := req.Header.Get("X-Pinecone-Api-Version"); got != "2025-10" { + t.Fatalf("unexpected api version header %q", got) + } + + body := `{"indexes":[{"name":"example-index","host":"example-index-abc1234.svc.us-east1-aws.pinecone.io","spec":{"serverless":{"cloud":"aws","region":"us-east-1"}}}]}` + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(body)), + Header: make(http.Header), + Request: req, + }, nil + }), + }} + + verified, extraData, err := scanner.verifyMatch(context.Background(), scanner.client, validKeyLong) + if err != nil { + t.Fatalf("verifyMatch returned error: %v", err) + } + if !verified { + t.Fatal("expected token to verify successfully") + } + if extraData["total_indexes"] != "1" { + t.Fatalf("expected total_indexes=1, got %q", extraData["total_indexes"]) + } + if extraData["project_id"] != "abc1234" { + t.Fatalf("expected project_id=abc1234, got %q", extraData["project_id"]) + } + if extraData["index_0_name"] != "example-index" { + t.Fatalf("expected index_0_name to be populated, got %q", extraData["index_0_name"]) + } +} + +func TestPinecone_VerifyMatchRejectsMissingIndexesKey(t *testing.T) { + scanner := Scanner{client: &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{"projects":[]}`)), + Header: make(http.Header), + Request: req, + }, nil + }), + }} + + verified, extraData, err := scanner.verifyMatch(context.Background(), scanner.client, validKeyLong) + if err == nil { + t.Fatal("expected an error for malformed 200 response") + } + if !strings.Contains(err.Error(), "unexpected response body structure") { + t.Fatalf("unexpected error: %v", err) + } + if verified { + t.Fatal("expected malformed 200 response to remain unverified") + } + if extraData != nil { + t.Fatalf("expected no extra data, got %#v", extraData) + } +} + +func TestPinecone_VerifyMatchRejectsInvalidIndexesPayload(t *testing.T) { + scanner := Scanner{client: &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{"indexes":{}}`)), + Header: make(http.Header), + Request: req, + }, nil + }), + }} + + verified, extraData, err := scanner.verifyMatch(context.Background(), scanner.client, validKeyLong) + if err == nil { + t.Fatal("expected a decode error for invalid indexes payload") + } + if !strings.Contains(err.Error(), "failed to decode 200 response") { + t.Fatalf("unexpected error: %v", err) + } + if verified { + t.Fatal("expected invalid indexes payload to remain unverified") + } + if extraData != nil { + t.Fatalf("expected no extra data, got %#v", extraData) + } +} + +func TestPinecone_FromDataPreservesMetadataOnVerificationError(t *testing.T) { + scanner := Scanner{ + client: &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{"indexes":{}}`)), + Header: make(http.Header), + Request: req, + }, nil + }), + }, + } + + results, err := scanner.FromData(context.Background(), true, []byte(validKeyLong)) + if err != nil { + t.Fatalf("FromData returned error: %v", err) + } + if len(results) != 1 { + t.Fatalf("expected one result, got %d", len(results)) + } + if results[0].Verified { + t.Fatal("expected malformed verification response to keep result unverified") + } + if results[0].VerificationError() == nil { + t.Fatal("expected malformed verification response to set a verification error") + } + if got := results[0].ExtraData["key_id"]; got != "T5Afk6" { + t.Fatalf("expected key_id=T5Afk6, got %q", got) + } + if got := results[0].SecretParts["key"]; got != validKeyLong { + t.Fatalf("expected secret key part to be preserved, got %q", got) + } +} diff --git a/pkg/engine/defaults/defaults.go b/pkg/engine/defaults/defaults.go index 46db6f158..7f5a58383 100644 --- a/pkg/engine/defaults/defaults.go +++ b/pkg/engine/defaults/defaults.go @@ -559,6 +559,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/photoroom" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/phraseaccesstoken" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/pinata" + "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/pinecone" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/pipedream" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/pipedrive" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/pivotaltracker" @@ -1445,6 +1446,7 @@ func buildDetectorList() []detectors.Detector { &photoroom.Scanner{}, &phraseaccesstoken.Scanner{}, &pinata.Scanner{}, + &pinecone.Scanner{}, &pipedream.Scanner{}, &pipedrive.Scanner{}, &pivotaltracker.Scanner{}, diff --git a/pkg/pb/detector_typepb/detector_type.pb.go b/pkg/pb/detector_typepb/detector_type.pb.go index 9876452be..c4a8365ad 100644 --- a/pkg/pb/detector_typepb/detector_type.pb.go +++ b/pkg/pb/detector_typepb/detector_type.pb.go @@ -1102,6 +1102,7 @@ const ( DetectorType_JiraDataCenterPAT DetectorType = 1046 DetectorType_ConfluenceDataCenter DetectorType = 1047 DetectorType_Cloudinary DetectorType = 1048 + DetectorType_Pinecone DetectorType = 1049 ) // Enum value maps for DetectorType. @@ -2152,6 +2153,7 @@ var ( 1046: "JiraDataCenterPAT", 1047: "ConfluenceDataCenter", 1048: "Cloudinary", + 1049: "Pinecone", } DetectorType_value = map[string]int32{ "Alibaba": 0, @@ -3199,6 +3201,7 @@ var ( "JiraDataCenterPAT": 1046, "ConfluenceDataCenter": 1047, "Cloudinary": 1048, + "Pinecone": 1049, } ) @@ -3234,7 +3237,7 @@ var File_detector_type_proto protoreflect.FileDescriptor var file_detector_type_proto_rawDesc = []byte{ 0x0a, 0x13, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x2a, 0x9d, 0x88, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x74, 0x79, 0x70, 0x65, 0x2a, 0xac, 0x88, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x6c, 0x69, 0x62, 0x61, 0x62, 0x61, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x4d, 0x51, 0x50, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x10, @@ -4324,11 +4327,12 @@ var file_detector_type_proto_rawDesc = []byte{ 0x74, 0x65, 0x72, 0x50, 0x41, 0x54, 0x10, 0x96, 0x08, 0x12, 0x19, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x10, 0x97, 0x08, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x69, 0x6e, 0x61, - 0x72, 0x79, 0x10, 0x98, 0x08, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, - 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x79, 0x10, 0x98, 0x08, 0x12, 0x0d, 0x0a, 0x08, 0x50, 0x69, 0x6e, 0x65, 0x63, 0x6f, 0x6e, + 0x65, 0x10, 0x99, 0x08, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x33, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/detector_type.proto b/proto/detector_type.proto index 5b0f0f42f..6e7161cd3 100644 --- a/proto/detector_type.proto +++ b/proto/detector_type.proto @@ -1050,4 +1050,5 @@ enum DetectorType { JiraDataCenterPAT = 1046; ConfluenceDataCenter = 1047; Cloudinary = 1048; + Pinecone = 1049; }