Skip to content

implement cmd:jwt and fix remote execution of commands#2292

Merged
sawka merged 3 commits intomainfrom
sawka/cmd-jwt
Aug 26, 2025
Merged

implement cmd:jwt and fix remote execution of commands#2292
sawka merged 3 commits intomainfrom
sawka/cmd-jwt

Conversation

@sawka
Copy link
Member

@sawka sawka commented Aug 26, 2025

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds a new metadata flag "cmd:jwt" across the stack: declares MetaKey_CmdJwt, adds CmdJwt to MetaTSType and the frontend MetaType, and propagates it via blockcontroller into a new CommandOptsType.ForceJwt field. Updates shellexec to optionally inject a Wave JWT into environments for WSL (when a token exists), and for local/remote sessions when ForceJwt is true; also adds logs, adjusts command construction, and removes unused shell-type helpers. In server startup, adds cleanup to unset several WAVETERM_* environment variables after a successful grab.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1205418 and 7f8fae6.

📒 Files selected for processing (1)
  • docs/docs/customwidgets.mdx (1 hunks)
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sawka/cmd-jwt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@sawka sawka merged commit 56f0389 into main Aug 26, 2025
6 of 7 checks passed
@sawka sawka deleted the sawka/cmd-jwt branch August 26, 2025 23:23
Copy link
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/shellexec/shellexec.go (1)

267-274: Redact sensitive tokens in command logs

The only remaining critical issue is that we still emit the full combined command—complete with WAVETERM_SWAP_TOKEN and WAVETERM_JWT—to the console. This must be redacted. No changes are needed around ForceJwt gating, as both the WSL and standard execution paths already respect cmdOpts.ForceJwt (see the if jwtToken != "" && cmdOpts.ForceJwt checks in pkg/shellexec/shellexec.go and pkg/blockcontroller/blockcontroller.go).

Please apply the following mandatory refactor:

• Replace the plain logging call:

- log.Printf("full combined command: %s", cmdCombined)
+ log.Printf("full combined command: %s", redactSensitive(cmdCombined))

• Add a redaction helper and import:

import "regexp"

var sensitiveEnvRe = regexp.MustCompile(`\b(WAVETERM_SWAP_TOKEN|WAVETERM_JWT)=\S+`)
func redactSensitive(s string) string {
    return sensitiveEnvRe.ReplaceAllString(s, "$1=<redacted>")
}

Optionally, add unit tests for redactSensitive to ensure no tokens slip through.

🧹 Nitpick comments (6)
frontend/types/gotypes.d.ts (1)

539-539: Add MetaType key "cmd:jwt" — looks good; consider adding connection-level support.

The TS typing addition is correct and consistent with surrounding MetaType keys.

Optional follow-ups:

  • Allow setting this at the connection level too (ConnKeywords), for org-wide defaults.
  • Briefly document semantics in your user-facing metadata docs (default false; when true, Wave injects a JWT into the command environment as per backend rules).

Additional change outside this hunk (if you want connection-level support):

