fix: revert breaking security changes and update tests for version removal

Restores dangerous-settings and allowSelfSigned which are required for
self-hosted servers with self-signed certificates. Makes HealthResponse.version
optional to match server-side removal, and updates router tests to assert
version is correctly omitted from unauthenticated endpoints.

https://claude.ai/code/session_01KKo3RwjdmcNzkgXNfUkgNT
This commit is contained in:
Claude
2026-04-04 18:02:26 +00:00
parent 1673c37b9c
commit 1d8bfe2d6a
4 changed files with 23 additions and 13 deletions
+6 -1
View File
@@ -23,7 +23,12 @@ tauri-plugin-global-shortcut = "2"
tauri-plugin-notification = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-http = { version = "2.5.7", features = ["rustls-tls"] }
# NOTE: dangerous-settings is required for self-signed certificate support.
# The app is designed for self-hosted servers which commonly use self-signed certs.
# The Rust TOFU WS proxy handles WebSocket certs, but HTTP API calls (health,
# login, upload) and attachment downloads need this feature to accept self-signed
# certs via the `danger` fetch option. Scoped to server URLs only in attachments.ts.
tauri-plugin-http = { version = "2.5.7", features = ["rustls-tls", "dangerous-settings"] }
tauri-plugin-opener = "2"
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
+2 -1
View File
@@ -497,7 +497,8 @@ export interface RegisterResponse {
/** GET /api/health response. */
export interface HealthResponse {
readonly status: string;
readonly version: string;
/** Version is omitted from unauthenticated endpoints to prevent fingerprinting. */
readonly version?: string;
readonly uptime: number;
readonly online_users: number;
}
+7 -5
View File
@@ -85,10 +85,12 @@ if (!appEl) {
// Create core services
const router = createRouter("connect");
// H-3: Default to strict TLS verification. Self-signed cert support is handled
// by the Rust-side TOFU WS proxy and the CertMismatchModal, not by disabling
// TLS validation in the HTTP client.
const api = createApiClient({ host: "", allowSelfSigned: false }, () => {
// NOTE: allowSelfSigned must be true because the app targets self-hosted servers
// that commonly use self-signed certificates. The Rust TOFU WS proxy handles
// WebSocket certs, but HTTP API calls (health, login, register, upload) have no
// equivalent proxy and need this flag to function. The ideal future fix is adding
// a TOFU HTTP proxy in Rust alongside the existing WS proxy.
const api = createApiClient({ host: "", allowSelfSigned: true }, () => {
log.warn("Session expired (401), clearing auth");
clearAuth();
});
@@ -195,7 +197,7 @@ function runHealthChecks(
connectPage.updateHealthStatus(profile.host, {
status: elapsed > 1500 ? "slow" : "online",
latencyMs: elapsed,
version: health.version,
version: health.version ?? null,
onlineUsers: health.online_users ?? null,
});
} catch {
+8 -6
View File
@@ -87,7 +87,7 @@ func TestHealthEndpointStatusOK(t *testing.T) {
}
}
func TestHealthEndpointHasVersion(t *testing.T) {
func TestHealthEndpointOmitsVersion(t *testing.T) {
router := setupRouter(t)
req := httptest.NewRequest(http.MethodGet, "/health", nil)
@@ -100,8 +100,9 @@ func TestHealthEndpointHasVersion(t *testing.T) {
t.Fatalf("JSON decode error: %v", err)
}
if body["version"] == nil || body["version"] == "" {
t.Error("health response missing 'version' field")
// C-2: Version must NOT be exposed on unauthenticated endpoints.
if _, exists := body["version"]; exists {
t.Error("health response must not contain 'version' field (prevents fingerprinting)")
}
}
@@ -136,7 +137,7 @@ func TestAPIV1InfoReturnsServerName(t *testing.T) {
}
}
func TestAPIV1InfoReturnsVersion(t *testing.T) {
func TestAPIV1InfoOmitsVersion(t *testing.T) {
router := setupRouter(t)
req := httptest.NewRequest(http.MethodGet, "/api/v1/info", nil)
@@ -149,8 +150,9 @@ func TestAPIV1InfoReturnsVersion(t *testing.T) {
t.Fatalf("JSON decode error: %v", err)
}
if body["version"] == nil {
t.Error("info response missing 'version' field")
// C-2: Version must NOT be exposed to prevent fingerprinting.
if _, exists := body["version"]; exists {
t.Error("info response must not contain 'version' field (prevents fingerprinting)")
}
}