Skip to content
Merged
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
10 changes: 3 additions & 7 deletions go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
25 changes: 6 additions & 19 deletions go/go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand All @@ -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=
Expand Down
6 changes: 5 additions & 1 deletion go/internal/tests/remediations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
121 changes: 73 additions & 48 deletions go/internal/testserver/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"flag"
"fmt"
"io"
"iter"
"log"
"math/big"
"net"
Expand All @@ -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"
Expand Down Expand Up @@ -244,77 +244,89 @@ 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),
chromedp.WithLogf(t.Logf),
)
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)
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion tests/docker-compose-browser-tests.yaml
Original file line number Diff line number Diff line change
@@ -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: [
Expand Down
Loading