Skip to content

feat(detectors): add VaultRootToken detector#5091

Open
deerajcm wants to merge 9 commits into
trufflesecurity:mainfrom
deerajcm:add-vaultroottoken-detector
Open

feat(detectors): add VaultRootToken detector#5091
deerajcm wants to merge 9 commits into
trufflesecurity:mainfrom
deerajcm:add-vaultroottoken-detector

Conversation

@deerajcm

@deerajcm deerajcm commented Jun 30, 2026

Copy link
Copy Markdown

Summary

Add detector for HashiCorp Vault root tokens (TF034).

Changes

  • Add VaultRootToken detector for root tokens generated during vault init
  • Pattern: hvs.[A-Za-z0-9]{20,} or s.[a-zA-Z0-9]{24,}
  • Keywords: "root_token", "root token", "initial root token", "VAULT_ROOT_TOKEN"
  • Format-based verification (API verification skipped for security)
  • Add enum VaultRootToken = 1064 to proto definitions
  • Register detector in defaults.go
  • Add comprehensive unit tests

Detector Details

Type: HashiCorp Vault Root Token
Patterns:

  • New format: hvs.XXXXX (28+ characters)
  • Legacy format: s.XXXXX (26+ characters)

Keywords: Specifically targets root token context to reduce false positives with regular service tokens

Verification: Format validation only - root tokens have unrestricted access and shouldn't be tested against live systems

Use Case: Detect exposed root tokens from vault initialization output

Context-Aware Detection

Unlike regular Vault tokens, this detector uses specific keywords to identify root token context:

  • root_token (variable names)
  • Initial Root Token: (vault init output)
  • VAULT_ROOT_TOKEN (environment variables)

This reduces false positives while maintaining high detection rate for actual root tokens.

Security Note

Root tokens have unrestricted access to all Vault operations. Format-only verification is intentional:

  • ✅ Avoids testing against live Vault instances
  • ✅ Prevents potential security alerts from verification attempts
  • ✅ Still provides high confidence detection via format validation

Test Results

✅ valid_new_format_root_token - PASS
✅ valid_legacy_format_root_token - PASS
✅ token_in_terraform_config - PASS
✅ invalid_too_short - PASS
✅ invalid_wrong_prefix - PASS

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Broad new detector and proto enum changes can affect builds and downstream type mappings; Vault/Docker verification paths touch sensitive credentials and default local endpoints.
> 
> **Overview**
> Adds several new secret scanners and wires them into the default engine, alongside tighter verification for existing ones.
> 
> **New detectors** include **HTTP Basic Auth** (`Authorization: Basic` base64 with decoded username/password in `SecretParts`), **HashiCorp Vault root tokens** (context keywords + format-only “verification” with rotation warnings), **Vault service/batch/recovery tokens** (optional `lookup-self` against configurable/default Vault URLs), **Duffel** API keys (live tokens skipped for verification), and **Docker Swarm unlock** keys (`SWMKEY-1-…`). **Duffel** is enabled in proto (no longer “not implemented”). **Slack** now matches **app-level** `xapp-1-…` tokens.
> 
> **AWS AppSync** verification is refactored to try multiple GraphQL queries, always read the response body, and classify 200/502/400 responses using JSON error snippets (auth vs schema vs success), with new handling for 400/404.
> 
> **Proto / enum churn** replaces `BrainTrustApiKey` at 1053 with several new types (`BasicAuth`, `VaultRootToken` at 1070, etc.); note `dockerswarmunlock` references `DockerSwarmUnlock` while proto lists `DockerSwarmJoinToken` at 1059—a possible mismatch worth checking at build time.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d633f939ddfe2f3af69c45e10fdfa9d8ab319d84. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Deeraj CM and others added 8 commits June 22, 2026 10:26
Add detector for HTTP Basic Authentication tokens (BSCAU002).
Detects Authorization: Basic <base64> patterns and decodes them
to extract username:password credentials.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat(detectors): add BasicAuth detector
Add detector for Docker Swarm Unlock Keys (DKRSWRM002).
Detects SWMKEY-1-<base64> patterns used to unlock locked
Docker Swarm managers. Validates format and returns verified
status for properly formatted keys.

Docker Swarm unlock keys are 52+ character tokens starting
with SWMKEY-1- prefix followed by base64-encoded data.
These keys are used to unlock encrypted swarm state after
manager node restarts.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add detector for Duffel API tokens (DFL001).
Detects duffel_test_ and duffel_live_ token patterns
used to access Duffel's travel booking API.

Duffel is a travel API platform for booking flights, stays,
and ground transportation. Tokens are 40-60 character strings
with test/live prefixes. Only test tokens are verified
automatically for safety.

Pattern: duffel_(test|live)_[a-zA-Z0-9_-]{40,60}
API Version: v2 (v1 deprecated)
Verification: GET /air/airlines endpoint

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add detector for HashiCorp Vault tokens (TF030).
Detects hvs., hvb., hvr., and legacy s. token formats
used to authenticate with Vault servers.

HashiCorp Vault tokens are used to access secrets, manage
policies, and perform administrative operations. Supports
multiple token formats:
- Service tokens (hvs.): Vault 1.10+ format, 90+ chars
- Batch tokens (hvb.): 138+ chars
- Recovery tokens (hvr.): 138+ chars
- Legacy service tokens (s.): 24+ chars

