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
31 changes: 17 additions & 14 deletions frontend/wave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,23 @@ async function initWave(initOpts: WaveInitOpts) {
subscribeToConnEvents();

// ensures client/window/workspace are loaded into the cache before rendering
const [client, waveWindow, initialTab] = await Promise.all([
WOS.loadAndPinWaveObject<Client>(WOS.makeORef("client", initOpts.clientId)),
WOS.loadAndPinWaveObject<WaveWindow>(WOS.makeORef("window", initOpts.windowId)),
WOS.loadAndPinWaveObject<Tab>(WOS.makeORef("tab", initOpts.tabId)),
]);
const [ws, layoutState] = await Promise.all([
WOS.loadAndPinWaveObject<Workspace>(WOS.makeORef("workspace", waveWindow.workspaceid)),
WOS.reloadWaveObject<LayoutState>(WOS.makeORef("layout", initialTab.layoutstate)),
]);
loadAllWorkspaceTabs(ws);
WOS.wpsSubscribeToObject(WOS.makeORef("workspace", waveWindow.workspaceid));

document.title = `Wave Terminal - ${initialTab.name}`; // TODO update with tab name change

try {
const [client, waveWindow, initialTab] = await Promise.all([
WOS.loadAndPinWaveObject<Client>(WOS.makeORef("client", initOpts.clientId)),
WOS.loadAndPinWaveObject<WaveWindow>(WOS.makeORef("window", initOpts.windowId)),
WOS.loadAndPinWaveObject<Tab>(WOS.makeORef("tab", initOpts.tabId)),
]);
const [ws, layoutState] = await Promise.all([
WOS.loadAndPinWaveObject<Workspace>(WOS.makeORef("workspace", waveWindow.workspaceid)),
WOS.reloadWaveObject<LayoutState>(WOS.makeORef("layout", initialTab.layoutstate)),
]);
loadAllWorkspaceTabs(ws);
WOS.wpsSubscribeToObject(WOS.makeORef("workspace", waveWindow.workspaceid));
document.title = `Wave Terminal - ${initialTab.name}`; // TODO update with tab name change
} catch (e) {
console.error("Failed initialization error", e);
getApi().sendLog("Error in initialization (wave.ts, loading required objects) " + e.message + "\n" + e.stack);
}
Comment on lines +184 to +200
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 | 🟠 Major

Consider adding early return or UI fallback after initialization failure.

The try-catch correctly logs initialization errors, but the function continues to execute (lines 201-220) even when core objects fail to load. If client, waveWindow, or initialTab are undefined due to loading failures, downstream code (React components, global store access, etc.) may encounter runtime errors.

Consider one of these approaches:

  1. Early return with user notification:
 } catch (e) {
     console.error("Failed initialization error", e);
     getApi().sendLog("Error in initialization (wave.ts, loading required objects) " + e.message + "\n" + e.stack);
+    pushFlashError("Failed to initialize Wave Terminal. Please restart the application.");
+    return;
 }
  1. Conditional rendering guard:
 } catch (e) {
     console.error("Failed initialization error", e);
     getApi().sendLog("Error in initialization (wave.ts, loading required objects) " + e.message + "\n" + e.stack);
+    // Set a flag so App can render an error state
+    globalStore.set(atoms.initFailure, true);
 }

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

🤖 Prompt for AI Agents
In frontend/wave.ts around lines 184 to 200, the catch logs initialization
failures but execution continues causing downstream runtime errors; modify the
catch to stop further initialization by performing an early return (or set a
visible UI error/fallback state and skip the rest of the init code), and ensure
any subsequent code (lines 201-220) is guarded by a check that required objects
(client, waveWindow, initialTab) are present before use or by rendering a
fallback error UI so no undefined objects are accessed.

