Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions cycletls/decompress_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package cycletls

import (
"bytes"
"compress/gzip"
"testing"

"github.com/andybalholm/brotli"
)

func TestUnBrotliDataValid(t *testing.T) {
var buf bytes.Buffer
w := brotli.NewWriter(&buf)
if _, err := w.Write([]byte("hello cycle")); err != nil {
t.Fatal(err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}

got, err := unBrotliData(buf.Bytes())
if err != nil {
t.Fatalf("valid brotli: %v", err)
}
if string(got) != "hello cycle" {
t.Fatalf("got %q", got)
}

decoded := DecompressBody(buf.Bytes(), []string{"br"}, nil)
if string(decoded) != "hello cycle" {
t.Fatalf("DecompressBody got %q", decoded)
}
}

func TestUnBrotliDataInvalidDoesNotPanic(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("invalid brotli panicked: %v", r)
}
}()

inputs := [][]byte{
{},
{0x00},
[]byte("not brotli"),
{0x06, 0x00},
bytes.Repeat([]byte{0xff}, 64),
}
for _, in := range inputs {
got, err := unBrotliData(in)
out := DecompressBody(in, []string{"br"}, nil)
if err != nil {
if !bytes.Equal(out, in) {
t.Errorf("DecompressBody should return original body on error, got %q for %v (unz=%q err=%v)", out, in, got, err)
}
continue
}
_ = got
}
}

func TestReadLimitedCapsSize(t *testing.T) {
r := bytes.NewReader(make([]byte, maxDecompressedBody+1))
if _, err := readLimited(r); err == nil {
t.Fatal("expected size limit error")
}
}

func TestGUnzipDataValid(t *testing.T) {
var buf bytes.Buffer
w := gzip.NewWriter(&buf)
if _, err := w.Write([]byte("hello gzip")); err != nil {
t.Fatal(err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}

got, err := gUnzipData(buf.Bytes())
if err != nil {
t.Fatalf("valid gzip: %v", err)
}
if string(got) != "hello gzip" {
t.Fatalf("got %q", got)
}
}
2 changes: 1 addition & 1 deletion cycletls/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ toolchain go1.24.4

require (
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131
github.com/andybalholm/brotli v1.2.0
github.com/andybalholm/brotli v1.2.7
github.com/gorilla/websocket v1.5.1
github.com/quic-go/quic-go v0.53.0
github.com/refraction-networking/uquic v0.0.6
Expand Down
6 changes: 2 additions & 4 deletions cycletls/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1 h1:/lqhaiz7xdPr6kuaW1tQ/8DdpWdxkdyd9W/6EHz4oRw=
github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1/go.mod h1:Hvab/V/YKCDXsEpKYKHjAXH5IFOmoq9FsfxjztEqvDc=
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131 h1:gRgU8L1jIWP8noBJk8p1mD3JSJNVDB3HNncjKT6SAFg=
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131/go.mod h1:Hvab/V/YKCDXsEpKYKHjAXH5IFOmoq9FsfxjztEqvDc=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/brotli v1.2.7 h1:UgafRPGMam5ySHfRkhthMqOFA6IFoeF3D7mWIDLjG5A=
github.com/andybalholm/brotli v1.2.7/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
Expand Down
25 changes: 19 additions & 6 deletions cycletls/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import (
const (
chrome = "chrome" //chrome User agent enum
firefox = "firefox" //firefox User agent enum

// maxDecompressedBody caps gzip/deflate/brotli inflate so io.ReadAll
// cannot grow without bound on a malicious Content-Encoding payload.
maxDecompressedBody = 64 << 20 // 64 MiB
)

// Cipher suite mappings from hex to uTLS constants
Expand Down Expand Up @@ -108,28 +112,37 @@ func DecompressBody(Body []byte, encoding []string, content []string) (parsedBod
}
}

func readLimited(r io.Reader) ([]byte, error) {
limited := io.LimitReader(r, maxDecompressedBody+1)
data, err := io.ReadAll(limited)
if err != nil {
return nil, err
}
if int64(len(data)) > maxDecompressedBody {
return nil, errors.New("decompressed body exceeds size limit")
}
return data, nil
}

func gUnzipData(data []byte) (resData []byte, err error) {
gz, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return []byte{}, err
}
defer gz.Close()
respBody, err := io.ReadAll(gz)
return respBody, err
return readLimited(gz)
}
func enflateData(data []byte) (resData []byte, err error) {
zr, err := zlib.NewReader(bytes.NewReader(data))
if err != nil {
return []byte{}, err
}
defer zr.Close()
enflated, err := io.ReadAll(zr)
return enflated, err
return readLimited(zr)
}
func unBrotliData(data []byte) (resData []byte, err error) {
br := brotli.NewReader(bytes.NewReader(data))
respBody, err := io.ReadAll(br)
return respBody, err
return readLimited(br)
}

func ModernChromeSpec(forceHTTP1 bool) *utls.ClientHelloSpec {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ go 1.24

require (
github.com/Danny-Dasilva/CycleTLS/cycletls v1.0.26 // indirect
github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131 // indirect
github.com/andybalholm/brotli v1.2.7 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ github.com/Danny-Dasilva/CycleTLS/cycletls v1.0.26 h1:6fexoGmvzoXMSk14BZ0AirapVm
github.com/Danny-Dasilva/CycleTLS/cycletls v1.0.26/go.mod h1:QFi/EVO7qqru3Ftxz1LR+96jIc91Tifv0DnskF/gWQ8=
github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1 h1:/lqhaiz7xdPr6kuaW1tQ/8DdpWdxkdyd9W/6EHz4oRw=
github.com/Danny-Dasilva/fhttp v0.0.0-20240217042913-eeeb0b347ce1/go.mod h1:Hvab/V/YKCDXsEpKYKHjAXH5IFOmoq9FsfxjztEqvDc=
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131/go.mod h1:Hvab/V/YKCDXsEpKYKHjAXH5IFOmoq9FsfxjztEqvDc=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/brotli v1.2.7 h1:UgafRPGMam5ySHfRkhthMqOFA6IFoeF3D7mWIDLjG5A=
github.com/andybalholm/brotli v1.2.7/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
Expand Down
2 changes: 1 addition & 1 deletion src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require github.com/Danny-Dasilva/CycleTLS/cycletls v1.0.29

require (
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/brotli v1.2.7 // indirect
github.com/gaukas/clienthellod v0.4.2 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131 h1:gRgU8L1jIWP8noBJk8p1mD3JSJNVDB3HNncjKT6SAFg=
github.com/Danny-Dasilva/fhttp v0.0.0-20260106165651-41258808b131/go.mod h1:Hvab/V/YKCDXsEpKYKHjAXH5IFOmoq9FsfxjztEqvDc=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/brotli v1.2.7 h1:UgafRPGMam5ySHfRkhthMqOFA6IFoeF3D7mWIDLjG5A=
github.com/andybalholm/brotli v1.2.7/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
Expand Down