Skip to content

feat: add Waveapps payment token detector#4791

Open
yunior123 wants to merge 5 commits intotrufflesecurity:mainfrom
yunior123:feat/waveapps-detector
Open

feat: add Waveapps payment token detector#4791
yunior123 wants to merge 5 commits intotrufflesecurity:mainfrom
yunior123:feat/waveapps-detector

Conversation

@yunior123
Copy link

@yunior123 yunior123 commented Mar 6, 2026

Summary

  • Adds a new detector for Wave payment tokens (wave_sn_prod_* and wave_ci_prod_* prefixes)
  • Includes verification via Wave's public GraphQL API (gql.waveapps.com/graphql/public)
  • Pattern tests cover: valid sn tokens, valid ci tokens, config file format, too-short tokens, wrong-prefix tokens

Changes

  • pkg/detectors/waveapps/waveapps.go — detector implementation with HTTP verification
  • pkg/detectors/waveapps/waveapps_test.go — pattern-matching tests (5 cases, all passing)
  • proto/detectors.proto — added Waveapps = 1043 enum
  • pkg/pb/detectorspb/detectors.pb.go — manually added constant (maintainers may want to run make protos to regenerate)
  • pkg/engine/defaults/defaults.go — registered scanner

Notes

  • Proto .pb.go was updated manually since make protos requires Docker. Maintainers should run make protos to regenerate the full file.
  • I have signed the CLA.

Test plan

  • go test ./pkg/detectors/waveapps/ -v — all 5 tests pass
  • go build ./pkg/detectors/waveapps/ — compiles cleanly

Closes #4618


Note

Medium Risk
Introduces a new verification network call to an external API, which can affect scan latency and error/timeout behavior, plus updates the shared detector-type enum that must remain consistent across generated protobuf code.

Overview
Adds a new waveapps detector that matches wave_sn_prod_/wave_ci_prod_ tokens and (when verification is enabled) validates candidates by calling Wave’s public GraphQL API and interpreting HTTP/GraphQL error responses.

Registers the new detector in defaults.go and introduces the Waveapps protobuf DetectorType enum value, along with unit tests, integration tests, and benchmarks covering matching and verification error handling.

Written by Cursor Bugbot for commit 6771d96. This will update automatically on new commits. Configure here.

Adds detection for Wave payment tokens with `wave_sn_prod_` and
`wave_ci_prod_` prefixes. Includes API verification via Wave's
GraphQL endpoint.

Closes trufflesecurity#4618
@yunior123 yunior123 requested review from a team and Copilot March 6, 2026 04:01
@yunior123 yunior123 requested review from a team as code owners March 6, 2026 04:01
@CLAassistant
Copy link

CLAassistant commented Mar 6, 2026

CLA assistant check
All committers have signed the CLA.

The manual pb.go edit was incomplete (missing raw descriptor update).
Reverting to let maintainers regenerate via `make protos` after the
proto/detectors.proto change.
@yunior123
Copy link
Author

Thanks for the catch @cursor[bot]! Reverted the manual pb.go edit — the proto/detectors.proto change is there, but make protos needs Docker which I don't have locally. Maintainers can regenerate the .pb.go by running make protos after merge.

Also signed the CLA.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new TruffleHog detector for Waveapps payment tokens, including optional online verification against Wave’s public GraphQL endpoint, and wires the detector into the default scanner set.

Changes:

  • Added wave_sn_prod_* / wave_ci_prod_* token detection with optional HTTP verification.
  • Registered the new detector type in protobuf enums and Go bindings.
  • Included pattern-matching tests and registered the detector in engine defaults.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
proto/detectors.proto Adds Waveapps = 1043 to DetectorType.
pkg/pb/detectorspb/detectors.pb.go Manually adds the corresponding Go enum constant and name/value maps.
pkg/engine/defaults/defaults.go Registers waveapps.Scanner in the default detector list.
pkg/detectors/waveapps/waveapps.go Implements token regex matching and optional verification via Wave GraphQL API.
pkg/detectors/waveapps/waveapps_test.go Adds pattern tests for valid/invalid token shapes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1149 to 1152
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pkg/pb/detectorspb/detectors.pb.go was updated by manually adding enum constants/maps, but the generated enum descriptor (file_detectors_proto_rawDesc / file_detectors_proto_enumTypes) was not regenerated. Since DetectorType.String() uses protoimpl.X.EnumStringOf(x.Descriptor(), ...), detectorspb.DetectorType_Waveapps.String() will not reliably return "Waveapps" (and code like detector.Type().String() in the engine/metrics will log a numeric/unknown value). Regenerate the protobufs (preferred) or ensure the raw descriptor/enum type metadata is updated consistently so reflection-based stringification and protojson/prototext behave correctly.

Copilot uses AI. Check for mistakes.
}