--- a/frontend/types/gotypes.d.ts
+++ b/frontend/types/gotypes.d.ts
@@
     type ConnKeywords = {
@@
         "cmd:env"?: {[key: string]: string};
+        "cmd:jwt"?: boolean;
         "cmd:initscript"?: string;
         "cmd:initscript.sh"?: string;
         "cmd:initscript.bash"?: string;
         "cmd:initscript.zsh"?: string;
         "cmd:initscript.pwsh"?: string;
         "cmd:initscript.fish"?: string;
pkg/waveobj/metaconsts.go (1)

54-54: New meta key constant MetaKey_CmdJwt — consistent and well placed.

The constant fits the existing grouping/order and naming. Consider adding a short godoc-style comment in the generator so it shows up in generated places.

cmd/server/main-server.go (1)

262-271: Centralize WAVETERM env‐var cleanup and reuse canonical constants

To avoid drift and make it easier to add or remove vars in one place:

• Create a single slice of env‐var names in cmd/server/main-server.go
• Import and use the existing pkg/wavebase constants where available (e.g. WaveJwtTokenVarName)
• For any names not yet defined in pkg/wavebase, consider adding them there so all WAVETERM_* vars live in one package

Suggested diff:

--- a/cmd/server/main-server.go
+++ b/cmd/server/main-server.go
@@ -261,12 +261,16 @@ func startServer(...) error {
-   // Remove WAVETERM env vars that leak from prod => dev
-   os.Unsetenv("WAVETERM_CLIENTID")
-   os.Unsetenv("WAVETERM_WORKSPACEID")
-   os.Unsetenv("WAVETERM_TABID")
-   os.Unsetenv("WAVETERM_BLOCKID")
-   os.Unsetenv("WAVETERM_CONN")
-   os.Unsetenv("WAVETERM_JWT")
-   os.Unsetenv("WAVETERM_VERSION")
+   // Remove WAVETERM env vars that leak from prod => dev
+   // TODO: move all of these into pkg/wavebase as constants to keep names in one place
+   leakVars := []string{
+       "WAVETERM_CLIENTID",
+       "WAVETERM_WORKSPACEID",
+       "WAVETERM_TABID",
+       "WAVETERM_BLOCKID",
+       "WAVETERM_CONN",
+       wavebase.WaveJwtTokenVarName, // == "WAVETERM_JWT"
+       "WAVETERM_VERSION",
+   }
+   for _, k := range leakVars {
+       _ = os.Unsetenv(k)
+   }

This ensures all cleanup targets are defined together, and reuses the verified wavebase.WaveJwtTokenVarName constant.

pkg/blockcontroller/blockcontroller.go (2)

551-561: Apply cmd:jwt to “shell” controller as well (if intended).

Currently, ForceJwt is only set for “cmd” blocks. If the semantics of cmd:jwt are meant to apply to interactive “shell” blocks too (local/remote), set it here as well so shellexec can inject the JWT in non-WSH paths when requested.

Proposed change (outside the modified hunk, shown for clarity):

--- a/pkg/blockcontroller/blockcontroller.go
+++ b/pkg/blockcontroller/blockcontroller.go
@@
   if bc.ControllerType == BlockController_Shell {
       cmdOpts.Interactive = true
       cmdOpts.Login = true
       cmdOpts.Cwd = blockMeta.GetString(waveobj.MetaKey_CmdCwd, "")
       if cmdOpts.Cwd != "" {
           cwdPath, err := wavebase.ExpandHomeDir(cmdOpts.Cwd)
           if err != nil {
               return nil, err
           }
           cmdOpts.Cwd = cwdPath
       }
+      // Honor cmd:jwt for shell-type controllers as well.
+      cmdOpts.ForceJwt = blockMeta.GetBool(waveobj.MetaKey_CmdJwt, false)

If the design is “cmd:jwt only affects direct command runs,” feel free to ignore, but please confirm expected behavior in a short code comment near createCmdStrAndOpts for future readers.


372-373: Propagate cmd:jwt → cmdOpts.ForceJwt verified and explicit

  • Ripgrep check confirms no other .ForceJwt = assignments outside of blockcontroller.go – this is the sole source of truth for ForceJwt.
  • All other occurrences in pkg/shellexec/shellexec.go are reads/uses of ForceJwt, not writes.
  • ✅ Behavior is explicit: only createCmdStrAndOpts (in pkg/blockcontroller/blockcontroller.go at lines 372–373) drives the flag.

Optional improvement:

  • Add a targeted unit test for createCmdStrAndOpts to assert that when blockMeta contains MetaKey_CmdJwt=true, the returned cmdOpts.ForceJwt is true.
pkg/waveobj/wtypemeta.go (1)

53-54: MetaTSType.CmdJwt field addition — consistent with TS and consts.

Matches the TS type and the new metaconst. Consider adding a brief comment in the metadata docs explaining precedence and when JWT is injected (WSL always if available; local/SSH when ForceJwt=true), to align frontend and backend expectations.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b81b824 and 1205418.

📒 Files selected for processing (6)
  • cmd/server/main-server.go (1 hunks)
  • frontend/types/gotypes.d.ts (1 hunks)
  • pkg/blockcontroller/blockcontroller.go (1 hunks)
  • pkg/shellexec/shellexec.go (6 hunks)
  • pkg/waveobj/metaconsts.go (1 hunks)
  • pkg/waveobj/wtypemeta.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/blockcontroller/blockcontroller.go (2)
pkg/util/utilfn/utilfn.go (1)
  • GetBool (71-88)
pkg/waveobj/metaconsts.go (1)
  • MetaKey_CmdJwt (54-54)
pkg/shellexec/shellexec.go (4)
pkg/blocklogger/blocklogger.go (1)
  • Debugf (86-92)
pkg/wavebase/wavebase.go (1)
  • WaveJwtTokenVarName (36-36)
pkg/wshutil/wshutil.go (1)
  • WaveJwtTokenVarName (50-50)
pkg/util/shellutil/shellutil.go (1)
  • UpdateCmdEnv (237-262)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build for TestDriver.ai
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (4)
pkg/shellexec/shellexec.go (4)

215-215: Debug log of cmdStr is fine; keep it debug-only

Logging the raw cmdStr can include secrets. Good that this is Debugf (gated). Please keep it out of info-level logs going forward.


513-517: LGTM: Local JWT injection gated behind ForceJwt

Behavior matches the new flag and uses UpdateCmdEnv rather than string-prefixing — safer and clearer. Keep the debug log at debug level only.


38-46: Add documentation for ForceJwt and retain existing JSON tag

To clarify the intent of ForceJwt and avoid breaking downstream consumers, add a comment above the field and keep the forcejwt tag unchanged:

 pkg/shellexec/shellexec.go
 type CommandOptsType struct {
   Interactive bool                      `json:"interactive,omitempty"`
   Login       bool                      `json:"login,omitempty"`
   Cwd         string                    `json:"cwd,omitempty"`
   ShellPath   string                    `json:"shellPath,omitempty"`
   ShellOpts   []string                  `json:"shellOpts,omitempty"`
   SwapToken   *shellutil.TokenSwapEntry `json:"swapToken,omitempty"`
-  ForceJwt    bool                      `json:"forcejwt,omitempty"`
+  // ForceJwt injects WAVETERM_JWT into the spawned process environment when true.
+  // Note: WSL handling currently differs—see StartWslShellProc for Windows-specific logic.
+  ForceJwt    bool                      `json:"forcejwt,omitempty"`
 }

• No other references to the JSON tag exist beyond this struct, the two shell-start sites (lines 447 and 514), and the blockcontroller assignment (line 372), so renaming the tag risks breaking existing JSON contracts.
• Keeping the lowercase forcejwt tag ensures backward compatibility while the comment makes the behavior clear.


446-450: Quote environment assignments in shell commands

The code currently embeds raw token values into a VAR=value prefix for sh -c. While JWTs are strictly base64url (letters, digits, -, _, and .) and won’t break a shell assignment, your swap-token payload (from PackForClient()) may include characters (e.g. JSON delimiters, padding = signs, or other symbols) that could. To be safe, please either:

  • Confirm that both
    • cmdOpts.SwapToken.PackForClient()
    • cmdOpts.SwapToken.Env[wavebase.WaveJwtTokenVarName]
      only ever produce characters in [A-Za-z0-9._-], or
  • Wrap both values in a POSIX‐quoting helper.

Suggested diff (in pkg/shellexec/shellexec.go):

- cmdCombined = fmt.Sprintf(`%s=%s %s`, wavebase.WaveSwapTokenVarName, packedToken, cmdCombined)
+ cmdCombined = fmt.Sprintf(`%s=%s %s`, wavebase.WaveSwapTokenVarName, shellutil.PosixQuote(packedToken), cmdCombined)

…later…

- cmdCombined = fmt.Sprintf(`%s=%s %s`, wavebase.WaveJwtTokenVarName, jwtToken, cmdCombined)
+ cmdCombined = fmt.Sprintf(`%s=%s %s`, wavebase.WaveJwtTokenVarName, shellutil.PosixQuote(jwtToken), cmdCombined)

Helper in pkg/util/shellutil:

// PosixQuote wraps s in single quotes and escapes embedded single quotes.
func PosixQuote(s string) string {
  if s == "" {
    return "''"
  }
  return "'" + strings.ReplaceAll(s, "'", `'"'"'`) + "'"
}

Please verify the character sets of your tokens if you opt not to add quoting.

Comment on lines 366 to 369
conn.Infof(logCtx, "swaptoken: %s\n", cmdOpts.SwapToken.Token)
conn.Debugf(logCtx, "cmdStr: %q\n", cmdStr)

if cmdStr == "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove token logs and redact combined command on SSH path

  • conn.Infof(... "swaptoken: %s", cmdOpts.SwapToken.Token) at Line 366 logs a sensitive token at info level. This is a credential leak.
  • Logging cmdCombined at info level (Line 404 below) also leaks env-injected secrets.

Switch to Debugf and redact, or drop entirely.

Apply:

- conn.Infof(logCtx, "swaptoken: %s\n", cmdOpts.SwapToken.Token)
+ conn.Debugf(logCtx, "swaptoken: <redacted>\n")
- conn.Infof(logCtx, "starting shell, using command: %s\n", cmdCombined)
+ conn.Debugf(logCtx, "starting shell, using command: %s\n", redactSensitive(cmdCombined))

Optional: search for similar leaks elsewhere:

#!/bin/bash
rg -nP '(Infof|Printf)\([^)]*(WAVETERM_(JWT|SWAP_TOKEN)|swaptoken|packed swaptoken|PackForClient)'

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