Skip to content

Add bearer token auth support for OpenSearch clusters in Elasticsearch source#5125

Open
anon767 wants to merge 6 commits into
trufflesecurity:mainfrom
anon767:add-opensearch-bearer-token-support
Open

Add bearer token auth support for OpenSearch clusters in Elasticsearch source#5125
anon767 wants to merge 6 commits into
trufflesecurity:mainfrom
anon767:add-opensearch-bearer-token-support

Conversation

@anon767

@anon767 anon767 commented Jul 12, 2026

Copy link
Copy Markdown

Fixes #5124

Summary

OpenSearch clusters share TruffleHog's Elasticsearch API surface but aren't fully compatible with the existing elasticsearch source, so scanning an OpenSearch cluster currently fails. This adds a --bearer-token flag (ELASTICSEARCH_BEARER_TOKEN env var) that works around five incompatibilities (see linked issue), all gated behind bearer-token auth being set so existing Elasticsearch behavior (basic/API-key/service-token) is unchanged.

Test plan

  • go build ./... passes
  • Existing Elasticsearch integration test updated for the new isOpenSearch parameter (passed false, no behavior change)
  • Manually verified against a real OpenSearch 2.17.0 cluster with the JWT auth plugin enabled: minted a JWT signed with the cluster's configured HMAC key, confirmed --bearer-token authenticates, PIT opens via the OpenSearch-specific endpoint, the _doc tiebreaker sort works, and documents are correctly fetched and chunked for scanning. Seeded a document containing a JWT-shaped test secret and confirmed TruffleHog's own JWT detector correctly fired on it end-to-end through this path (source: Elasticsearch, index test-logs) -- this testing is also what surfaced the empty-query-clause bug above.

Notes for reviewers

Nothing about existing Elasticsearch source behavior changes -- the auth/PIT/tiebreaker workarounds only activate when bearer_token is non-empty; the query-clause fix is a strict improvement on both backends.


Note

Low Risk
Small, additive credential wiring with no change to default Elasticsearch auth when bearer token is omitted; secrets still follow the same flag/env patterns as other cluster credentials.

Overview
Adds --bearer-token (and ELASTICSEARCH_BEARER_TOKEN) to the elasticsearch scan command so OpenSearch clusters that use JWT/Bearer auth can authenticate the same way as other Elasticsearch credential flags.

The value is passed through sources.ElasticsearchConfig into the scan engine’s Elasticsearch protobuf connection, and the man page documents the new option. Existing username/password, API key, and service-token flows are unchanged when bearer token is not set.

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

anon767 added 2 commits July 12, 2026 13:01
…h source

OpenSearch clusters aren't fully compatible with the existing Elasticsearch
source despite sharing an API surface, so scanning them previously failed:

- JWT auth: OpenSearch's JWT auth plugin expects a raw
  "Authorization: Bearer <jwt>" header, which none of the existing auth
  options (basic, API key, service token) produce. A new --bearer-token
  flag / ELASTICSEARCH_BEARER_TOKEN env var sets this directly.
- Product header: the vendored go-elasticsearch client rejects any
  response missing the X-Elastic-Product header, which OpenSearch doesn't
  reliably send. A custom http.RoundTripper injects it after the fact when
  bearer-token auth is used (i.e. only for the OpenSearch path).
- Point-in-time API: OpenSearch exposes PIT creation at
  POST /{index}/_search/point_in_time rather than Elasticsearch's
  POST /{index}/_pit, so the vendored esapi.OpenPointInTimeRequest 404s;
  added an OpenSearch-specific PIT-open call for this path.
- Sort tiebreaker: Elasticsearch's PIT tiebreaker field is "_shard_doc";
  OpenSearch doesn't recognize it and rejects the search with a
  query_shard_exception, but accepts the equivalent "_doc".

All four are gated behind bearer-token being set, so existing Elasticsearch
behavior (basic/API-key/service-token auth) is unchanged.
Found via live testing against a real OpenSearch 2.17 cluster with JWT
auth: when no --query-json/--since-timestamp/resumed timestamp is set,
the search body's "query" field was an empty {} object. Elasticsearch
tolerates this as an implicit match-all, but OpenSearch's stricter query
parser rejects it outright with "illegal_argument_exception: query
malformed, empty clause found", silently zeroing out every scan.

match_all is the explicit, portable equivalent and works identically on
both, so this isn't gated behind isOpenSearch.
@anon767
anon767 requested a review from a team July 12, 2026 14:05
@anon767
anon767 requested review from a team as code owners July 12, 2026 14:05
@CLAassistant

CLAassistant commented Jul 12, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@MuneebUllahKhan222

Copy link
Copy Markdown
Contributor

Can you add some tests that demonstrate the current Elasticsearch source doesn't work with OpenSearch, but that they pass with this PR? I think that would clearly show the value of the change and help ensure we don't regress in the future.

TestSource_OpenSearchAPI runs a real opensearchproject/opensearch:2.17.0
container and calls processSearchedDocuments directly against it, showing
both sides of the fix: isOpenSearch=false fails, isOpenSearch=true succeeds.

Verified locally: passes, and pre-existing TestSource_ElasticAPI (real
Elasticsearch 8.9.0) still passes unmodified.
@anon767

anon767 commented Jul 17, 2026

Copy link
Copy Markdown
Author

Done

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.

Support scanning OpenSearch clusters via the Elasticsearch source

3 participants