Skip to content

Add chrome and JA3 strategies for -tls-impersonate#2461

Merged
Mzack9999 merged 8 commits into
devfrom
tls-impersonate-strategy
Jul 16, 2026
Merged

Add chrome and JA3 strategies for -tls-impersonate#2461
Mzack9999 merged 8 commits into
devfrom
tls-impersonate-strategy

Conversation

@Mzack9999

@Mzack9999 Mzack9999 commented Mar 22, 2026

Copy link
Copy Markdown
Member

Proposed changes

Closes #2044

Converts -tls-impersonate / -tlsi from a boolean to a string strategy:

Value Behavior
chrome Chrome ClientHello impersonation
<ja3 string> Custom JA3 fingerprint
random Kept for compatibility; maps to chrome (random JA3 removed due to unsupported curve picks — see fastdialer#535)
invalid JA3 Warns and falls back to chrome

Also includes the fastdialer elliptic-curves fix discovered while testing that issue.

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Test plan

  • Unit: resolveImpersonateStrategy (chrome / random / JA3 / invalid)
  • Integration: local TLS server captures ClientHello for chrome vs default vs custom JA3
  • End-to-end: httpx.New dialer with chrome / JA3
  • Functional: -tls-impersonate chrome in cmd/functional-test/testcases.txt
  • Manual smoke: scanme.sh with chrome, custom JA3, invalid JA3 (warning), random

@Mzack9999 Mzack9999 self-assigned this Mar 22, 2026
@auto-assign
auto-assign Bot requested a review from dwisiswant0 March 22, 2026 11:58
@coderabbitai

coderabbitai Bot commented Mar 22, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR converts the -tls-impersonate flag from a boolean toggle to a string-based option, updates CLI registration and documentation to specify supported modes (random, chrome, or a full JA3 string), and refactors the TLS dialer implementation to parse those string values and resolve them into impersonation strategies using JA3 support.

Changes

TLS Impersonation with JA3 Support

Layer / File(s) Summary
CLI Option Contract: String-based Impersonation Mode
runner/options.go, README.md
Options.TlsImpersonate field changes from bool to string; CLI flag registration changes from BoolVarP (default false) to StringVarP (default ""); documentation specifies accepted impersonation values: random, chrome, or a full ja3 string.
TLS Dialer Implementation: JA3 Parsing and Strategy Resolution
common/httpx/httpx.go
HTTP transport DialTLSContext hook delegates to new buildTLSDialer(options) method; resolveImpersonateStrategy(value) interprets the string value case-insensitively (empty or "chrome"impersonate.Chrome; other values parsed via ja3.ParseWithJa3 with fallback to Chrome on error; successful parse → impersonate.Custom with identity). JA3 package imported to support custom impersonation.

Sequence Diagram

sequenceDiagram
  participant Transport as http.Transport
  participant buildTLSDialer as buildTLSDialer()
  participant resolveImpersonate as resolveImpersonateStrategy()
  participant ja3Parser as ja3.ParseWithJa3()
  participant Dialer as h.Dialer.DialTLSWithConfigImpersonate()

  Transport->>buildTLSDialer: DialTLSContext with options
  buildTLSDialer->>buildTLSDialer: check if TlsImpersonate is empty
  alt TlsImpersonate is empty
    buildTLSDialer->>Dialer: use h.Dialer.DialTLS
  else TlsImpersonate is not empty
    buildTLSDialer->>resolveImpersonate: resolve strategy from string value
    resolveImpersonate->>ja3Parser: parse string as JA3 spec
    alt JA3 parse succeeds
      ja3Parser-->>resolveImpersonate: parsed identity
      resolveImpersonate-->>buildTLSDialer: impersonate.Custom with identity
    else JA3 parse fails
      resolveImpersonate-->>buildTLSDialer: impersonate.Chrome (fallback)
    end
    buildTLSDialer->>Dialer: call with resolved strategy and TLS config
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A string where a bool used to dance,
JA3 parsers seize their chance,
Chrome impersonates with graceful ease,
While fallbacks flow like a gentle breeze!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding Chrome and JA3 impersonation strategies to the tls-impersonate option.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tls-impersonate-strategy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@neo-by-projectdiscovery-dev

neo-by-projectdiscovery-dev Bot commented Mar 22, 2026

Copy link
Copy Markdown

Neo - PR Security Review

No security issues found

Highlights

  • Incremental review from commit 4586d67 to 888bdd8
  • PR fixes elliptic curves bug in TLS impersonation feature
  • Updated fastdialer dependency to v0.5.6-0.20260322114839-243754103eca
  • JA3 string parsing continues to use safe error handling with fallback to Chrome strategy
Hardening Notes
  • Automated scanner failed to generate incremental diff; manual review performed on accessible code paths
  • The elliptic curves bug fix is in the external fastdialer dependency, not in httpx codebase itself
  • No new user-facing input handling or attack surface introduced in the incremental commits

Comment @pdneo help for available commands. · Open in Neo

Comment thread common/httpx/httpx.go Dismissed
Comment thread common/httpx/httpx.go Dismissed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
common/httpx/httpx.go (1)

237-241: Invalid JA3 strings silently fall back to random impersonation.

When a user provides an invalid JA3 string, the function silently falls back to impersonate.Random without any warning. This could be confusing if the user expects their custom JA3 to be applied but gets random behavior instead.

Consider logging a warning when JA3 parsing fails, or returning an error during options validation so users are informed their input is invalid.

💡 Suggested improvement
 	default:
 		spec, err := ja3.ParseWithJa3(value)
 		if err != nil {
+			// Consider logging: gologger.Warning().Msgf("invalid JA3 string, falling back to random: %v", err)
 			return impersonate.Random, nil
 		}
 		identity := impersonate.Identity(*spec)
 		return impersonate.Custom, &identity
 	}

