From bd6d22a40846341ad6fd47d5eef86628e8ec8760 Mon Sep 17 00:00:00 2001 From: Robin Thellend Date: Mon, 26 Oct 2026 09:52:42 -0700 Subject: [PATCH] Update chromedp, cdproto, and headless-shell test image - Upgrade github.com/chromedp/chromedp to v0.21.0 and github.com/chromedp/cdproto to v0.158.5 (removing transitive sysutil and gobwas/ws dependencies). - Migrate go/internal/testserver/main_test.go to the chromedp v0.21.0 / cdproto v0.158.5 typed command, generic action, and event iterator APIs. - Update tests/docker-compose-browser-tests.yaml to chromedp/headless-shell:157.0.8103.15. - Update TestXtermWorkarounds/CSIRepGuard/VulnerabilityWithoutRemediation to use a 5-code-unit combined character cell so repeatPrecedingCharacter(2147483647) exceeds V8's 32 GiB TypedArray limit in Chrome 157+ and throws RangeError immediately. --- CHANGELOG.md | 9 ++ go/go.mod | 10 +- go/go.sum | 25 ++--- go/internal/tests/remediations_test.go | 6 +- go/internal/testserver/main_test.go | 121 ++++++++++++++---------- tests/docker-compose-browser-tests.yaml | 2 +- 6 files changed, 97 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bdfa96..f1bcba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # SSH Term Release Notes +## next + +### :wrench: Misc + +* Update go dependencies: + * upgraded github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d => v0.158.5 + * upgraded github.com/chromedp/chromedp v0.14.2 => v0.21.0 + * upgraded github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 => v0.0.0-20260820222146-c27c302e5fc3 + ## v0.11.6 ### :lock: Security fixes diff --git a/go/go.mod b/go/go.mod index 7a3b0e6..199a1ee 100644 --- a/go/go.mod +++ b/go/go.mod @@ -3,8 +3,8 @@ module github.com/c2FmZQ/sshterm go 1.27.3 require ( - github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d - github.com/chromedp/chromedp v0.14.2 + github.com/chromedp/cdproto v0.158.5 + github.com/chromedp/chromedp v0.21.0 github.com/fxamacker/cbor/v2 v2.9.6 github.com/gorilla/websocket v1.5.3 github.com/pkg/sftp v1.13.12 @@ -14,12 +14,8 @@ require ( ) require ( - github.com/chromedp/sysutil v1.1.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect - github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect - github.com/gobwas/httphead v0.1.0 // indirect - github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.4.0 // indirect + github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 // indirect github.com/kr/fs v0.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/x448/float16 v0.8.4 // indirect diff --git a/go/go.sum b/go/go.sum index 7082aae..595056c 100644 --- a/go/go.sum +++ b/go/go.sum @@ -1,31 +1,19 @@ -github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d h1:ZtA1sedVbEW7EW80Iz2GR3Ye6PwbJAJXjv7D74xG6HU= -github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k= -github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZSzM= -github.com/chromedp/chromedp v0.14.2/go.mod h1:rHzAv60xDE7VNy/MYtTUrYreSc0ujt2O1/C3bzctYBo= -github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM= -github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8= +github.com/chromedp/cdproto v0.158.5 h1:KBaaESG33oLvC/O7D3wQPBL0vsO91xoT6DkUvswj42Q= +github.com/chromedp/cdproto v0.158.5/go.mod h1:h68JaIuFr9o3yiayxEYhBdvaxgYO3shDrPSYhNlyfu0= +github.com/chromedp/chromedp v0.21.0 h1:nVHgS2LHyhZJ1l3BWRbG2eFIFYiJMnvkJbELbd8WBoQ= +github.com/chromedp/chromedp v0.21.0/go.mod h1:6z1Y5FdKUxRzxx2qcC5h1xjaBJrSWrZp1mjgITfNZig= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.9.6 h1:NHZPxuUjJXQjX9itA785y/5NKSlI4HQ+S7noZsmI7hs= github.com/fxamacker/cbor/v2 v2.9.6/go.mod h1:4EaZp6ClVunETjQ4GHmGNzysDK91Lr+r6Yz6tJqzVCw= -github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs= -github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= -github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= +github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 h1:UADEEmDKgfXbtnGJZ97beY5XLo9ZechG1nlU4KnRrkE= +github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= -github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pkg/sftp v1.13.12 h1:Myhzy/ZzLPnCtE2F2WtDHsgs8RfgMnH2gfbHuCJhkMU= github.com/pkg/sftp v1.13.12/go.mod h1:uNkH9roSXglNJqM+glJJi+TQXQUm0fXFWqCFmT8hsN0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -42,7 +30,6 @@ github.com/xrash/smetrics v0.0.0-20261013231157-39c47d93efba h1:tXo8fNGjrZLpWGM8 github.com/xrash/smetrics v0.0.0-20261013231157-39c47d93efba/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54= golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM= diff --git a/go/internal/tests/remediations_test.go b/go/internal/tests/remediations_test.go index b9bfbcd..0529b3d 100644 --- a/go/internal/tests/remediations_test.go +++ b/go/internal/tests/remediations_test.go @@ -103,7 +103,11 @@ func TestXtermWorkarounds(t *testing.T) { "csiLoopGuard": false, }) defer cleanup() - writeTerm(t, term, "a") + // Use a 5-code-unit combined character cell so n.length * 2147483647 * 4 + // (40 GiB) exceeds V8's 32 GiB TypedArray limit in Chrome 157+ and + // immediately throws RangeError instead of allocating 8 GiB and looping + // 2.1B times. + writeTerm(t, term, "a\u0300\u0301\u0302\u0303") paramsArr := js.Global().Get("Int32Array").New(1) paramsArr.SetIndex(0, 2147483647) diff --git a/go/internal/testserver/main_test.go b/go/internal/testserver/main_test.go index 43a17cf..5797e3e 100644 --- a/go/internal/testserver/main_test.go +++ b/go/internal/testserver/main_test.go @@ -38,6 +38,7 @@ import ( "flag" "fmt" "io" + "iter" "log" "math/big" "net" @@ -48,10 +49,9 @@ import ( "testing" "time" - "github.com/chromedp/cdproto" - "github.com/chromedp/cdproto/runtime" "github.com/chromedp/cdproto/webauthn" "github.com/chromedp/chromedp" + "github.com/chromedp/chromedp/remote" "github.com/gorilla/websocket" "github.com/pkg/sftp" "golang.org/x/crypto/ssh" @@ -244,9 +244,11 @@ func TestSSHTerm(t *testing.T) { t.Run("WASM App Tests", func(t *testing.T) { ctx, cancel = context.WithTimeout(t.Context(), 5*time.Minute) defer cancel() - ctx, cancel = chromedp.NewRemoteAllocator(ctx, *withChromeDP) + ctx, cancel = remote.NewAllocator(ctx, *withChromeDP) defer cancel() + var eventWG sync.WaitGroup + defer eventWG.Wait() ctx, cancel = chromedp.NewContext(ctx, //chromedp.WithDebugf(t.Logf), chromedp.WithErrorf(t.Logf), @@ -254,67 +256,77 @@ func TestSSHTerm(t *testing.T) { ) defer cancel() - chromedp.ListenTarget(ctx, func(ev any) { - switch ev := ev.(type) { - case *cdproto.Message: - case *runtime.EventConsoleAPICalled: - //t.Logf("* console.%s call:", ev.Type) - //for _, arg := range ev.Args { - // t.Logf(" %s - %s", arg.Type, arg.Value) - //} - case *runtime.EventExceptionThrown: - t.Logf("Exception: * %s", ev.ExceptionDetails.Error()) - case *webauthn.EventCredentialAdded, *webauthn.EventCredentialAsserted, *webauthn.EventCredentialDeleted, *webauthn.EventCredentialUpdated: - t.Logf("WebAuthn event: %#v", ev) - default: - //t.Logf("Target event: %#v", ev) + listenEvents(&eventWG, chromedp.Console(ctx), func(m chromedp.ConsoleMessage) { + if m.IsException() { + t.Logf("Exception: * %s", m.Text) } }) + listenEvents(&eventWG, chromedp.Events(ctx, webauthn.CredentialAdded), func(ev webauthn.EventCredentialAdded) { + t.Logf("WebAuthn event: %#v", ev) + }) + listenEvents(&eventWG, chromedp.Events(ctx, webauthn.CredentialAsserted), func(ev webauthn.EventCredentialAsserted) { + t.Logf("WebAuthn event: %#v", ev) + }) + listenEvents(&eventWG, chromedp.Events(ctx, webauthn.CredentialDeleted), func(ev webauthn.EventCredentialDeleted) { + t.Logf("WebAuthn event: %#v", ev) + }) + listenEvents(&eventWG, chromedp.Events(ctx, webauthn.CredentialUpdated), func(ev webauthn.EventCredentialUpdated) { + t.Logf("WebAuthn event: %#v", ev) + }) - if err := chromedp.Run(ctx, webauthn.Enable().WithEnableUI(false)); err != nil { + if _, err := chromedp.Call(ctx, webauthn.Enable, webauthn.EnableParams{EnableUI: new(false)}); err != nil { t.Fatalf("webauthn.Enable(): %v", err) } - var authenticatorID webauthn.AuthenticatorID - if err := chromedp.Run(ctx, - chromedp.ActionFunc(func(ctx context.Context) error { - authID, err := webauthn.AddVirtualAuthenticator(&webauthn.VirtualAuthenticatorOptions{ - Protocol: webauthn.AuthenticatorProtocolCtap2, - Ctap2version: webauthn.Ctap2versionCtap21, - Transport: webauthn.AuthenticatorTransportInternal, - HasResidentKey: true, - HasUserVerification: true, - AutomaticPresenceSimulation: true, - IsUserVerified: true, - }).Do(ctx) - authenticatorID = authID - return err - }), - ); err != nil { + authRes, err := chromedp.Call(ctx, webauthn.AddVirtualAuthenticator, webauthn.AddVirtualAuthenticatorParams{ + Options: &webauthn.VirtualAuthenticatorOptions{ + Protocol: webauthn.AuthenticatorProtocolCtap2, + Ctap2version: webauthn.Ctap2versionCtap21, + Transport: webauthn.AuthenticatorTransportInternal, + HasResidentKey: true, + HasUserVerification: true, + AutomaticPresenceSimulation: true, + IsUserVerified: true, + }, + }) + if err != nil { t.Fatalf("webauthn.AddVirtualAuthenticator(): %v", err) } + authenticatorID := authRes.AuthenticatorID t.Logf("AddVirtualAuthenticator: %q", authenticatorID) - if err := chromedp.Run(ctx, - webauthn.ClearCredentials(authenticatorID), - webauthn.SetAutomaticPresenceSimulation(authenticatorID, true), - chromedp.ActionFunc(func(ctx context.Context) error { - creds, err := webauthn.GetCredentials(authenticatorID).Do(ctx) - t.Logf("Credentials: %v", creds) - return err - }), - ); err != nil { + if _, err := chromedp.Call(ctx, webauthn.ClearCredentials, webauthn.ClearCredentialsParams{ + AuthenticatorID: authenticatorID, + }); err != nil { + t.Fatalf("webauthn.ClearCredentials(): %v", err) + } + if _, err := chromedp.Call(ctx, webauthn.SetAutomaticPresenceSimulation, webauthn.SetAutomaticPresenceSimulationParams{ + AuthenticatorID: authenticatorID, + Enabled: true, + }); err != nil { t.Fatalf("webauthn.SetAutomaticPresenceSimulation(): %v", err) } + credsRes, err := chromedp.Call(ctx, webauthn.GetCredentials, webauthn.GetCredentialsParams{ + AuthenticatorID: authenticatorID, + }) + if err != nil { + t.Fatalf("webauthn.GetCredentials(): %v", err) + } + t.Logf("Credentials: %v", credsRes.Credentials) - var res, output string - if err := chromedp.Run(ctx, + if err := chromedp.Do(ctx, chromedp.Navigate("https://devtest.local:8443/tests.html"), chromedp.WaitVisible("#done"), - chromedp.Evaluate(`window.sshApp.exited`, &res), - chromedp.Evaluate(`window.sshApp.term.selectAll(), window.sshApp.term.getSelection()`, &output), ); err != nil { - t.Logf("chromedp.Run: %v", err) + t.Logf("chromedp.Do: %v", err) + } + res, err := chromedp.Run(ctx, chromedp.Evaluate[string](`window.sshApp.exited`)) + if err != nil { + t.Logf("chromedp.Run(exited): %v", err) + } + output, err := chromedp.Run(ctx, chromedp.Evaluate[string](`window.sshApp.term.selectAll(), window.sshApp.term.getSelection()`)) + if err != nil { + t.Logf("chromedp.Run(output): %v", err) } t.Log(output) t.Log(res) @@ -324,6 +336,19 @@ func TestSSHTerm(t *testing.T) { }) } +func listenEvents[E any](wg *sync.WaitGroup, seq iter.Seq2[E, error], fn func(E)) { + wg.Add(1) + go func() { + defer wg.Done() + for ev, err := range seq { + if err != nil { + return + } + fn(ev) + } + }() +} + var _ net.Conn = (*netConn)(nil) type netConn struct { diff --git a/tests/docker-compose-browser-tests.yaml b/tests/docker-compose-browser-tests.yaml index f8a2422..2a417a7 100644 --- a/tests/docker-compose-browser-tests.yaml +++ b/tests/docker-compose-browser-tests.yaml @@ -1,7 +1,7 @@ services: chrome: container_name: "headless-shell" - image: "chromedp/headless-shell:141.0.7390.37" + image: "chromedp/headless-shell:157.0.8103.15" user: "65534:65534" shm_size: "2gb" command: [