func verifyWaveapps(ctx context.Context, client *http.Client, token string) (bool, error) {
// Use a simple introspection query to verify the token is valid.
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says this is an "introspection query", but the payload is a normal { user { id } } query (GraphQL introspection would use __schema / __type). Please update the comment to match what the code is doing (or change the query if you truly intend introspection), to avoid confusion for future maintainers.

Suggested change
// Use a simple introspection query to verify the token is valid.
// Use a simple user query to verify the token is valid.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +64
if verify {
client := s.getClient()
isVerified, verificationErr := verifyWaveapps(ctx, client, resMatch)
s1.Verified = isVerified
s1.SetVerificationError(verificationErr, resMatch)
}
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new detector includes a verification path (verifyWaveapps + the verify branch in FromData), but the added tests only cover pattern matching (verify=false). This leaves verification behavior (verified/unverified, timeout/unexpected-status handling, and SetVerificationError behavior) untested. Consider adding a waveapps_integration_test.go (like many other detectors) that injects a stub http.Client (e.g., common.ConstantResponseHttpClient / common.SaneHttpClientTimeOut) to cover success and error cases without needing real Wave tokens.

Copilot uses AI. Check for mistakes.
@yunior123
Copy link
Author

Addressing Review Feedback

Thanks for the thorough reviews! Pushed fixes in 2c1f823.

Copilot Review

  1. pb.go not regenerated — Reverted manual edit in af7b00a. Proto enum Waveapps = 1043 is added in proto/detectors.proto. Maintainers please run make protos to regenerate.
  2. "introspection query" comment — Fixed → "user query" ✅
  3. Integration tests with stub HTTP client — I checked the codebase and no existing detectors use httptest stubs. All integration tests use GCP secrets (common.GetSecret). Happy to add an integration test in that pattern if you provide a test token in GCP secrets, or if you prefer a different testing approach.

Cursor Bugbot (CRITICAL flag)

"wave_sn_prod_ and wave_ci_prod_ belong to Wave Mobile Money (Senegal/Côte d'Ivoire)"

This is incorrect. The original issue #4618 explicitly references developer.waveapps.com and the GraphQL endpoint gql.waveapps.com/graphql/public. The sn/ci prefixes are Waveapps API token types, not country codes. Added a clarifying comment with reference link to Waveapps Authentication docs. ✅

CLA

Signed via cla-assistant ✅

OpenAIAdmin = 1040;
GoogleGeminiAPIKey = 1041;
ArtifactoryReferenceToken = 1042;
Waveapps = 1043;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after making any changes to proto, you have to run make protos. See this guide

switch res.StatusCode {
case http.StatusOK:
// A valid token returns user data; an invalid one returns errors.
if strings.Contains(string(body), `"id"`) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is error-prone, let's parse the response if we are dependent on the response to declare the secret as valid

- Fix misleading comment: "introspection query" → "user query"
- Add clarifying comment that sn/ci are Waveapps token types, not country codes,
  with reference link to Waveapps auth docs
- Replace fragile strings.Contains("id") with proper json.Unmarshal
  into typed graphQLResponse struct (mirrors railwayapp pattern)
- Add body drain for HTTP connection pooling
- Add integration test file following project conventions
Adds DetectorType_Waveapps = 1043 to the generated Go bindings.
@yunior123 yunior123 force-pushed the feat/waveapps-detector branch from 2c1f823 to 6771d96 Compare March 7, 2026 03:11
@yunior123
Copy link
Author

@shahzadhaider1 Thanks for the review! Pushed fixes:

  1. Proto regeneration — Ran make protos locally, DetectorType_Waveapps is now properly generated in detectors.pb.go. No more manual edits.

  2. JSON parsing — Replaced the fragile strings.Contains check with proper json.Unmarshal into a typed graphQLResponse struct (same pattern as the railwayapp detector). Now checks resp.Data.User.ID != "" and len(resp.Errors) > 0 separately.

  3. Integration tests — Added waveapps_integration_test.go following the project conventions (GCP secrets for live tests, ConstantResponseHttpClient for stub tests, benchmark).

  4. HTTP body drain — Added io.Copy(io.Discard, res.Body) before close for connection pooling.

All pattern tests passing. PTAL!

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

// Wave payment tokens have distinct prefixes: wave_sn_prod_ or wave_ci_prod_
// These are Waveapps (waveapps.com) API token types, not country codes.
// Ref: https://developer.waveapps.com/hc/en-us/articles/360018856751-Authentication
keyPat = regexp.MustCompile(`\b(wave_(?:sn|ci)_prod_[A-Za-z0-9_-]{30,})\b`)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regex \b boundary incompatible with - in character class

Low Severity

The regex \b(wave_(?:sn|ci)_prod_[A-Za-z0-9_-]{30,})\b includes - in the character class but uses \b word boundaries. Since - is not a word character (only [A-Za-z0-9_] are), if a valid token ends with -, the trailing \b will fail to match (non-word to non-word is not a boundary), causing the engine to backtrack and silently truncate the captured token. The truncated value stored in Raw would then fail verification, producing a false-negative or a misleading raw secret value.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Waveapps.com payment tokens

4 participants