mandatoryprogrammer / mandatoryprogrammer/thermoptic
onStart hook unable to solve CloudFlare challenge
- Dominant language
- JavaScript
- Stars
- 1k
- Forks
- 67
- PR merge metrics
- No merged PRs in 30d
Description
When testing the hook with the `docker compose` setup with the [2Captcha Turnstile test](https://2captcha.com/demo/cloudflare-turnstile-challenge), I get challenged by CloudFlare. It seems to detect the captcha, but is unable to convince CloudFlare Turnstile that it's human-like in clicking the checkbox. Actually, I'm not sure if it sees the shadow DOM element.
`docker-compose.yml`:
```
# Hook that runs on proxy start
ON_START_HOOK_FILE_PATH: /work/hooks/onstart.js
# Hook that runs before each HTTP request
```
Output from `docker compose` starting:
```
thermoptic-thermoptic-1 | 2025-10-16T16:52:10.836Z [INFO] Waiting for CDP availability. {"host":"chrome","port":3003,"poll_interval_ms":1000,"startup_timeout_ms":null}
thermoptic-thermoptic-1 | 2025-10-16T16:52:10.856Z [INFO] CDP is available. {"host":"chrome","port":3003}
thermoptic-thermoptic-1 | 2025-10-16T16:52:11.383Z [INFO] thermoptic has begun the initializing process.
thermoptic-thermoptic-1 | 2025-10-16T16:52:11.385Z [INFO] A thermoptic onstart hook has been declared, running hook before starting proxy server... {"hook_file":"/work/hooks/onstart.js"}
thermoptic-thermoptic-1 | 2025-10-16T16:52:11.736Z [INFO] Waiting until Cloudflare JavaScript challenge is complete.
thermoptic-thermoptic-1 | 2025-10-16T16:52:11.744Z [INFO] Passed Cloudflare JavaScript check, continuing startup.
thermoptic-thermoptic-1 | 2025-10-16T16:52:12.550Z [INFO] The thermoptic HTTP Proxy server is now running.
thermoptic-thermoptic-1 | 2025-10-16T16:52:12.551Z [INFO] Health probe endpoint listening. {"port":8085,"path":"/__thermoptic_health"}
thermoptic-thermoptic-1 | 2025-10-16T16:52:12.728Z [INFO] Health probe succeeded. {"duration_ms":175}
```
Output from `curl`:
```
$ curl --proxy http://changeme:changeme@127.0.0.1:1234 --insecure -v -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36' https://2captcha.com/demo/cloudflare-turnstile-challenge
* Trying 127.0.0.1...
* TCP_NODELAY set
* connect to 127.0.0.1 port 1234 failed: Connection refused
* Failed to connect to 127.0.0.1 port 1234: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 127.0.0.1 port 1234: Connection refused
[ec2-user@ip-172-31-21-93 ~]$ curl --proxy http://changeme:changeme@127.0.0.1:1234 --insecure -v -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36' https://2captcha.com/demo/cloudflare-turnstile-challenge
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 1234 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to 2captcha.com:443
* Proxy auth using Basic with user 'changeme'
> CONNECT 2captcha.com:443 HTTP/1.1
> Host: 2captcha.com:443
> Proxy-Authorization: Basic Y2hhbmdlbWU6Y2hhbmdlbWU=
> User-Agent: curl/7.61.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 OK
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=US; O=Thermoptic Proxy; ST=CA; OU=Thermoptic Browser Proxy; CN=2captcha.com
* start date: Oct 14 16:59:02 2025 GMT
* expire date: Jan 17 16:59:02 2028 GMT
* issuer: C=US; O=Thermoptic Proxy; ST=CA; OU=Thermoptic Browser Proxy; CN=Thermoptic Proxy
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET /demo/cloudflare-turnstile-challenge HTTP/1.1
> Host: 2captcha.com
> Accept: */*
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
>
< HTTP/1.1 403 Forbidden
< date: Thu, 16 Oct 2025 16:53:18 GMT
< content-type: text/html; charset=UTF-8
< report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=Ox4rLHy57i9YOeVaZ23WRoqQ3Lp%2BfxZtXGmj7Nzg21w03hI92zZzhfbTrt%2BDMniuwvDAtjbkb9VbmaNodlDjOqJmy8dYO%2By4r0tihv4dzoavOK7whRz9PX4ImjQbEg%3D%3D"}],"group":"cf-nel","max_age":604800}
< priority: u=0,i
< content-encoding: br
< accept-ch: Sec-CH-UA-Bitness, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform, Sec-CH-UA, UA-Bitness, UA-Arch, UA-Full-Version, UA-Mobile, UA-Model, UA-Platform-Version, UA-Platform, UA
< cf-mitigated: challenge
< critical-ch: Sec-CH-UA-Bitness, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform, Sec-CH-UA, UA-Bitness, UA-Arch, UA-Full-Version, UA-Mobile, UA-Model, UA-Platform-Version, UA-Platform, UA
< cross-origin-embedder-policy: require-corp
< cross-origin-opener-policy: same-origin
< cross-origin-resource-policy: same-origin
< origin-agent-cluster: ?1
< permissions-policy: accelerometer=(),autoplay=(),browsing-topics=(),camera=(),clipboard-read=(),clipboard-write=(),geolocation=(),gyroscope=(),hid=(),interest-cohort=(),magnetometer=(),microphone=(),payment=(),publickey-credentials-get=(),screen-wake-lock=(),serial=(),sync-xhr=(),usb=()
< referrer-policy: same-origin
< server-timing: cfL4;desc="?proto=QUIC&rtt=1496&min_rtt=1215&rtt_var=401&sent=10&recv=10&lost=0&retrans=0&sent_bytes=4103&recv_bytes=4607&delivery_rate=7847125&ipace=0&icwnd=12000&ss_exit_cwnd=0&ss_exit_bw=0&ss_exit_reason=0&cwnd=16075&unsent_bytes=0&cid=2fa29438da47d8bb&ts=25&inflight_dur=5&x=118"
< x-content-type-options: nosniff
< x-frame-options: SAMEORIGIN
< cache-control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< expires: Thu, 01 Jan 1970 00:00:01 GMT
< cf-ray: 98f91258ad66d6d5-IAD
< server: cloudflare
< vary: Accept-Encoding
< nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
< alt-svc: h3=":443"; ma=86400
< Content-Length: 9715
< Connection: keep-alive
< Keep-Alive: timeout=5
<
Just a moment...*{box-sizing:border-box;margin:0;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%;color:#313131;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"}body{display:flex;flex-direction:column;height:100vh;min-height:100vh}.main-content{margin:8rem auto;padding-left:1.5rem;max-width:60rem}@media (width <= 720px){.main-content{margin-top:4rem}}.h2{line-height:2.25rem;font-size:1.5rem;font-weight:500}@media (width <= 720px){.h2{line-height:1.5rem;font-size:1.25rem}}#challenge-error-text{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iI0IyMEYwMyIgZD0iTTE2IDNhMTMgMTMgMCAxIDAgMTMgMTNBMTMuMDE1IDEzLjAxNSAwIDAgMCAxNiAzbTAgMjRhMTEgMTEgMCAxIDEgMTEtMTEgMTEuMDEgMTEuMDEgMCAwIDEtMTEgMTEiLz48cGF0aCBmaWxsPSIjQjIwRjAzIiBkPSJNMTcuMDM4IDE4LjYxNUgxNC44N0wxNC41NjMgOS41aDIuNzgzem0tMS4wODQgMS40MjdxLjY2IDAgMS4wNTcuMzg4LjQwNy4zODkuNDA3Ljk5NCAwIC41OTYtLjQwNy45ODQtLjM5Ny4zOS0xLjA1Ny4zODktLjY1IDAtMS4wNTYtLjM4OS0uMzk4LS4zODktLjM5OC0uOTg0IDAtLjU5Ny4zOTgtLjk4NS40MDYtLjM5NyAxLjA1Ni0uMzk3Ii8+PC9zdmc+");background-repeat:no-repeat;background-size:contain;padding-left:34px}@media (prefers-color-scheme: dark){body{background-color:#222;color:#d9d9d9}}
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reviewing the docker-compose.yml hook configuration and the /work/hooks/onstart.js entry point, then reproduce the failure with the 2Captcha Turnstile URL and the provided curl request. Compare the proxy startup logs with the resulting 403 challenge response. Done means the onStart hook handles this test case successfully without the Cloudflare challenge remaining unresolved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker-compose, javascript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100