Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Single Session Mode #1634

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Single Session Mode #1634

wants to merge 5 commits into from

Conversation

oneirocosm
Copy link
Member

Allows connections to spawn multiple sessions from only one remote session. Useful specifically in situations where sshd ForceCommand is being used for 2-factor authentication. The main idea is that a single connection session is made with a command containing the wsh connserver. Then, the connserver is able to spawn local sessions that can be routed through the connserver instead of through individual shells.

This adds a shellexec command for launching connections that only
require one remote session.
This adds the code that will call the shellexec code. It also disables
the existing connserver/install code for single shell mode.
Copy link

coderabbitai bot commented Dec 27, 2024

Walkthrough

The pull request introduces a new configuration option for managing connection sessions across multiple files in the project. An optional boolean property conn:singlesession is added to the ConnKeywords type within the global declaration. Modifications are made to the DoRunShellCommand method in the BlockController struct, where a new boolean variable singleSession is introduced to influence shell process handling for remote connections.

Additionally, a new boolean variable singleSession is added to the connectInternal method of the SSHConn struct, modifying the logic for installing Wave Shell Extensions based on connection settings. The shellexec package introduces a new variable singleSessionCmdTemplate and a function StartSingleSessionRemoteShellProc to manage single-session remote shell execution. Lastly, the ConnKeywords struct in the RPC type definitions is updated to include the new ConnSingleSession field.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
pkg/shellexec/shellexec.go (1)

10-10: Consider using text/template if HTML-specific features are unnecessary
Since there's no HTML sanitization or escaping in use, text/template might suffice and be clearer.

pkg/blockcontroller/blockcontroller.go (2)

319-325: Variable naming clarity
singleSession is fine, but consider renaming it to something like useSingleSession to be more self-descriptive.


350-379: Follow up on TODO for domain socket approach
The comment indicates future domain socket signaling. Either implement the plan or remove the comment if it’s no longer necessary.

pkg/remote/conncontroller/conncontroller.go (1)

514-514: Add a quick doc comment
Explain why singleSession is needed so future maintainers can easily see its purpose.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c9f357 and 7880eb8.

📒 Files selected for processing (5)
  • frontend/types/gotypes.d.ts (1 hunks)
  • pkg/blockcontroller/blockcontroller.go (2 hunks)
  • pkg/remote/conncontroller/conncontroller.go (2 hunks)
  • pkg/shellexec/shellexec.go (2 hunks)
  • pkg/wshrpc/wshrpctypes.go (1 hunks)
🔇 Additional comments (4)
pkg/shellexec/shellexec.go (1)

241-253: Sanitize any user-supplied inputs in singleSessionCmdTemplate
If external input is inserted into this template, ensure it’s properly escaped or validated to avoid command injection.

pkg/remote/conncontroller/conncontroller.go (1)

524-526: Check thread-safety on connSettings usage
If other goroutines can modify connSettings, ensure consistent locking or concurrency control.

pkg/wshrpc/wshrpctypes.go (1)

465-465: Clear naming
ConnSingleSession accurately conveys the single-session concept. This addition looks consistent with the rest of the code.

frontend/types/gotypes.d.ts (1)

294-294: Consistent typing with backend
Adding "conn:singlesession"?: boolean; ensures the front-end matches the new backend field. This looks correct.

Comment on lines +255 to +310
func StartSingleSessionRemoteShellProc(termSize waveobj.TermSize, conn *conncontroller.SSHConn) (*ShellProc, error) {
client := conn.GetClient()
session, err := client.NewSession()
if err != nil {
return nil, err
}
remoteStdinRead, remoteStdinWriteOurs, err := os.Pipe()
if err != nil {
return nil, err
}

remoteStdoutReadOurs, remoteStdoutWrite, err := os.Pipe()
if err != nil {
return nil, err
}

pipePty := &PipePty{
remoteStdinWrite: remoteStdinWriteOurs,
remoteStdoutRead: remoteStdoutReadOurs,
}
if termSize.Rows == 0 || termSize.Cols == 0 {
termSize.Rows = shellutil.DefaultTermRows
termSize.Cols = shellutil.DefaultTermCols
}
if termSize.Rows <= 0 || termSize.Cols <= 0 {
return nil, fmt.Errorf("invalid term size: %v", termSize)
}
session.Stdin = remoteStdinRead
session.Stdout = remoteStdoutWrite
session.Stderr = remoteStdoutWrite

session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil)