Pattern detection works for all formats. Verification
attempts to connect to common Vault endpoints but will
show unverified status without accessible server.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add detection for Slack App-Level tokens (SVC027).
Extends existing Slack detector to support xapp-1- token format
used for app-level authentication.

Slack App-Level tokens are used to authenticate apps at the
workspace level, allowing Socket Mode connections and other
app-level functionality. Format: xapp-1-[A-Za-z0-9-]{48,}

Changes:
- Add pattern for App-Level tokens (xapp-1-)
- Update keywords to include "xapp-"
- Add test cases for valid/invalid App-Level tokens
- Supports lowercase hex characters in token body

Verification uses existing auth.test endpoint which works
for all Slack token types including App-Level tokens.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhance AWS AppSync detector with more robust verification:

- Add two-query fallback strategy (introspection → __typename)
- Improve error detection in response body for 200 status
- Better handling of schema vs authentication errors
- Detect GraphQLSchemaException and treat as valid key
- Add 400 and 404 status code handling
- Parse JSON response body for UnauthorizedException
- Distinguish between "no schema" (valid key) and "invalid key"

Previously, the detector only tried a single __typename query.
Now it attempts introspection first for better coverage, with
fallback to __typename if introspection fails.

Verification now correctly identifies:
- Valid keys with schemas (200 + data)
- Valid keys without schemas (200/502 + schema errors)
- Invalid keys (401/403 or UnauthorizedException in body)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add detector for HashiCorp Vault root tokens (TF034).
Detects hvs. and s. token formats specifically in root token
context (vault init output, root_token variables).

HashiCorp Vault root tokens are special tokens with unrestricted
access generated during vault initialization. Unlike regular
service tokens, root tokens should be tightly controlled and
rotated regularly.

Format-based verification identifies:
- New format: hvs.XXXXX (28+ chars, typically ~28-100)
- Legacy format: s.XXXXX (26+ chars)

Keywords target root token context: "root_token", "root token",
"initial root token", "VAULT_ROOT_TOKEN" to reduce false positives
with regular service tokens.

Pattern: hvs.[A-Za-z0-9]{20,} or s.[a-zA-Z0-9]{24,}
Verification: Format validation (API verification skipped for
security - root tokens shouldn't be tested against live systems)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@deerajcm deerajcm requested a review from a team June 30, 2026 09:47
@deerajcm deerajcm requested review from a team as code owners June 30, 2026 09:47
@CLAassistant

CLAassistant commented Jun 30, 2026

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.

✅ deerajcm
❌ Deeraj CM


Deeraj CM 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.

if err != nil {
continue
}
defer resp.Body.Close()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deferred response body close inside loop causes resource leak

Medium Severity

defer resp.Body.Close() is called inside the for loop iterating over vaultUrls. In Go, defer runs when the enclosing function returns, not when the loop iteration ends. When the switch hits StatusBadRequest or default and continues, the response body stays open. Multiple iterations accumulate unclosed response bodies until the function finally returns, causing a resource leak of HTTP connections and file descriptors.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fd75bdc. Configure here.

}

var (
defaultClient = detectors.DetectorHttpClientWithNoLocalAddresses

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verification uses localhost-blocking client with localhost-only endpoints

Medium Severity

defaultClient is DetectorHttpClientWithNoLocalAddresses, which blocks connections to loopback/private IPs. The only HTTP endpoint tried is localhost:2375, which will always be rejected. The unix socket is explicitly skipped. So the HTTP verification is dead code, and the fallback format check at line 98 always passes for any regex-matched token, making every detection unconditionally Verified: true.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fd75bdc. Configure here.

&dronahq.Scanner{},
&droneci.Scanner{},
&dropbox.Scanner{},
&duffel.Scanner{},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Duffel still in test exclusion list after implementation

High Severity

The Duffel detector is now registered in defaults.go, but DetectorType_Duffel remains in the excludedFromDefaultList map in defaults_test.go. The TestAllDetectorTypesAreInDefaultList test performs a reverse check that fails when a type appears in both the active detector list and the exclusion list, so this will cause a test failure.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fd75bdc. Configure here.

return a
}
return b
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Redundant custom min function shadows Go builtin

Low Severity

A custom min function is defined that duplicates the built-in min available since Go 1.21. The project uses Go 1.25 (per go.mod), so this package-level function unnecessarily shadows the builtin and adds dead code.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fd75bdc. Configure here.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

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.

There are 5 total unresolved issues (including 4 from previous reviews).

Fix All in Cursor

Reviewed by Cursor Bugbot for commit d633f93. Configure here.

Comment thread proto/detector_type.proto
SpectralOps = 1051;
AWSAppSync = 1052;
BrainTrustApiKey = 1053;
ShippoLiveToken = 1053;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Enum value 1053 reassigned breaking existing BrainTrust detector

High Severity

Enum value 1053 was reassigned from BrainTrustApiKey to ShippoLiveToken, but the existing braintrust detector package still references DetectorType_BrainTrustApiKey and is still registered in defaults.go. This removes a live enum constant that's actively used by production code.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d633f93. Configure here.

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.

2 participants