Skip to content

Fix Shopify detector: add missing shpca_ and shptka_ token patterns#4946

Open
asivaprasad09 wants to merge 5 commits intotrufflesecurity:mainfrom
asivaprasad09:fix-shopify-detector-missing-token-patterns
Open

Fix Shopify detector: add missing shpca_ and shptka_ token patterns#4946
asivaprasad09 wants to merge 5 commits intotrufflesecurity:mainfrom
asivaprasad09:fix-shopify-detector-missing-token-patterns

Conversation

@asivaprasad09
Copy link
Copy Markdown

@asivaprasad09 asivaprasad09 commented May 4, 2026

Summary

Problem

The existing Shopify detector only covered two of the four Shopify access token formats:

  • shppa_ — Partner app tokens ✅
  • shpat_ — Private app tokens ✅
  • shpca_ — Custom app tokens ❌ missing
  • shptka_ — Token app tokens ❌ missing

Any shpca_ or shptka_ token in scanned content was silently missed.

Changes

  • Regex(shppa_|shpat_)shp(?:ca|at|tka|pa)_ covering all 4 formats
  • Keywords — added shpca_ and shptka_ to the pre-filter list
  • Hex charset — tightened from [0-9A-Fa-f] to [a-f0-9] (Shopify tokens use lowercase hex only)

Before vs After

Token type Before After
shppa_ Partner app ✅ Detected ✅ Detected
shpat_ Private app ✅ Detected ✅ Detected
shpca_ Custom app ❌ Missed ✅ Detected
shptka_ Token app ❌ Missed ✅ Detected

Test plan

  • All 4 token types return 1 result each when scanned with a co-located *.myshopify.com domain
  • go build ./pkg/detectors/shopify/... passes cleanly

Note

Medium Risk
Adds a new detector with live HTTP verification and adjusts multiple existing detectors’ matching/verification behavior, which could affect scan results and outbound request volume. Regex tightening and new “emit unverified when no domain found” paths may change false-positive/false-negative rates.

Overview
Improves secret detection coverage and robustness across several detectors.

Expands the Shopify detector to recognize additional token prefixes (now covering shpca_, shpat_, shptka_, shppa_), updates keyword prefiltering accordingly, and refactors verification into a helper using a shared “sane” HTTP client with safer response body draining.

Updates Grafana service account detection to a stricter token format, dedupes matches, uses the shared HTTP client, and emits findings as unverified when no *.grafana.net domain is present instead of dropping them.

Adjusts Artifactory reference token matching to a more specific base64 prefix/length and similarly emits unverified results when no *.jfrog.io host is found.

Adds a new TogetherAI detector (regex + optional verification against GET /v1/models), wires it into default detector lists, and registers the new DetectorType_TogetherAI in the protobuf enum (with tests/benchmarks for the new detector).

Reviewed by Cursor Bugbot for commit ba42427. Bugbot is set up for automated code reviews on this repo. Configure here.

Akshara Sivaprasad and others added 4 commits May 4, 2026 12:12
Adds a detector for Together AI API keys (tgp_v1_ format).
Verifies keys via GET /v1/models endpoint.
…us codes, deduplicate

- Previously tokens with no co-located domain were silently dropped.
  Now an unverified result is always emitted so the token is never lost.
- Switch to common.SaneHttpClient() consistent with all other detectors.
- Properly drain response body with io.Copy(io.Discard) to avoid
  connection leaks.
- Treat 403 as determinately invalid (was falling into error bucket).
- Deduplicate keys and domains before processing to avoid duplicate results.
- Extract verification into verifyGrafanaServiceAccount() helper.
…d when no domain

- Tighten regex from cmVmdGtu[a-zA-Z0-9]{54,60} to cmVmdGtuOj[a-zA-Z0-9]{54}
  Reference tokens always decode to reftkn:01:<expiry>:<random> making
  the full prefix cmVmdGtuOj (base64 of 'reftkn:') and total length
  always exactly 64 chars. Variable length range was incorrect.
- Update keyword from cmVmdGtu to cmVmdGtuOj for more specific
  pre-filtering and fewer false positives.
- Emit unverified result when no jfrog.io domain is found in the chunk.
  Previously tokens without a co-located domain were silently dropped.
The detector only covered shppa_ (partner app) and shpat_ (private app).
Two token types were silently missed:
- shpca_ (custom app tokens)
- shptka_ (token app tokens)

Updated regex and keywords to cover all 4 Shopify token formats.
@asivaprasad09 asivaprasad09 requested a review from a team May 4, 2026 08:30
@asivaprasad09 asivaprasad09 requested review from a team as code owners May 4, 2026 08:30
- Token alone with no myshopify.com domain was silently dropped.
  Now emits unverified result so token is always flagged.
- Fix status code handling: explicit 401/403 = determinately invalid,
  other non-200 = indeterminate error (was silently ignored before).
- Drain response body with io.Copy(io.Discard) to avoid connection leaks.
- Extract verifyShopify() helper consistent with detector conventions.
- Switch to common.SaneHttpClient(), deduplicate keys and domains.
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ asivaprasad09
❌ Akshara Sivaprasad


Akshara Sivaprasad seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown

@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 2 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit ba42427. Configure here.


var (
client = detectors.DetectorHttpClientWithNoLocalAddresses
defaultClient = common.SaneHttpClient()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SSRF protection removed from user-controlled domain requests

Medium Severity

The defaultClient was changed from detectors.DetectorHttpClientWithNoLocalAddresses to common.SaneHttpClient(). Both the Shopify and Grafana detectors make verification HTTP requests to domains extracted from scanned data (user-controlled input). The previous client included WithNoLocalIP() protection that blocks connections to loopback, link-local, and private IP addresses, preventing SSRF. common.SaneHttpClient() lacks this safeguard. While the domain regexes constrain targets to *.myshopify.com and *.grafana.net, this still removes an intentional defense-in-depth layer against DNS rebinding or similar attacks.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ba42427. Configure here.

if err != nil {
return false, err
}
defer res.Body.Close()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Response body not drained before close

Low Severity

In verifyTogetherAI, the response body is closed with a bare defer res.Body.Close() without first draining it via io.Copy(io.Discard, res.Body). The other verification functions added in this same PR (verifyShopify, verifyGrafanaServiceAccount) correctly drain before closing. Not draining the body prevents the underlying TCP connection from being reused by the connection pool, which can lead to connection churn under load.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ba42427. Configure here.

Copy link
Copy Markdown
Contributor

@amanfcp amanfcp left a comment

Choose a reason for hiding this comment

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

I'd suggest you to create separate PRs for each detector enhancements.

Regarding the change mentioned in description, how were you able to test sptka and shpca? When I last worked on it, it said that new credentials of these types cannot be created anymore, however the existing ones work. It's been deferred since we cannot merge a change we cannot test.

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.

3 participants