wshDir := "~/.waveterm/bin"
wshPath := wshDir + "/wsh"
var installWords = map[string]string{
"wshPath": wshPath,
"tempPath": wshPath + ".temp",
}

//todo add code that allows streaming base64 for download

singleSessionCmd := &bytes.Buffer{}
cmdTemplate := template.Must(template.New("").Parse(singleSessionCmdTemplate))
cmdTemplate.Execute(singleSessionCmd, installWords)

log.Printf("full single session command is: %s", singleSessionCmd)
sessionWrap := MakeSessionWrap(session, singleSessionCmd.String(), pipePty)

err = sessionWrap.Start()
if err != nil {
pipePty.Close()
return nil, err
}
return &ShellProc{Cmd: sessionWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Check the error from session.RequestPty
The call to session.RequestPty might fail, leaving the session in a partial state. Handling this error helps identify pty allocation issues.

- session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil)
+if errPty := session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil); errPty != nil {
+    pipePty.Close()
+    session.Close()
+    return nil, fmt.Errorf("failed to request pty: %w", errPty)
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func StartSingleSessionRemoteShellProc(termSize waveobj.TermSize, conn *conncontroller.SSHConn) (*ShellProc, error) {
client := conn.GetClient()
session, err := client.NewSession()
if err != nil {
return nil, err
}
remoteStdinRead, remoteStdinWriteOurs, err := os.Pipe()
if err != nil {
return nil, err
}
remoteStdoutReadOurs, remoteStdoutWrite, err := os.Pipe()
if err != nil {
return nil, err
}
pipePty := &PipePty{
remoteStdinWrite: remoteStdinWriteOurs,
remoteStdoutRead: remoteStdoutReadOurs,
}
if termSize.Rows == 0 || termSize.Cols == 0 {
termSize.Rows = shellutil.DefaultTermRows
termSize.Cols = shellutil.DefaultTermCols
}
if termSize.Rows <= 0 || termSize.Cols <= 0 {
return nil, fmt.Errorf("invalid term size: %v", termSize)
}
session.Stdin = remoteStdinRead
session.Stdout = remoteStdoutWrite
session.Stderr = remoteStdoutWrite
session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil)
wshDir := "~/.waveterm/bin"
wshPath := wshDir + "/wsh"
var installWords = map[string]string{
"wshPath": wshPath,
"tempPath": wshPath + ".temp",
}
//todo add code that allows streaming base64 for download
singleSessionCmd := &bytes.Buffer{}
cmdTemplate := template.Must(template.New("").Parse(singleSessionCmdTemplate))
cmdTemplate.Execute(singleSessionCmd, installWords)
log.Printf("full single session command is: %s", singleSessionCmd)
sessionWrap := MakeSessionWrap(session, singleSessionCmd.String(), pipePty)
err = sessionWrap.Start()
if err != nil {
pipePty.Close()
return nil, err
}
return &ShellProc{Cmd: sessionWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
}
func StartSingleSessionRemoteShellProc(termSize waveobj.TermSize, conn *conncontroller.SSHConn) (*ShellProc, error) {
client := conn.GetClient()
session, err := client.NewSession()
if err != nil {
return nil, err
}
remoteStdinRead, remoteStdinWriteOurs, err := os.Pipe()
if err != nil {
return nil, err
}
remoteStdoutReadOurs, remoteStdoutWrite, err := os.Pipe()
if err != nil {
return nil, err
}
pipePty := &PipePty{
remoteStdinWrite: remoteStdinWriteOurs,
remoteStdoutRead: remoteStdoutReadOurs,
}
if termSize.Rows == 0 || termSize.Cols == 0 {
termSize.Rows = shellutil.DefaultTermRows
termSize.Cols = shellutil.DefaultTermCols
}
if termSize.Rows <= 0 || termSize.Cols <= 0 {
return nil, fmt.Errorf("invalid term size: %v", termSize)
}
session.Stdin = remoteStdinRead
session.Stdout = remoteStdoutWrite
session.Stderr = remoteStdoutWrite
if errPty := session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil); errPty != nil {
pipePty.Close()
session.Close()
return nil, fmt.Errorf("failed to request pty: %w", errPty)
}
wshDir := "~/.waveterm/bin"
wshPath := wshDir + "/wsh"
var installWords = map[string]string{
"wshPath": wshPath,
"tempPath": wshPath + ".temp",
}
//todo add code that allows streaming base64 for download
singleSessionCmd := &bytes.Buffer{}
cmdTemplate := template.Must(template.New("").Parse(singleSessionCmdTemplate))
cmdTemplate.Execute(singleSessionCmd, installWords)
log.Printf("full single session command is: %s", singleSessionCmd)
sessionWrap := MakeSessionWrap(session, singleSessionCmd.String(), pipePty)
err = sessionWrap.Start()
if err != nil {
pipePty.Close()
return nil, err
}
return &ShellProc{Cmd: sessionWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
pkg/shellexec/shellexec.go (1)

299-299: Implement the base64 streaming functionality.

The TODO comment indicates missing implementation for streaming base64 data.

Would you like me to help implement the base64 streaming functionality for downloading the binary?

pkg/blockcontroller/blockcontroller.go (1)

374-379: Implement connection server and domain socket handling.

The TODO comments indicate missing functionality for:

  1. Receiving signals from the process when domain socket is opened
  2. Forwarding the unix domain socket
  3. Setting the wsh boolean flag

Consider implementing a simple protocol for domain socket readiness:

  1. Process writes a special marker to stdout when socket is ready
  2. Parent process monitors stdout for this marker
  3. Upon marker detection, proceed with socket forwarding

Would you like me to provide a detailed implementation plan?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7880eb8 and f86b607.

📒 Files selected for processing (4)
  • frontend/types/gotypes.d.ts (1 hunks)
  • pkg/blockcontroller/blockcontroller.go (2 hunks)
  • pkg/remote/conncontroller/conncontroller.go (2 hunks)
  • pkg/shellexec/shellexec.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/types/gotypes.d.ts
🔇 Additional comments (3)
pkg/shellexec/shellexec.go (1)

290-290: Handle errors from session.RequestPty.

The call to session.RequestPty might fail, leaving the session in a partial state.

Apply this fix:

-session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil)
+if err := session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil); err != nil {
+    pipePty.Close()
+    session.Close()
+    return nil, fmt.Errorf("failed to request pty: %w", err)
+}
pkg/remote/conncontroller/conncontroller.go (1)

