26 KiB
Security Audit Report
1. Executive Summary
This audit reviewed Nexus Terminal, a Wails desktop SSH/Telnet/RAW/VNC/SFTP client with a Go backend and JavaScript frontend. Fix mode was requested, so confirmed issues were patched, regression-tested, and prepared for commit.
The highest-risk issues were SSH trust failures: regular SSH sessions were not wired to the existing known-host verification flow, jump-host SSH connections ignored host keys, and VNC SSH tunnels also ignored host keys. These could allow machine-in-the-middle attacks against SSH credentials and terminal/VNC traffic. This run wired all SSH paths to known-host verification and prompt handling.
Other fixed issues include a local noVNC WebSocket proxy access-control weakness, SSH key-name path traversal in public-key reads, unsafe update-result DOM rendering, inline secret leakage in non-secret profile exports, and current vulnerable dependency versions in golang.org/x/crypto and esbuild.
Residual risk remains in user-selected insecure modes such as direct unencrypted VNC, user-configured HostKeyInsecure, and large local file transfer workflows that still process complete files in memory.
2. Scope Reviewed
Reviewed:
- Go backend packages under
internal/: API bindings, sessions, SSH, VNC, SFTP, storage, secrets, updater, raw/telnet clients, recording, validation, known hosts, config, app startup. - Frontend JavaScript under
frontend/src/js: settings/update UI, host-key event handling, terminal and SFTP/VNC/GitHub surfaces, common DOM helpers. - Dependency manifests and lockfiles:
go.mod,go.sum,frontend/package.json,frontend/package-lock.json. - Build and release scripts:
Makefile,build.sh,release.sh, Wails config. - Tests under
internal/**/*_test.go.
Not in scope:
- Live network penetration testing of remote hosts.
- Reverse engineering packaged binaries.
- Manual verification of Wails runtime origin values in a built app.
3. Methodology
- Static source review of trust boundaries, file operations, SSH/VNC flows, Wails API bindings, frontend DOM sinks, update checks, storage, crypto, and secret handling.
- Dependency review with
govulncheckandnpm audit. - Configuration review of Wails, build scripts, update manifests, and release paths.
- Threat modelling of desktop renderer compromise, local web-origin attacks, remote SSH/VNC attackers, and malicious update metadata.
- Denial-of-service review of network timeouts, WebSocket proxy behavior, transfer paths, and file ingestion.
- Regression tests added for fixed Go issues.
4. Risk Rating Method
Severity considered exploitability, attacker position, affected security property, and impact:
- Critical: practical remote compromise or credential disclosure without meaningful preconditions.
- High: credential/session compromise or machine-in-the-middle exposure on primary connection paths.
- Medium: meaningful local, renderer, or network-assisted exploit with constraints.
- Low: hardening issue, local DoS, or user-assisted exposure.
- Informational: checked area or non-security note.
Confidence reflects direct code evidence and verification coverage.
5. Findings Summary Table
| ID | Severity | Confidence | Title | Component | Status |
|---|---|---|---|---|---|
| SEC-001 | High | High | SSH sessions and jump hosts bypassed known-host verification | SSH sessions | Fixed |
| SEC-002 | High | High | VNC SSH tunnels accepted any SSH host key | VNC over SSH | Fixed |
| SEC-003 | Medium | High | Local noVNC WebSocket proxy used a guessable root endpoint | VNC WebSocket proxy | Fixed |
| SEC-004 | Medium | High | SSH public-key read accepted traversal key names | SSH key API | Fixed |
| SEC-005 | Medium | High | Update metadata rendered through dynamic HTML and inline handler | Settings/update UI | Fixed |
| SEC-006 | Medium | High | Non-secret profile export leaked inline SSH secrets | Profile export | Fixed |
| SEC-007 | High | High | golang.org/x/crypto vulnerable in reachable SSH code |
Go dependencies | Fixed |
| SEC-008 | Medium | High | esbuild dev dependency matched current npm advisory |
Frontend dependency | Fixed |
| SEC-009 | Low | Medium | Large local file upload paths remain memory-heavy | File transfer UX | Open |
| SEC-010 | Low | Medium | Direct VNC remains unencrypted by protocol choice | VNC direct mode | Open |
6. Detailed Findings
Finding ID: SEC-001
Title
SSH sessions and jump hosts bypassed known-host verification
Severity
High
Confidence
High
Affected Files / Components
internal/sessions/manager.go, SSH connection setup, now fixed at lines 657 and 1450-1502.internal/sshclient/sshclient.go, jump-host config, now fixed at lines 599 and 646-652.frontend/src/js/app.js, host-key response correlation, now fixed at lines 334-342.
Description
The project already contained known-host storage, a host-key verifier, and frontend prompt UI, but the main SSH session manager previously called the SSH client's default Connect path. That default path used ssh.InsecureIgnoreHostKey(). Jump hosts also built their own ssh.ClientConfig with ssh.InsecureIgnoreHostKey().
Evidence
The fix now calls ConnectWithHostKeyCallback(...) for regular SSH sessions and propagates the final host-key callback into jump-host configs. A request ID is included in host-key prompt responses to prevent cross-session prompt confusion.
Attack Scenario
An attacker on the network path presents a fake SSH server or jump host. Without host-key verification, Nexus Terminal would accept the attacker's key, allowing credential capture, terminal tampering, or traffic interception.
Impact
Possible SSH credential theft, terminal command tampering, and loss of server authenticity.
Recommendation
Always use the known-host verifier for every SSH hop. Unknown host keys should follow the configured profile policy: strict, ask, accept-new, or explicit insecure.
Safer Example
Implemented: Manager.hostKeyCallback builds sshclient.NewHostKeyVerifier(...), emits a prompt when needed, and connectSSH calls ConnectWithHostKeyCallback(...).
Suggested Tests
Added regression coverage:
TestHostKeyCallbackAcceptNewStoresHostTestHostKeyCallbackStrictRejectsUnknownHostTestJumpHostClientConfigUsesFinalHostKeyCallback
References
- CWE-295: Improper Certificate Validation, analogous host-key trust failure.
- OWASP ASVS 9.1.2: server certificate validation principles.
Assumptions / Limitations
Profiles may still explicitly choose HostKeyInsecure; that is a user-selected insecure mode and remains visible as residual risk.
Finding ID: SEC-002
Title
VNC SSH tunnels accepted any SSH host key
Severity
High
Confidence
High
Affected Files / Components
internal/vncclient/vncclient.go, VNC SSH config, now fixed at lines 49 and 89-95.internal/sessions/manager.go, VNC SSH callback wiring, now fixed at line 1016.
Description
VNC SSH tunnels established SSH connections with ssh.InsecureIgnoreHostKey(), allowing a network attacker to impersonate the SSH tunnel endpoint.
Evidence
The VNC client now requires SSHHostKeyCallback for tunneled SSH. The session manager supplies the same known-host/prompt path used by SSH sessions.
Attack Scenario
An attacker controlling DNS, Wi-Fi, or a gateway presents a fake SSH tunnel host for VNC traffic. The prior client accepted the key and tunneled VNC traffic through the attacker.
Impact
Possible VNC credential exposure, remote desktop traffic interception, and remote interaction tampering.
Recommendation
Require known-host verification for VNC SSH tunnels and reject tunnel setup if no callback is configured.
Safer Example
Implemented: vncclient.Connect returns an error if cfg.SSHHostKeyCallback is nil, and uses the provided callback in ssh.ClientConfig.
Suggested Tests
Covered indirectly by session host-key callback tests and VNC package tests.
References
- CWE-295.
- OWASP ASVS 9.1.2.
Assumptions / Limitations
VNC tunnel host-key policy defaults to ask mode because VNC profiles do not currently expose a separate host-key policy field.
Finding ID: SEC-003
Title
Local noVNC WebSocket proxy used a guessable root endpoint
Severity
Medium
Confidence
High
Affected Files / Components
internal/vncclient/vncclient.go, WebSocket proxy setup, now fixed at lines 171-183 and 255-260.
Description
The local VNC WebSocket proxy listened on 127.0.0.1 with a random port, but accepted upgrades on / and allowed any Origin. A malicious local web page could scan localhost ports and attempt to connect to an active proxy.
Evidence
The proxy now generates a 128-bit random token and exposes only ws://127.0.0.1:<port>/<token> to the Wails frontend.
Attack Scenario
A user visits a malicious web page while a VNC session is active. The page scans localhost WebSocket ports and connects to the proxy endpoint, potentially reading or injecting VNC traffic.
Impact
Possible local cross-origin access to VNC session traffic if the port is discovered.
Recommendation
Require an unguessable per-session URL token and continue binding only to loopback.
Safer Example
Implemented: newProxyToken() uses crypto/rand and the proxy route is registered only at the token path.
Suggested Tests
Added TestNewProxyTokenIsRandomHex.
References
- CWE-346: Origin Validation Error.
- OWASP ASVS 14.4.3: cross-origin resource restrictions.
Assumptions / Limitations
CheckOrigin still returns true because Wails/noVNC origin behavior needs built-app verification. The unguessable path materially reduces exposure.
Finding ID: SEC-004
Title
SSH public-key read accepted traversal key names
Severity
Medium
Confidence
High
Affected Files / Components
internal/api/api.go, SSH key API, now fixed at lines 1653-1708.
Description
GetSSHPublicKey(name) previously built ~/.ssh/<name>.pub directly from a renderer-provided name. Unlike key generation, it did not restrict names to simple key basenames. Names such as ../somefile could read ~/.ssh/../somefile.pub.
Evidence
The fix centralizes key name validation through sshKeyPairPaths(...), allowing only alphanumeric characters, hyphens, and underscores.
Attack Scenario
If renderer-side JavaScript is compromised or a malicious plugin/flow can invoke the Wails API, it can request public-key contents outside the intended .ssh key namespace if the target path ends in .pub.
Impact
Local file disclosure for matching .pub paths under paths reachable through traversal.
Recommendation
Treat key names as basenames, not paths. Apply the same validation to generate, read, and delete operations.
Safer Example
Implemented: GetSSHPublicKey, GenerateSSHKey, and DeleteSSHKey now all use sshKeyPairPaths.
Suggested Tests
Added:
TestSSHKeyPairPathsAcceptsSafeNameTestSSHKeyPairPathsRejectsTraversalTestSSHKeyPairPathsRejectsNestedPath
References
- CWE-22: Path Traversal.
Assumptions / Limitations
The API still intentionally lists and reads legitimate SSH public keys in ~/.ssh.
Finding ID: SEC-005
Title
Update metadata rendered through dynamic HTML and inline handler
Severity
Medium
Confidence
High
Affected Files / Components
frontend/src/js/settings.js, update-check UI, now fixed at lines 940-958.
Description
Update metadata from GitHub or a configured update manifest was inserted into innerHTML, including an inline onclick that interpolated info.releaseURL. A malicious or compromised update metadata source could inject script into the Wails renderer.
Evidence
The fix constructs DOM nodes, sets text via textContent, and attaches a click listener with addEventListener.
Attack Scenario
An attacker controlling a self-hosted update manifest returns a release URL containing a quote and JavaScript payload. The prior inline handler interpolation could execute renderer JavaScript.
Impact
Renderer code execution. In a Wails app, renderer execution can call exposed backend APIs, increasing impact.
Recommendation
Never render remote metadata through string-built HTML. Use textContent and event listeners.
Safer Example
Implemented in settings.js with createElement, document.createTextNode, and addEventListener.
Suggested Tests
No frontend unit harness exists. Verified with npm run build.
References
- CWE-79: Cross-Site Scripting.
- OWASP ASVS 5.3: output encoding.
Assumptions / Limitations
Other static modal templates still use innerHTML; reviewed high-risk dynamic remote metadata paths first.
Finding ID: SEC-006
Title
Non-secret profile export leaked inline SSH secrets
Severity
Medium
Confidence
High
Affected Files / Components
internal/api/api.go, Wails export wrapper, now fixed at lines 683-684.internal/storage/store.go, storage export, now fixed at lines 356-358 and 726-738.
Description
The plain profile export path accepted includeSecrets=false but profiles can still contain inline ssh.proxyPassword and ssh.totpSecret fields. Those fields could be serialized despite a non-secret export request.
Evidence
The fix redacts ProxyPassword and TOTPSecret for non-secret exports in both API and storage layers.
Attack Scenario
A user creates a plain profile export believing secrets are excluded. The exported JSON contains proxy credentials or TOTP secrets that are then shared, backed up, or stored insecurely.
Impact
Credential leakage and potential 2FA bypass for affected profiles.
Recommendation
Redact inline secret fields whenever includeSecrets=false. Prefer encrypted export for any export that intentionally includes secrets.
Safer Example
Implemented redactProfileSecrets(...) in storage and API packages.
Suggested Tests
Added:
TestExportProfilesRedactsInlineSSHSecretsTestRedactProfileSecretsClearsInlineSSHSecrets
References
- CWE-200: Exposure of Sensitive Information.
- OWASP ASVS 8.3: sensitive data protection.
Assumptions / Limitations
The encrypted export path intentionally includes selected profile data and is password-protected.
Finding ID: SEC-007
Title
golang.org/x/crypto vulnerable in reachable SSH code
Severity
High
Confidence
High
Affected Files / Components
go.mod,golang.org/x/crypto, now fixed at line 14.go.mod, indirectgolang.org/x/sys, now updated at line 45.- Reachable SSH code in
internal/sshclient,internal/vncclient,internal/sftpclient.
Description
govulncheck reported seven reachable vulnerabilities in golang.org/x/crypto@v0.51.0, including SSH DoS/panic issues, FIDO/U2F security-key interaction bypass risk, and SSH agent forwarding constraint handling issues.
Evidence
govulncheck identified GO-2026-5033, GO-2026-5020, GO-2026-5019, GO-2026-5018, GO-2026-5017, GO-2026-5013, and GO-2026-5006 as reachable before the update. The dependency is now v0.52.0, and rerun output reports: "No vulnerabilities found. Your code is affected by 0 vulnerabilities."
Attack Scenario
A malicious or compromised SSH server, SSH agent, key material, or protocol peer triggers vulnerable SSH library behavior during connection, channel use, key parsing, or agent forwarding.
Impact
Possible client panic, denial of service, weakened security-key interaction, or agent forwarding constraint mishandling depending on the CVE path.
Recommendation
Keep golang.org/x/crypto at or above v0.52.0 and run govulncheck in CI.
Safer Example
Implemented: go get golang.org/x/crypto@v0.52.0.
Suggested Tests
Ran govulncheck and go test ./....
References
- https://pkg.go.dev/vuln/GO-2026-5033
- https://pkg.go.dev/vuln/GO-2026-5020
- https://pkg.go.dev/vuln/GO-2026-5019
- https://pkg.go.dev/vuln/GO-2026-5018
- https://pkg.go.dev/vuln/GO-2026-5017
- https://pkg.go.dev/vuln/GO-2026-5013
- https://pkg.go.dev/vuln/GO-2026-5006
Assumptions / Limitations
govulncheck also noted vulnerabilities in required modules that are not called by this code.
Finding ID: SEC-008
Title
esbuild dev dependency matched current npm advisory
Severity
Medium
Confidence
High
Affected Files / Components
frontend/package.json,esbuild, now fixed at line 22.frontend/package-lock.json.
Description
npm audit reported GHSA-67mh-4wv8-2f99 for esbuild <=0.24.2: any website could send requests to the dev server and read responses under affected versions.
Evidence
The frontend previously resolved esbuild@0.24.2. It now uses ^0.28.0, and npm audit --json reports zero vulnerabilities.
Attack Scenario
During local development, a malicious website could interact with the esbuild dev server and read responses if a developer runs an affected dev server.
Impact
Possible local development data exposure.
Recommendation
Use a fixed esbuild release and keep npm audit in CI.
Safer Example
Implemented: npm install esbuild@0.28.0.
Suggested Tests
Ran npm audit --json and npm run build.
References
Assumptions / Limitations
This is a devDependency, so production exploitability is lower than runtime dependencies.
Finding ID: SEC-009
Title
Large local file upload paths remain memory-heavy
Severity
Low
Confidence
Medium
Affected Files / Components
frontend/src/js/terminal.js, dropped-file paste/upload flows.internal/api/api.go,SFTPUploadData.
Description
Some upload paths read whole files into memory and send base64 payloads to Go before uploading. This can be expensive for very large local files.
Evidence
SFTPUploadData decodes the entire base64 payload before calling session upload. The frontend drag/drop paste path also reads file contents into strings.
Attack Scenario
A local user or compromised renderer submits very large files, causing high memory use and potential application unresponsiveness.
Impact
Local denial of service.
Recommendation
Add size limits for paste-as-text, prefer path-based streaming upload for large files, and surface user-facing warnings for files above a threshold.
Safer Example
Use file path streaming upload where possible and reject in-memory uploads above a configured maximum.
Suggested Tests
Add boundary tests for maximum accepted in-memory upload size and UI tests for large-file warnings.
References
- CWE-400: Uncontrolled Resource Consumption.
Assumptions / Limitations
This is primarily local/user-assisted. It was left open to avoid changing UX and transfer semantics during the security fix pass.
Finding ID: SEC-010
Title
Direct VNC remains unencrypted by protocol choice
Severity
Low
Confidence
Medium
Affected Files / Components
internal/vncclient/vncclient.go, direct TCP VNC mode.- VNC profile settings.
Description
Direct VNC connections use raw TCP to the VNC server. VNC is commonly unencrypted unless tunneled or separately protected.
Evidence
The direct branch calls net.DialTimeout to the VNC endpoint. SSH tunnel mode is available and now host-key verified.
Attack Scenario
On an untrusted network, direct VNC traffic may expose screen contents or credentials depending on server configuration.
Impact
Sensitive remote desktop data exposure.
Recommendation
Warn users before direct VNC connections and prefer SSH tunnel mode by default.
Safer Example
Default new VNC profiles to SSH tunnel where feasible, or show an explicit "unencrypted direct VNC" warning.
Suggested Tests
Add UI tests that direct VNC profiles show a warning before connection.
References
- OWASP ASVS 9.1: communications security.
Assumptions / Limitations
Some VNC servers may provide their own encryption; this audit did not test remote server configurations.
7. Denial-of-Service Review
Fixed:
- Updated
golang.org/x/cryptoto resolve reachable SSH panic/deadlock/DoS vulnerabilities. - Added random-token access control to the local VNC WebSocket proxy, reducing local cross-origin abuse.
Reviewed and not changed:
- HTTP clients for GitHub and update checks use finite timeouts.
- SSH/Telnet/RAW connection setup uses timeouts.
- VNC WebSocket bridge is intentionally long-lived and uses no read/write timeout.
- Large in-memory file upload/paste paths remain a local DoS risk; see SEC-009.
8. Dependency and Supply-Chain Review
Checks run:
govulncheck: fixed reachablegolang.org/x/cryptovulnerabilities by updating tov0.52.0; rerun found zero reachable vulnerabilities.npm audit --json: fixedesbuildadvisory by updating to0.28.0; rerun found zero vulnerabilities.npm run build: passed after dependency update.
Notes:
xtermpackages are deprecated upstream in favor of@xterm/*, but no current audit advisory was reported in this run.go.modstill has transitive modules with vulnerabilities thatgovulncheckreports as not called by this code.
9. Secrets and Sensitive Data Review
Fixed:
- Non-secret profile export now redacts inline
ProxyPasswordandTOTPSecret. - SSH host-key verification now protects credential submission from basic MITM paths.
Reviewed:
- Vault uses Argon2id and AES-256-GCM with random salts/nonces.
- Vault file is written with
0600permissions and store directories with0700. - OS keychain is preferred where available.
Residual:
- Secret values still exist in process memory while sessions need them.
- Proxy password is still present in profile model state when entered inline.
10. Authentication and Authorisation Review
Fixed:
- SSH, jump host, and VNC-over-SSH authentication now has host authenticity checks before credential use.
- SSH host-key prompt responses are request-correlated.
Reviewed:
- SSH/TOTP flows use profile-specific secret callbacks.
- Agent forwarding is explicit profile configuration.
- Known-host policies include strict, ask, accept-new, and insecure.
Residual:
HostKeyInsecureremains available for user-selected insecure connections.
11. Input Validation and Output Encoding Review
Fixed:
- SSH key names now reject traversal and nested path separators.
- Update metadata is rendered with DOM APIs instead of string-built HTML.
- Non-secret export redacts inline secret fields.
Reviewed:
- SQL operations use parameterized queries.
- Profile validation checks host, port, username, protocol-specific fields, and port-forwarding basics.
- Frontend frequently uses
textContentorescapeHtml; remaininginnerHTMLstatic templates should still be reviewed during future frontend hardening.
12. Infrastructure and Deployment Review
Reviewed:
- Wails app binds Go services directly to the renderer. This makes frontend XSS more impactful than in a browser-only app.
- Build scripts use
set -euo pipefail. - Update checking is opt-in and does not auto-install binaries.
Residual:
- No CI configuration was present in the reviewed file list, so dependency scanning is not currently enforced by repository automation.
13. Logging, Monitoring, and Error Handling Review
Reviewed:
- Audit logger records profile create/update/delete, connection events, secret access, import/export.
- Errors are generally returned to the frontend with enough context for troubleshooting.
Residual:
- Some error messages include file paths, which is acceptable for a local desktop tool but should be considered if telemetry is later added.
14. Test Coverage and Security Regression Gaps
Added tests:
- API SSH key path validation and export redaction.
- Session host-key callback storage and strict rejection behavior.
- Jump-host host-key callback propagation.
- Storage export redaction.
- VNC proxy token generation.
Remaining gaps:
- No frontend unit test harness for DOM injection regressions.
- No integration test for full Wails host-key prompt round trip.
- No VNC WebSocket integration test verifying token-only connection behavior.
- No large-file upload boundary tests.
15. Prioritised Remediation Plan
Immediate:
- Keep the fixes in this audit and ensure they are merged.
- Add dependency scanning to CI:
govulncheck ./...andnpm audit --audit-level=moderate.
Short-term:
- Add a frontend test harness for update UI and other dynamic DOM surfaces.
- Add direct VNC warning UX and default to SSH tunnel where possible.
- Add explicit in-memory upload size limits.
Medium-term:
- Expose host-key policy for VNC SSH tunnels instead of always using ask mode.
- Review all Wails-exposed methods under a compromised-renderer threat model.
- Migrate deprecated
xtermpackages to maintained@xterm/*packages.
Long-term:
- Consider capability gating or confirmation for high-risk renderer-callable APIs such as file deletion, SSH key deletion, local shell selection, and profile import/export.
- Add signed update metadata if self-hosted update manifests become a production channel.
16. False Positives / Needs Manual Verification
- Wails/noVNC Origin behavior in a packaged app should be manually verified. Tokenized VNC WebSocket paths are in place, but Origin policy may still be tightened later.
- Direct VNC encryption depends on remote VNC server configuration and network placement.
HostKeyInsecureis intentionally user-configurable; confirm UI wording makes the risk clear.- Large-file memory usage thresholds need product decisions before enforcement.
17. Final Notes
Fix mode completed with code changes, tests, and dependency updates. The strongest residual concern is the general Wails threat model: a frontend XSS can call powerful local backend APIs. This audit fixed the confirmed dynamic update UI injection path and several backend trust-boundary issues, but future work should continue reducing string-built HTML and adding capability checks around sensitive local operations.