Alternatively, validate the JA3 string in runner/options.go:ValidateOptions() to fail early with a clear error message.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/httpx/httpx.go` around lines 237 - 241, The current default branch
silently falls back to impersonate.Random when ja3.ParseWithJa3(value) fails,
which hides invalid user input; update the code that calls ja3.ParseWithJa3 (the
default branch) to either return the parse error up to callers or emit a warning
log before returning impersonate.Random, and also add validation in
ValidateOptions() to call ja3.ParseWithJa3(value) and return a clear validation
error if parsing fails so invalid JA3 strings fail early with a descriptive
message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@common/httpx/tls_impersonate_test.go`:
- Around line 218-219: Replace all unchecked conn.Close() calls in
tls_impersonate_test.go with explicit error discard assignments (e.g. _ =
conn.Close()) so the test obeys lint rules; update the occurrences where
conn.Close() is used after require.NoError(t, err) (currently at the instances
referenced around lines 219, 256, 285, 295, 322, 335, 374, and 399) to use _ =
conn.Close() instead, ensuring every conn.Close() call in functions in this test
file is changed to the `_ = conn.Close()` pattern.
- Around line 196-197: The test currently ignores the error returned by
conn.Close(); update the test to check and fail on any Close error by replacing
the bare conn.Close() with an assertion such as require.NoError(t, conn.Close())
(or handle via t.Cleanup with require.NoError inside the cleanup) so the Close
failure is surfaced; locate the usage of conn.Close() in tls_impersonate_test.go
and apply this change where conn is closed.
- Around line 174-175: The call to conn.Close() in the test ignores its error
return; update the test to assert the close succeeded by checking the error
(e.g., replace the bare conn.Close() with an assertion such as
require.NoError(t, conn.Close()) or call t.Cleanup with a closure that asserts
require.NoError on conn.Close()) so the connection close failure is treated as a
test error; locate the conn variable and its conn.Close() call in
tls_impersonate_test.go to make this change.
- Around line 78-93: The test currently ignores errors from ln.Close(),
conn.Close(), and conn.Read(), causing linter failures; update the cleanup and
goroutines to check and handle these errors: in the t.Cleanup closure replace
ln.Close() with a checked call (if err := ln.Close(); err != nil {
t.Logf("ln.Close error: %v", err) }), in the accept loop check ln.Accept() as
already done but when closing the accepted connection replace defer conn.Close()
with a checked close (defer func(){ if err := conn.Close(); err != nil {
t.Logf("conn.Close error: %v", err) } }()), and replace conn.Read(buf) with a
checked read (if _, err := conn.Read(buf); err != nil && err != io.EOF {
t.Logf("conn.Read error: %v", err) }); keep using the test's t for logging so
the errors are recorded without failing the test.

---

Nitpick comments:
In `@common/httpx/httpx.go`:
- Around line 237-241: The current default branch silently falls back to
impersonate.Random when ja3.ParseWithJa3(value) fails, which hides invalid user
input; update the code that calls ja3.ParseWithJa3 (the default branch) to
either return the parse error up to callers or emit a warning log before
returning impersonate.Random, and also add validation in ValidateOptions() to
call ja3.ParseWithJa3(value) and return a clear validation error if parsing
fails so invalid JA3 strings fail early with a descriptive message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9316c669-2482-41ad-954e-aad7b459edb7

📥 Commits