Line range hint 520-532: LGTM! Clean integration of single session mode.

The implementation correctly:

  1. Reads singleSession from connection settings
  2. Integrates with existing wsh installation logic
  3. Maintains backward compatibility
pkg/blockcontroller/blockcontroller.go (1)

350-373: LGTM! Clean implementation of single session shell process.

The code properly:

  1. Handles credentials and connection setup
  2. Validates connection status
  3. Sets up JWT token for authentication
  4. Launches single session shell process

Comment on lines +245 to +257
var singleSessionCmdTemplate = `bash -c ' \
{{.wshPath}} connserver --single || \
( uname -m && \
read -r count &&
for ((i=0; i<count; i++)); do \
read -r line || break; \
printf "%%b" "$line"; \
done > {{.tempPath}} && \
chmod a+x {{.tempPath}} && \
mv {{.tempPath}} {{.wshPath}} &&
{{.wshPath}} connserver --single \
)
`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling and secure file permissions.

The command template has several potential issues:

  1. No error handling for file operations (read/write/chmod/mv)
  2. Using chmod a+x grants execute permission to all users, which is overly permissive
  3. No cleanup of temporary file on failure

Consider these improvements:

var singleSessionCmdTemplate = `bash -c ' \
{{.wshPath}} connserver --single || \
( uname -m && \
  read -r count &&
  for ((i=0; i<count; i++)); do \
      read -r line || break; \
      printf "%%b" "$line"; \
  done > {{.tempPath}} && \
- chmod a+x {{.tempPath}} && \
+ chmod 0700 {{.tempPath}} && \
  mv {{.tempPath}} {{.wshPath}} && 
  {{.wshPath}} connserver --single \
) || { rm -f {{.tempPath}}; exit 1; }
'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var singleSessionCmdTemplate = `bash -c ' \
{{.wshPath}} connserver --single || \
( uname -m && \
read -r count &&
for ((i=0; i<count; i++)); do \
read -r line || break; \
printf "%%b" "$line"; \
done > {{.tempPath}} && \
chmod a+x {{.tempPath}} && \
mv {{.tempPath}} {{.wshPath}} &&
{{.wshPath}} connserver --single \
)
`
var singleSessionCmdTemplate = `bash -c ' \
{{.wshPath}} connserver --single || \
( uname -m && \
read -r count &&
for ((i=0; i<count; i++)); do \
read -r line || break; \
printf "%%b" "$line"; \
done > {{.tempPath}} && \
chmod 0700 {{.tempPath}} && \
mv {{.tempPath}} {{.wshPath}} &&
{{.wshPath}} connserver --single \
) || { rm -f {{.tempPath}}; exit 1; }
'