registerGlobalKeys();
registerElectronReinjectKeyHandler();
registerControlShiftStateUpdateHandler();
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ require (
github.com/spf13/cobra v1.10.1
github.com/ubuntu/gowsl v0.0.0-20240906163211-049fd49bd93b
github.com/wavetermdev/htmltoken v0.2.0
github.com/wavetermdev/waveterm/tsunami v0.0.0-00010101000000-000000000000
github.com/wavetermdev/waveterm/tsunami v0.12.3
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 | 🔴 Critical

🧩 Analysis chain

Verify tsunami v0.12.3 availability and security status.

The dependency has been updated from a pseudo-version to v0.12.3. Please verify that this version exists and is free from known vulnerabilities.

Run the following script to check the version and security status:


🏁 Script executed:

#!/bin/bash
# Check if the tsunami module version exists and look for security advisories

# Check if the version tag exists in the local tsunami directory (since it's replaced with ./tsunami)
if [ -d "./tsunami" ]; then
  cd ./tsunami
  echo "=== Checking git tags in ./tsunami ==="
  git tag -l "v0.12.3"
  
  echo -e "\n=== Checking current commit in ./tsunami ==="
  git log -1 --oneline
  
  cd ..
fi

# Check for Go security advisories related to the module
echo -e "\n=== Checking for security vulnerabilities ==="
go list -m -json github.com/wavetermdev/waveterm/tsunami | jq -r '.Version'

Length of output: 265



---

🏁 Script executed:

```shell
#!/bin/bash
# Inspect go.mod for tsunami dependency details and replace directive

echo "=== Checking go.mod for tsunami dependency and replace directive ==="
rg -A5 -B5 'tsunami' go.mod

echo -e "\n=== Checking if tsunami is a replace directive ==="
grep -n 'replace.*tsunami' go.mod || echo "No replace directive found in go.mod"

echo -e "\n=== Full tsunami dependency line ==="
grep 'tsunami' go.mod

Length of output: 1034


🌐 Web query:

tsunami v0.12.3 Go module security vulnerabilities

💡 Result:

I couldn't find any public security advisories or CVEs specifically affecting the Tsunami Go module at version v0.12.3. Sources checked: the Tsunami GitHub repo/docs, the Go vulnerability database, and NVD (no tsunami v0.12.3 advisories found). [1][2][3]

If you want, I can:

  • re-check NVD/GitHub security advisories and the Go vuln DB for any updates (I can run a fresh search now), or
  • show commands to scan your codebase (govulncheck / go list) to detect any transitive vulnerabilities affecting your module.

Sources:

  1. Tsunami GitHub repository / docs — google/tsunami-security-scanner. [https://github.com/google/tsunami-security-scanner]. [1]
  2. Go vulnerability database documentation / database. [https://go.dev/doc/security/vuln/database]. [2]
  3. NVD (National Vulnerability Database) search (no matching CVE for tsunami v0.12.3). [https://nvd.nist.gov]. [3]

Version v0.12.3 declared in go.mod does not exist as a git tag in the local tsunami directory.

The dependency uses a replace directive (replace github.com/wavetermdev/waveterm/tsunami => ./tsunami), so the actual version being used is the current state of the local ./tsunami directory at commit 952f980. The version declaration in go.mod (v0.12.3) is mismatched and misleading. Either:

  • Tag the local tsunami repository with v0.12.3 to match the declaration, or
  • Update go.mod to reflect the actual commit hash of the local tsunami dependency.

No known security vulnerabilities were found for this module.

🤖 Prompt for AI Agents
In go.mod around line 36, the declared version
"github.com/wavetermdev/waveterm/tsunami v0.12.3" does not match the local
replace (./tsunami) state (commit 952f980), which is misleading; fix by either
tagging the local tsunami repo with v0.12.3 so the version exists, or update the
go.mod entry to reference the actual commit (use the pseudo-version or full
commit-based version) that corresponds to 952f980 (and run go mod tidy) so the
declared version matches the local replace.

golang.org/x/crypto v0.45.0
golang.org/x/mod v0.30.0
golang.org/x/sync v0.18.0
Expand Down Expand Up @@ -80,6 +80,7 @@ require (
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/outrigdev/goid v0.3.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuE
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/outrigdev/goid v0.3.0 h1:t/otQD3EXc45cLtQVPUnNgEyRaTQA4cPeu3qVcrsIws=
github.com/outrigdev/goid v0.3.0/go.mod h1:hEH7f27ypN/GHWt/7gvkRoFYR0LZizfUBIAbak4neVE=
github.com/photostorm/pty v1.1.19-0.20230903182454-31354506054b h1:cLGKfKb1uk0hxI0Q8L83UAJPpeJ+gSpn3cCU/tjd3eg=
github.com/photostorm/pty v1.1.19-0.20230903182454-31354506054b/go.mod h1:KO+FcPtyLAiRC0hJwreJVvfwc7vnNz77UxBTIGHdPVk=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
Expand Down
15 changes: 7 additions & 8 deletions pkg/service/workspaceservice/workspaceservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,14 @@ func (svc *WorkspaceService) CloseTab_Meta() tsgenmeta.MethodMeta {
// returns the new active tabid
func (svc *WorkspaceService) CloseTab(ctx context.Context, workspaceId string, tabId string, fromElectron bool) (*CloseTabRtnType, waveobj.UpdatesRtnType, error) {
ctx = waveobj.ContextWithUpdates(ctx)
tab, err := wstore.DBMustGet[*waveobj.Tab](ctx, tabId)
if err != nil {
return nil, nil, fmt.Errorf("error getting tab: %w", err)
tab, err := wstore.DBGet[*waveobj.Tab](ctx, tabId)
if err == nil && tab != nil {
go func() {
for _, blockId := range tab.BlockIds {
blockcontroller.StopBlockController(blockId)
}
}()
}
go func() {
for _, blockId := range tab.BlockIds {
blockcontroller.StopBlockController(blockId)
}
}()
newActiveTabId, err := wcore.DeleteTab(ctx, workspaceId, tabId, true)
if err != nil {
return nil, nil, fmt.Errorf("error closing tab: %w", err)
Expand Down
6 changes: 3 additions & 3 deletions pkg/wcore/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func createBlockObj(ctx context.Context, tabId string, blockDef *waveobj.BlockDe
// recursive: if true, will recursively close parent tab, window, workspace, if they are empty.
// Returns new active tab id, error.
func DeleteBlock(ctx context.Context, blockId string, recursive bool) error {
block, err := wstore.DBMustGet[*waveobj.Block](ctx, blockId)
block, err := wstore.DBGet[*waveobj.Block](ctx, blockId)
if err != nil {
return fmt.Errorf("error getting block: %w", err)
}
Expand Down Expand Up @@ -223,11 +223,11 @@ func deleteBlockObj(ctx context.Context, blockId string) (int, error) {
}
}
wstore.DBDelete(tx.Context(), waveobj.OType_Block, blockId)

// Clean up block runtime info
blockORef := waveobj.MakeORef(waveobj.OType_Block, blockId)
wstore.DeleteRTInfo(blockORef)

return parentBlockCount, nil
})
}
Expand Down
17 changes: 9 additions & 8 deletions pkg/wcore/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,12 @@ func DeleteTab(ctx context.Context, workspaceId string, tabId string, recursive

// close blocks (sends events + stops block controllers)
tab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId)
if tab == nil {
return "", fmt.Errorf("tab not found: %q", tabId)
}
for _, blockId := range tab.BlockIds {
err := DeleteBlock(ctx, blockId, false)
if err != nil {
return "", fmt.Errorf("error deleting block %s: %w", blockId, err)
if tab != nil {
for _, blockId := range tab.BlockIds {
err := DeleteBlock(ctx, blockId, false)
if err != nil {
return "", fmt.Errorf("error deleting block %s: %w", blockId, err)
}
}
}

Expand All @@ -319,7 +318,9 @@ func DeleteTab(ctx context.Context, workspaceId string, tabId string, recursive

wstore.DBUpdate(ctx, ws)
wstore.DBDelete(ctx, waveobj.OType_Tab, tabId)
wstore.DBDelete(ctx, waveobj.OType_LayoutState, tab.LayoutState)
if tab != nil {
wstore.DBDelete(ctx, waveobj.OType_LayoutState, tab.LayoutState)
}

// if no tabs remaining, close window
if recursive && newActiveTabId == "" {
Expand Down
Loading