diff --git a/lib/httplib/httpheaders.go b/lib/httplib/httpheaders.go index 9a62c8eb2703e..ddc36c71fb18f 100644 --- a/lib/httplib/httpheaders.go +++ b/lib/httplib/httpheaders.go @@ -188,6 +188,10 @@ var desktopSessionRe = regexp.MustCompile(`^/web/cluster/[^/]+/desktops/[^/]+/[^ // which is a route to a desktop recording that uses WASM. var recordingRe = regexp.MustCompile(`^/web/cluster/[^/]+/session/[^/]+$`) +// regex for the ssh terminal endpoint /web/cluster/:clusterId/console/node/:sid/:login +// which is a route to a ssh session that uses WASM. +var sshSessionRe = regexp.MustCompile(`^/web/cluster/[^/]+/console/node/[^/]+/[^/]+$`) + var indexCSPStringCache *cspCache = newCSPCache() func getIndexContentSecurityPolicyString(cfg proto.Features, urlPath string) string { @@ -197,7 +201,7 @@ func getIndexContentSecurityPolicyString(cfg proto.Features, urlPath string) str } // Nothing found in cache, calculate regex and result - withWasm := desktopSessionRe.MatchString(urlPath) || recordingRe.MatchString(urlPath) + withWasm := desktopSessionRe.MatchString(urlPath) || recordingRe.MatchString(urlPath) || sshSessionRe.MatchString(urlPath) cspString := GetContentSecurityPolicyString( getIndexContentSecurityPolicy(withWasm), ) diff --git a/lib/httplib/httplib_test.go b/lib/httplib/httplib_test.go index ef7adc063da41..33fce42ecb976 100644 --- a/lib/httplib/httplib_test.go +++ b/lib/httplib/httplib_test.go @@ -327,6 +327,23 @@ func TestSetIndexContentSecurityPolicy(t *testing.T) { "connect-src": "'self' wss:", }, }, + { + name: "for web ssh session (with wasm)", + features: proto.Features{}, + urlPath: "/web/cluster/:clusterId/console/node/:sessionId/:username", + expectedCspVals: map[string]string{ + "default-src": "'self'", + "base-uri": "'self'", + "form-action": "'self'", + "frame-ancestors": "'none'", + "object-src": "'none'", + "script-src": "'self' 'wasm-unsafe-eval'", + "style-src": "'self' 'unsafe-inline'", + "img-src": "'self' data: blob:", + "font-src": "'self' data:", + "connect-src": "'self' wss:", + }, + }, { name: "for cloud based usage & desktop session, with wasm", features: proto.Features{Cloud: true, IsUsageBased: true, IsStripeManaged: true}, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6c15eb71f40f..85894a9ca8cf9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -356,6 +356,9 @@ importers: '@xterm/addon-fit': specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) + '@xterm/addon-image': + specifier: ^0.8.0 + version: 0.8.0(@xterm/xterm@5.5.0) '@xterm/addon-web-links': specifier: ^0.11.0 version: 0.11.0(@xterm/xterm@5.5.0) @@ -2826,6 +2829,11 @@ packages: peerDependencies: '@xterm/xterm': ^5.0.0 + '@xterm/addon-image@0.8.0': + resolution: {integrity: sha512-b/dqpFn3jUad2pUP5UpF4scPIh0WdxRQL/1qyiahGfUI85XZTCXo0py9G6AcOR2QYUw8eJ8EowGspT7BQcgw6A==} + peerDependencies: + '@xterm/xterm': ^5.2.0 + '@xterm/addon-web-links@0.11.0': resolution: {integrity: sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==} peerDependencies: @@ -9780,6 +9788,10 @@ snapshots: dependencies: '@xterm/xterm': 5.5.0 + '@xterm/addon-image@0.8.0(@xterm/xterm@5.5.0)': + dependencies: + '@xterm/xterm': 5.5.0 + '@xterm/addon-web-links@0.11.0(@xterm/xterm@5.5.0)': dependencies: '@xterm/xterm': 5.5.0 diff --git a/web/packages/teleport/package.json b/web/packages/teleport/package.json index 523e6cb14eb86..d76bc3addb6d4 100644 --- a/web/packages/teleport/package.json +++ b/web/packages/teleport/package.json @@ -32,11 +32,12 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/sdk-trace-web": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@xterm/xterm": "^5.5.0", "@xterm/addon-canvas": "^0.7.0", "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-image": "^0.8.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/addon-webgl": "^0.18.0", + "@xterm/xterm": "^5.5.0", "create-react-class": "^15.6.3", "events": "3.3.0" }, diff --git a/web/packages/teleport/src/lib/term/terminal.ts b/web/packages/teleport/src/lib/term/terminal.ts index cc8f68a2ff12b..d4b4e40dfda57 100644 --- a/web/packages/teleport/src/lib/term/terminal.ts +++ b/web/packages/teleport/src/lib/term/terminal.ts @@ -19,6 +19,7 @@ import '@xterm/xterm/css/xterm.css'; import { ITheme, Terminal } from '@xterm/xterm'; import { FitAddon } from '@xterm/addon-fit'; +import { ImageAddon } from '@xterm/addon-image'; import { WebglAddon } from '@xterm/addon-webgl'; import { WebLinksAddon } from '@xterm/addon-web-links'; import { CanvasAddon } from '@xterm/addon-canvas'; @@ -50,6 +51,7 @@ export default class TtyTerminal { _convertEol: boolean; _debouncedResize: DebouncedFunc<() => void>; _fitAddon = new FitAddon(); + _imageAddon = new ImageAddon(); _webLinksAddon = new WebLinksAddon(); _webglAddon: WebglAddon; _canvasAddon = new CanvasAddon(); @@ -88,6 +90,7 @@ export default class TtyTerminal { this.term.loadAddon(this._fitAddon); this.term.loadAddon(this._webLinksAddon); + this.term.loadAddon(this._imageAddon); // handle context loss and load webgl addon try { // try to create a new WebglAddon. If webgl is not supported, this @@ -155,6 +158,7 @@ export default class TtyTerminal { this._disconnect(); this._debouncedResize.cancel(); this._fitAddon.dispose(); + this._imageAddon.dispose(); this._webglAddon?.dispose(); this._canvasAddon?.dispose(); this._el.innerHTML = null;