Comment on lines +292 to +297
wshDir := "~/.waveterm/bin"
wshPath := wshDir + "/wsh"
var installWords = map[string]string{
"wshPath": wshPath,
"tempPath": wshPath + ".temp",
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate directory existence before file operations.

The code assumes the ~/.waveterm/bin directory exists without validation.

Add directory validation and creation:

wshDir := "~/.waveterm/bin"
+if err := os.MkdirAll(wshDir, 0700); err != nil {
+    return nil, fmt.Errorf("failed to create wsh directory: %w", err)
+}
wshPath := wshDir + "/wsh"

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/remote/conncontroller/conncontroller.go (1)

Line range hint 515-527: LGTM! Consider adding documentation.

The implementation correctly reads the single session mode configuration and modifies the WSH installation behavior accordingly. This change aligns with the PR objective of enabling connections that can spawn multiple sessions from a single remote session.

Consider adding a comment explaining the purpose of the singleSession flag and its implications for WSH installation, particularly in ForceCommand scenarios.

pkg/blockcontroller/blockcontroller.go (1)

381-386: Implementation needed for domain socket handling.

The TODO comments indicate several critical pieces of functionality are missing:

  1. Signal handling for domain socket creation
  2. Unix domain socket forwarding
  3. WSH boolean state management

These are essential for proper single session mode operation.

Would you like me to help implement these missing components or create GitHub issues to track them?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f86b607 and 1db0a60.

📒 Files selected for processing (2)
  • pkg/blockcontroller/blockcontroller.go (2 hunks)
  • pkg/remote/conncontroller/conncontroller.go (2 hunks)
🧰 Additional context used
🪛 GitHub Check: Analyze (javascript-typescript)
pkg/blockcontroller/blockcontroller.go

[failure] 363-363363:
not enough return values


[failure] 368-368368:
not enough return values


[failure] 373-373373:
not enough return values


[failure] 379-379379:
not enough return values

🪛 GitHub Check: Analyze (go)
pkg/blockcontroller/blockcontroller.go

[failure] 363-363363:
not enough return values


[failure] 368-368368:
not enough return values


[failure] 373-373373:
not enough return values


[failure] 379-379379:
not enough return values

🪛 GitHub Actions: CodeQL
pkg/blockcontroller/blockcontroller.go

[error] 363-363363: not enough return values

🔇 Additional comments (2)
pkg/remote/conncontroller/conncontroller.go (1)

527-527: LGTM! Condition handles WSH installation correctly.

The condition enableWsh && !singleSession ensures WSH is only installed when appropriate, preventing interference with ForceCommand scenarios.

pkg/blockcontroller/blockcontroller.go (1)

327-332: LGTM! Configuration retrieval is implemented correctly.

The code properly retrieves the single session mode configuration from connection settings.

Comment on lines 357 to 380
} else if remoteName != "" && singleSession {
credentialCtx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelFunc()

opts, err := remote.ParseOpts(remoteName)
if err != nil {
return err
}
conn := conncontroller.GetConn(credentialCtx, opts, false, &wshrpc.ConnKeywords{})
connStatus := conn.DeriveConnStatus()
if connStatus.Status != conncontroller.Status_Connected {
return fmt.Errorf("not connected, cannot start shellproc")
}
if !blockMeta.GetBool(waveobj.MetaKey_CmdNoWsh, false) {
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId, Conn: conn.Opts.String()}, conn.GetDomainSocketName())
if err != nil {
return fmt.Errorf("error making jwt token: %w", err)
}
cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr
}
shellProc, err = shellexec.StartSingleSessionRemoteShellProc(rc.TermSize, conn)
if err != nil {
return err
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix return values in single session handling.

The function setupAndStartShellProcess expects a return type of (*shellexec.ShellProc, error), but several return statements only return error. This will cause runtime issues.

Apply this fix to the return statements:

-			return err
+			return nil, err
-			return fmt.Errorf("not connected, cannot start shellproc")
+			return nil, fmt.Errorf("not connected, cannot start shellproc")
-				return fmt.Errorf("error making jwt token: %w", err)
+				return nil, fmt.Errorf("error making jwt token: %w", err)
-			return err
+			return nil, err
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if remoteName != "" && singleSession {
credentialCtx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelFunc()
opts, err := remote.ParseOpts(remoteName)
if err != nil {
return err
}
conn := conncontroller.GetConn(credentialCtx, opts, false, &wshrpc.ConnKeywords{})
connStatus := conn.DeriveConnStatus()
if connStatus.Status != conncontroller.Status_Connected {
return fmt.Errorf("not connected, cannot start shellproc")
}
if !blockMeta.GetBool(waveobj.MetaKey_CmdNoWsh, false) {
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId, Conn: conn.Opts.String()}, conn.GetDomainSocketName())
if err != nil {
return fmt.Errorf("error making jwt token: %w", err)
}
cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr
}
shellProc, err = shellexec.StartSingleSessionRemoteShellProc(rc.TermSize, conn)
if err != nil {
return err
}
} else if remoteName != "" && singleSession {
credentialCtx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelFunc()
opts, err := remote.ParseOpts(remoteName)
if err != nil {
return nil, err
}
conn := conncontroller.GetConn(credentialCtx, opts, false, &wshrpc.ConnKeywords{})
connStatus := conn.DeriveConnStatus()
if connStatus.Status != conncontroller.Status_Connected {
return nil, fmt.Errorf("not connected, cannot start shellproc")
}
if !blockMeta.GetBool(waveobj.MetaKey_CmdNoWsh, false) {
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId, Conn: conn.Opts.String()}, conn.GetDomainSocketName())
if err != nil {
return nil, fmt.Errorf("error making jwt token: %w", err)
}
cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr
}
shellProc, err = shellexec.StartSingleSessionRemoteShellProc(rc.TermSize, conn)
if err != nil {
return nil, err
}
🧰 Tools
🪛 GitHub Check: Analyze (javascript-typescript)

