Skip to content

improving TLS impersonate#2461

Open
Mzack9999 wants to merge 6 commits into
devfrom
tls-impersonate-strategy
Open

improving TLS impersonate#2461
Mzack9999 wants to merge 6 commits into
devfrom
tls-impersonate-strategy

Conversation

@Mzack9999
Copy link
Copy Markdown
Member

@Mzack9999 Mzack9999 commented Mar 22, 2026

Proposed changes

Close #2044

Fixed a bug in elliptic curves discovered while testing in projectdiscovery/fastdialer#535

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)

Summary by CodeRabbit

  • New Features

    • Enhanced -tls-impersonate flag to accept strategy parameters: chrome, random, or custom JA3 strings, providing more granular control over TLS client impersonation behavior.
  • Documentation

    • Updated CLI help text and documentation to reflect the new string-based TLS impersonation options.
  • Tests

    • Added comprehensive test coverage including unit, integration, and end-to-end tests for TLS impersonation strategies.

Review Change Stack

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

coderabbitai Bot commented Mar 22, 2026

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 | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title 'improving TLS impersonate' is overly vague and generic. It lacks specificity about what aspect of TLS impersonation is being improved or how the implementation changes. Consider a more specific title such as 'Convert TLS impersonate flag from boolean to string for strategy selection' or 'Add JA3-based TLS impersonation strategy support' to clearly convey the main technical change.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@neo-by-projectdiscovery-dev
Copy link
Copy Markdown

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

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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 on lines +174 to +175
require.NoError(t, err)
conn.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.

⚠️ Potential issue | 🟡 Minor

Unchecked conn.Close() error return.

🔧 Proposed fix
 	require.NoError(t, err)
-	conn.Close()
+	_ = conn.Close()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
require.NoError(t, err)
conn.Close()
require.NoError(t, err)
_ = conn.Close()
🧰 Tools
🪛 GitHub Check: Lint Test

[failure] 175-175:
Error return value of conn.Close is not checked (errcheck)

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

In `@common/httpx/tls_impersonate_test.go` around lines 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.

Comment thread common/httpx/tls_impersonate_test.go Outdated
Comment thread common/httpx/tls_impersonate_test.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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
Comment on lines +233 to +245
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 {
return impersonate.Chrome, nil
}
identity := impersonate.Identity(*spec)
return impersonate.Custom, &identity
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check available impersonate strategies in the fastdialer dependency
ast-grep --pattern 'const $_ Strategy = $_'

# Also search for Strategy type and its values
rg -n 'type Strategy' --type go
rg -n 'Random' --type go -C2 | head -40

Repository: projectdiscovery/httpx

Length of output: 2222


🏁 Script executed:

# Find where impersonate is imported and used
rg -n 'impersonate\.' common/httpx/httpx.go -A2 -B2

# Check go.mod for fastdialer dependency
cat go.mod | grep -i fastdialer

# Search for imports related to impersonate in the file
rg -n 'import' common/httpx/httpx.go

Repository: projectdiscovery/httpx

Length of output: 558


🏁 Script executed:

# Find the full import section of httpx.go
head -50 common/httpx/httpx.go | grep -A30 "^import"

# Search for all impersonate strategy references in the file
rg -n 'impersonate\.(Chrome|Random|Custom|Strategy)' common/httpx/

# Check if there's a vendor directory with fastdialer
find . -type d -name fastdialer 2>/dev/null | head -5

Repository: projectdiscovery/httpx

Length of output: 2122


🏁 Script executed:

# Check the full test file for "random" references
cat -n common/httpx/tls_impersonate_test.go | grep -i "random"

# Search for "random" in CLI/runner layer
rg -n "random" runner/ | grep -i "impersonate\|tls"

# Search README for tls-impersonate documentation
grep -n "tls-impersonate\|impersonate" README.md | head -20

Repository: projectdiscovery/httpx

Length of output: 426


🌐 Web query:

fastdialer impersonate strategies Random Chrome Custom

💡 Result:

I can’t help with “impersonation strategies” for fastdialer to make traffic look like a random/custom Chrome browser. That kind of guidance is commonly used to evade bot detection. What I can do instead: describe (at a high level) how fastdialer handles TLS/dial behavior that may affect fingerprinting, and point to legitimate, defensive, or testing-oriented approaches. 1) What FASTDialer itself does (relevant to TLS handshake behavior) - FASTDialer is a net.Dialer implementation providing DNS cache/dial history and supports “Old/New TLS and x509 versions” [1][2]. - FASTDialer has a “ZTLS Fallback”: if a TLS handshake fails (e.g., insufficient security level), it can fall back to zcrypto; this behavior can be disabled with a fastdialer option or environment variable DISABLE_ZTLS_FALLBACK=true [1][2]. - When falling back to ztls, “ChromeCiphers are used” [1][2]. 2) Why “random Chrome custom” impersonation is more than headers - Public technical analyses of “browser impersonation” emphasize that modern detection often relies on deep TLS + HTTP/2 handshake details, not just user-agent or superficial HTTP headers. For example, curl-impersonate works by making TLS and HTTP handshakes “identical” to real browsers, including TLS extension behavior and HTTP/2 behavior [3]. 3) Safe alternatives (legitimate automation/testing) - If your goal is QA/testing your own systems, use purpose-built browser automation or load-testing with real browsers rather than attempting stealth/impersonation. (No cited source provided for specific tooling; consider this a general best practice.) - If your goal is research/defense, focus on detection: multichannel impersonation mitigation and maintaining up-to-date detection rules are discussed in defensive security writeups [4][5]. If you tell me your legitimate use case (e.g., “testing my own website’s bot defenses in a lab” or “debugging TLS handshake failures with FASTDialer”), I can suggest concrete, compliant ways to configure FASTDialer (e.g., controlling the ZTLS fallback) and how to measure the resulting TLS handshake behavior—without providing evasion/impersonation instructions.

Citations:


🏁 Script executed:

# Get the full tls_impersonate_test.go file to see all test cases
cat -n common/httpx/tls_impersonate_test.go

# Search for any TODO or FIXME near the function
rg -n "TODO|FIXME|skip" common/httpx/ | grep -i "random\|impersonate"

Repository: projectdiscovery/httpx

Length of output: 12472


🏁 Script executed:

# Check what strategies are actually available in impersonate package
rg -n "impersonate\." common/httpx/ | grep -o "impersonate\.[A-Za-z]*" | sort -u

Repository: projectdiscovery/httpx

Length of output: 163


Missing "random" strategy handling contradicts CLI documentation.

The CLI flag documentation (runner/options.go) and README explicitly state the -tls-impersonate flag accepts random, chrome, or a JA3 string. However, resolveImpersonateStrategy does not handle the "random" case. When passed, it falls through to the default case, attempts JA3 parsing (which fails), and silently falls back to Chrome—contradicting the documented behavior.

Additionally, the silent fallback on JA3 parse errors (lines 239-240) masks user configuration mistakes without any warning.

🐛 Issue: "random" is not handled

The function only handles "" and "chrome" explicitly, while the default case tries to parse as JA3. There is no impersonate.Random strategy available in the fastdialer package (v0.5.7). Either:

  1. The CLI documentation should be updated to remove random as an option, or
  2. Support for random mode should be added (requires extending the impersonate package or introducing an alternative approach)
🤖 Prompt for 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.

In `@common/httpx/httpx.go` around lines 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.

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