Reviewing files that changed from the base of the PR and between 9836829 and 788e10e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • README.md
  • cmd/functional-test/testcases.txt
  • common/httpx/httpx.go
  • common/httpx/option.go
  • common/httpx/tls_impersonate_test.go
  • go.mod
  • runner/options.go

Comment thread common/httpx/tls_impersonate_test.go
Comment thread common/httpx/tls_impersonate_test.go Outdated
Comment thread common/httpx/tls_impersonate_test.go Outdated
Comment thread common/httpx/tls_impersonate_test.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
common/httpx/httpx.go (1)

231-243: Silent fallback on invalid JA3 may mask configuration errors.

When ja3.ParseWithJa3 fails, the function silently falls back to Chrome impersonation. If a user provides a malformed JA3 string, they won't know their custom fingerprint isn't being used.

Consider logging a warning when JA3 parsing fails so users can diagnose misconfiguration:

♻️ Proposed improvement with warning log
 func resolveImpersonateStrategy(value string) (impersonate.Strategy, *impersonate.Identity) {
 	switch strings.ToLower(value) {
 	case "", "chrome":
 		return impersonate.Chrome, nil
 	default:
 		spec, err := ja3.ParseWithJa3(value)
 		if err != nil {
+			// Consider adding: gologger.Warning().Msgf("invalid JA3 string '%s', falling back to Chrome: %v", value, err)
 			return impersonate.Chrome, nil
 		}
 		identity := impersonate.Identity(*spec)
 		return impersonate.Custom, &identity
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/httpx/httpx.go` around lines 231 - 243, The resolveImpersonateStrategy
function currently swallows ja3.ParseWithJa3 errors and silently falls back to
impersonate.Chrome; update it to emit a warning when parsing fails (include the
provided value and the parse error) so users know their custom JA3 was invalid.
Modify the error branch around ja3.ParseWithJa3 to log a warning (using the
package logger or standard log) with context (value and err) and then continue
to return impersonate.Chrome, nil; keep the existing return behavior but add the
diagnostic log entry. Ensure the change references resolveImpersonateStrategy
and ja3.ParseWithJa3 so reviewers can locate it easily.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@common/httpx/httpx.go`:
- Around line 231-243: The resolveImpersonateStrategy function currently
swallows ja3.ParseWithJa3 errors and silently falls back to impersonate.Chrome;
update it to emit a warning when parsing fails (include the provided value and
the parse error) so users know their custom JA3 was invalid. Modify the error
branch around ja3.ParseWithJa3 to log a warning (using the package logger or
standard log) with context (value and err) and then continue to return
impersonate.Chrome, nil; keep the existing return behavior but add the
diagnostic log entry. Ensure the change references resolveImpersonateStrategy
and ja3.ParseWithJa3 so reviewers can locate it easily.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 582bf958-0258-4590-a339-2bf00173a156

📥 Commits

Reviewing files that changed from the base of the PR and between 478b774 and 888bdd8.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (2)
  • common/httpx/httpx.go
  • common/httpx/tls_impersonate_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@common/httpx/httpx.go`:
- Around line 233-245: resolveImpersonateStrategy currently doesn't handle the
"random" value and silently falls back to Chrome on JA3 parse errors; update
resolveImpersonateStrategy to explicitly handle the "random" case (check for
strings.ToLower(value) == "random") and return the appropriate strategy (if the
impersonate package exposes a Random constant use impersonate.Random, otherwise
implement a deterministic alternative or surface an error to the caller), and
change the default branch that calls ja3.ParseWithJa3 to not silently ignore
parse errors—log or return a clear warning/error when ja3.ParseWithJa3 fails
(instead of quietly returning impersonate.Chrome) so misconfigured JA3 strings
are visible; refer to resolveImpersonateStrategy and ja3.ParseWithJa3 when
making the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bc91c8e4-bb92-4c7d-b3e3-8de419b12824

📥 Commits

Reviewing files that changed from the base of the PR and between 888bdd8 and 12b77e8.

📒 Files selected for processing (3)
  • README.md
  • common/httpx/httpx.go
  • runner/options.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • runner/options.go

Comment thread common/httpx/httpx.go
@Mzack9999 Mzack9999 changed the title improving TLS impersonate Add chrome and JA3 strategies for -tls-impersonate Jul 16, 2026
@Mzack9999
Mzack9999 merged commit 7d8c90d into dev Jul 16, 2026
13 checks passed
@Mzack9999
Mzack9999 deleted the tls-impersonate-strategy branch July 16, 2026 10:30
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 for Impersonate Browsers TLS Fingerprint

2 participants