[failure] 363-363363:
not enough return values


[failure] 368-368368:
not enough return values


[failure] 373-373373:
not enough return values


[failure] 379-379379:
not enough return values

🪛 GitHub Check: Analyze (go)

[failure] 363-363363:
not enough return values


[failure] 368-368368:
not enough return values


[failure] 373-373373:
not enough return values


[failure] 379-379379:
not enough return values

🪛 GitHub Actions: CodeQL

[error] 363-363363: not enough return values

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1db0a60 and ce7d6dd.

📒 Files selected for processing (1)
  • pkg/blockcontroller/blockcontroller.go (2 hunks)
🔇 Additional comments (2)
pkg/blockcontroller/blockcontroller.go (2)

327-332: LGTM! Clean implementation of single session configuration.

The code properly retrieves and handles the single session mode configuration from the connection settings.


381-386: Verify WSH functionality in single session mode.

The comments indicate that the connection server is disabled for single session connections, which might affect WSH (Wave Shell) functionality. This needs verification to ensure it doesn't break existing features.

Run the following script to check WSH usage in single session mode:

✅ Verification successful

Based on the search results, I can now provide a comprehensive verification of the WSH functionality in single session mode:

WSH functionality is properly handled in single session mode

The codebase shows that WSH (Wave Shell) functionality is correctly managed for single session connections through several key mechanisms:

  1. The connection's WSH state is properly tracked via atomic boolean flags and respected throughout the codebase
  2. The system gracefully falls back to non-WSH mode when needed:
    if !conn.WshEnabled.Load() {
        shellProc, err = shellexec.StartRemoteShellProcNoWsh(rc.TermSize, cmdStr, cmdOpts, conn)
    }
  3. WSH enablement is configurable both globally and per-connection:
    enableWsh := config.Settings.ConnWshEnabled
    if connSettings.ConnWshEnabled != nil {
        enableWsh = *connSettings.ConnWshEnabled
    }
  4. The JWT token and domain socket handling required for WSH communication is properly implemented for both single session and regular modes

The commented-out code in the review is part of a planned enhancement but doesn't affect the current functionality, as the system already handles both WSH and non-WSH modes correctly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check WSH functionality usage in single session mode
# Test: Search for WSH-related function calls and verify their compatibility
# Expect: Functions that might need adaptation for single session mode

# Check WSH-related function calls
rg -A 5 "WshEnabled|StartRemoteShellProc.*wsh.*|wshutil\." 

# Check for potential WSH dependencies
ast-grep --pattern 'if $_.WshEnabled.Load() {
  $$$
}'

Length of output: 68367

Comment on lines +357 to +386
} else if remoteName != "" && singleSession {
credentialCtx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelFunc()

opts, err := remote.ParseOpts(remoteName)
if err != nil {
return nil, err
}
conn := conncontroller.GetConn(credentialCtx, opts, false, &wshrpc.ConnKeywords{})
connStatus := conn.DeriveConnStatus()
if connStatus.Status != conncontroller.Status_Connected {
return nil, fmt.Errorf("not connected, cannot start shellproc")
}
if !blockMeta.GetBool(waveobj.MetaKey_CmdNoWsh, false) {
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId, Conn: conn.Opts.String()}, conn.GetDomainSocketName())
if err != nil {
return nil, fmt.Errorf("error making jwt token: %w", err)
}
cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr
}
shellProc, err = shellexec.StartSingleSessionRemoteShellProc(rc.TermSize, conn)
if err != nil {
return nil, err
}
// todo
// i have disabled the conn server for this type of connection
// this means we need to receive a signal from the process once it has
// opened a domain socket. then, once that is done, we can forward the
// unix domain socket here. also, we need to set the wsh boolean true as
// is done in the current connserver implementation
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for domain socket setup.

The current implementation lacks proper error handling for the case where StartSingleSessionRemoteShellProc succeeds but the subsequent domain socket setup fails. This could lead to resource leaks or hanging connections.

Consider adding cleanup code and proper error propagation:

 		shellProc, err = shellexec.StartSingleSessionRemoteShellProc(rc.TermSize, conn)
 		if err != nil {
 			return nil, err
 		}
+		// Wait for domain socket setup with timeout
+		socketSetupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+		defer cancel()
+		select {
+		case <-socketSetupCtx.Done():
+			shellProc.Close()
+			return nil, fmt.Errorf("timeout waiting for domain socket setup")
+		case err := <-shellProc.SocketSetupErrCh:
+			if err != nil {
+				shellProc.Close()
+				return nil, fmt.Errorf("domain socket setup failed: %w", err)
+			}
+		}

Committable suggestion skipped: line range outside the PR's diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant