Uh oh!
There was an error while loading. Please reload this page.
fix(tauri): resolve relative paths and prepare for IPC merge - #12
Conversation
- Canonicalize paths in read_file command to handle relative paths - Canonicalize paths in macOS Opened event (fixes#11) - Add debug logging at all file loading entry points - Restructure builder to allow conditional IPC state management - Add placeholders for feat/unix-socket-ipc merge compatibility - Display full path in toolbar for better debugging
Important Review skippedReview was skipped as selected files did not have any reviewable changes. 💤 Files selected but had no reviewable changes (1)
⛔ Files ignored due to path filters (5)
You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds a TCP IPC server and state to the Tauri backend; makes IPC structs/functions public; adds path canonicalization and extensive [DEBUG] logging for CLI / opened-file flows; updates frontend to show full file paths and add debug logs. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as TCP Client
participant TCP as Tcp Listener (tcp_ipc)
participant App as Tauri App / process_command
participant FS as Filesystem
participant UI as Frontend (webview)
Client->>TCP: connect, send JSON line (IpcCommand)
TCP->>App: parse JSON -> call process_command(cmd)
App->>FS: read_file / resolve path (canonicalize)
alt command produces response
FS-->>App: file contents / error
App-->>TCP: IpcResponse (JSON)
App->>UI: emit "open-file" with resolved path (if applicable)
UI-->>UI: loadFile(resolved path), update toolbar
else error
App-->>TCP: IpcResponse (error)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/tauri/src-tauri/src/lib.rs (1)
366-387: Consider adding fallback behavior for consistency.The single-instance handler silently drops the file-open request when
canonicalizefails (lines 383-385 only log, noopen-fileevent emitted). In contrast:
- CLI args handler (line 462) uses
unwrap_or_elseto fall back to the original path- macOS Opened event (line 535) uses
unwrap_orto fall backThis inconsistency could cause confusing behavior where files open in some scenarios but not others.
♻️ Suggested fix to add fallback
if !file_path.is_empty() && !file_path.starts_with('-') { - if let Ok(abs_path) = std::fs::canonicalize(file_path) {- let path_str = abs_path.to_string_lossy().to_string();- eprintln!("[DEBUG] Emitting open-file with: {:?}", path_str);- let _ = app.emit("open-file", &path_str);- } else {- eprintln!("[DEBUG] Failed to canonicalize path: {:?}", file_path);- }+ let abs_path = std::fs::canonicalize(file_path).unwrap_or_else(|e| {+ eprintln!("[DEBUG] Canonicalize failed ({}), using as-is", e);+ PathBuf::from(file_path)+ });+ let path_str = abs_path.to_string_lossy().to_string();+ eprintln!("[DEBUG] Emitting open-file with: {:?}", path_str);+ let _ = app.emit("open-file", &path_str); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/tauri/src-tauri/src/lib.rs` around lines 366 - 387, The single-instance handler in the tauri_plugin_single_instance::init closure drops the open-file event when std::fs::canonicalize(file_path) fails; change the logic so that on canonicalize failure you fall back to the original file_path (or its to_string_lossy) and still call app.emit("open-file", &path_str) so behavior matches the CLI and macOS handlers; update the block handling args[1] to compute a path_str variable from canonicalize if Ok or else from the original file_path, log the fallback, and always emit "open-file" with that path_str.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/tauri/src-tauri/src/lib.rs`:
- Around line 366-387: The single-instance handler in the
tauri_plugin_single_instance::init closure drops the open-file event when
std::fs::canonicalize(file_path) fails; change the logic so that on canonicalize
failure you fall back to the original file_path (or its to_string_lossy) and
still call app.emit("open-file", &path_str) so behavior matches the CLI and
macOS handlers; update the block handling args[1] to compute a path_str variable
from canonicalize if Ok or else from the original file_path, log the fallback,
and always emit "open-file" with that path_str.
wilcorrea
commented
Feb 23, 2026
Addressed the CodeRabbit nitpick on the single-instance handler canonicalize fallback. lib.rs lines 378-385: Changed from Fixed in 53dbe11. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/tauri/src-tauri/src/lib.rs (1)
66-66: Consider structured logging for production.The
[DEBUG]prefix ineprintln!calls works for development, but for production builds you might want to use thelogortracingcrate with configurable log levels. This would allow filtering debug output without code changes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/tauri/src-tauri/src/lib.rs` at line 66, The debug print in read_file currently uses eprintln!("[DEBUG] ...", path); switch this to a structured logging call (e.g. log::debug! or tracing::debug!) so debug output can be filtered at runtime, and ensure the application initializes the chosen logger/subscriber (e.g. env_logger::init() or tracing_subscriber) in main/startup; replace the eprintln! invocation inside read_file with the appropriate debug macro and remove the hardcoded "[DEBUG]" prefix so the logging framework controls level and formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/tauri/src-tauri/src/lib.rs`:
- Around line 533-537: The opened-event handler uses
std::fs::canonicalize(&path).unwrap_or(path) which silently falls back; change
it to use unwrap_or_else so you can log the canonicalize error (matching
single-instance and CLI handlers). In the opened event block, replace
unwrap_or(path) with unwrap_or_else(|err| { eprintln!("[DEBUG] canonicalize
failed: {:?}", err); path.clone() }) (ensure abs_path and path_str keep the same
names) so failures are logged consistently.
---
Nitpick comments:
In `@apps/tauri/src-tauri/src/lib.rs`:
- Line 66: The debug print in read_file currently uses eprintln!("[DEBUG] ...",
path); switch this to a structured logging call (e.g. log::debug! or
tracing::debug!) so debug output can be filtered at runtime, and ensure the
application initializes the chosen logger/subscriber (e.g. env_logger::init() or
tracing_subscriber) in main/startup; replace the eprintln! invocation inside
read_file with the appropriate debug macro and remove the hardcoded "[DEBUG]"
prefix so the logging framework controls level and formatting.
Uh oh!
There was an error while loading. Please reload this page.
…onicalize Align the macOS Opened event handler with the single-instance and CLI handlers by replacing silent unwrap_or fallback with unwrap_or_else that logs the canonicalize failure before falling back.
wilcorrea
commented
Feb 23, 2026
Addressed all remaining CodeRabbit review comments from both review rounds: 1. Inconsistent canonicalization fallback (Opened event, line 535) -- Fixed in 6fb6ca2. Replaced 2. Single-instance handler dropping file-open on canonicalize failure (lines 366-387) -- Already addressed in 53dbe11 (previous commit). 3. Structured logging suggestion (line 66) -- Acknowledged as valid but deferred. Switching from All three canonicalization entry points now use the same |
- Add tcp_ipc.rs module with TCP listener on 127.0.0.1:7474 - Refactor ipc.rs to make IpcCommand/Response and process_command public - TCP socket works in parallel with Unix socket for compatibility - Supports same JSON protocol: open, ping, show commands - Fixes Unix socket limitation with Docker/Podman containers - Both sockets share same command processing logic Resolves container accessibility issue where Unix sockets from macOS are not accessible within Linux containers due to different kernel stacks.
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
apps/tauri/src-tauri/src/tcp_ipc.rs (1)
45-62: No connection limits or rate-limiting on the accept loop.Any local process can open unlimited connections to this TCP server. Without a maximum connection count or per-connection timeout, a misbehaving local process could exhaust file descriptors or memory by opening many connections and sending data slowly.
Consider adding:
- A maximum concurrent connection semaphore (
tokio::sync::Semaphore)- A read timeout per connection (
tokio::time::timeout)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/tauri/src-tauri/src/tcp_ipc.rs` around lines 45 - 62, The accept loop in tcp_listener_loop currently allows unlimited concurrent connections; add a tokio::sync::Semaphore to limit max concurrent clients (acquire a permit before spawning handle_client and release when that client's task finishes) and wrap per-connection read/write operations inside tokio::time::timeout in handle_client to enforce a per-connection inactivity/read timeout; ensure tcp_listener_loop attempts to accept new connections even when semaphore is exhausted (e.g., reject/close the stream immediately with a log) and propagate/handle timeout errors from handle_client so they are logged.apps/tauri/src-tauri/src/lib.rs (1)
529-533: TCP cleanup only clears state, doesn't stop the listener (seetcp_ipc.rscomment).As noted in the
tcp_ipc.rsreview,cleanup()only nulls out the stored address. The accept loop and active connections continue until the process exits. For theExitRequested+ExplicitQuitpath this is likely fine since the process terminates shortly after, but it's worth noting the asymmetry with the Unix socket cleanup which actually removes the socket file.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/tauri/src-tauri/src/lib.rs` around lines 529 - 533, The current call to tcp_ipc::cleanup(tcp_socket_state) only clears the stored address but does not stop the accept loop or close active connections; update the shutdown so the TCP listener and any active connections are closed and the accept loop signalled to exit. Either call a dedicated function (e.g., tcp_ipc::stop_listener or tcp_ipc::shutdown_listener) from this location, or extend tcp_ipc::cleanup to close the listener socket stored in tcp_ipc::TcpSocketState, close active connections and signal/join the accept loop so it terminates cleanly rather than leaving it running until process exit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/tauri/src-tauri/src/lib.rs`:
- Around line 416-419: The tcp_ipc::setup() function currently masks bind
failures by always returning Ok(()), so the error branch here
(tcp_ipc::setup(app)) is effectively unreachable; update tcp_ipc::setup in
tcp_ipc.rs to propagate and return an Err when socket bind/listen fails (return
a Result with the real io::Error or a boxed error), and keep this call site
as-is so the Err path (eprintln!("Failed to setup TCP IPC: {}", e)) is reachable
and logs the actual failure; ensure the tcp_ipc::setup signature and any callers
are updated to handle the Result change.
- Line 15: The tcp_ipc module is currently compiled unconditionally, exposing an
unauthenticated TCP listener (127.0.0.1:7474); gate this to be opt-in and add
simple auth: protect the module with a cfg feature (e.g., #[cfg(feature =
"tcp-ipc")] on the mod tcp_ipc declaration and corresponding Cargo.toml feature)
and/or add token-based auth inside the tcp_ipc listener (generate a random token
stored in a file with 0o600 perms and require clients to present it when
connecting, validating in the tcp listener accept/handle path); update any
public APIs that assume tcp_ipc exists to compile without it (use
#[cfg(any(...))] or conditional stubs) and document the feature and token file
location so consumers can opt in securely.
In `@apps/tauri/src-tauri/src/tcp_ipc.rs`:
- Around line 12-35: The setup() function currently spawns an async task and
returns Ok(()) before TcpListener::bind runs, so binding failures are never
propagated; fix by performing the bind synchronously (await the bind) or by
using a oneshot channel to relay the bind result back to the caller before
returning. Concretely, call TcpListener::bind(&addr).await (or bind on the sync
thread) and handle Err by returning Err(String) from setup(), or if you must
spawn first, create a tokio::sync::oneshot sender/receiver, have the spawned
task send Ok(()) or Err(e.to_string()) after attempting TcpListener::bind, and
have setup() block/await on the receiver and return the corresponding Result;
update references in this flow around setup, TcpListener::bind,
tcp_listener_loop, and the app_handle state assignment so errors are returned to
the caller instead of always returning Ok(()).
- Around line 37-43: cleanup() only removes the stored address string and does
not stop the spawned accept loop or active connections; update TcpSocketState to
hold a cancellation mechanism (e.g., a tokio::sync::watch,
tokio_util::sync::CancellationToken, or the JoinHandle of the spawned task) and
modify the accept loop to listen for that cancellation token or be abortable,
then in cleanup() signal cancellation or abort the JoinHandle and await/cleanup
resources before dropping the address; reference TcpSocketState, cleanup(), the
spawned accept loop (the task that owns the TcpListener) and whichever chosen
token/handle so you can set and trigger it from cleanup().
- Around line 64-94: The handle_client function currently logs raw,
attacker-controlled TCP input and command strings (the `line` contents and
`cmd.command`) which can contain newlines and inject fake log entries; sanitize
or escape/newline-normalize and/or truncate these values before logging (e.g.,
replace newlines with escaped sequences and limit length) when using the
eprintln! calls and when logging inside the match branch that processes
`process_command`. Also replace the use of
serde_json::to_string(&response).unwrap_or_default() by detecting serialization
errors and returning a definite error payload (a hardcoded JSON error string) to
the client if serialization fails, so you never send an empty line; reference
`handle_client`, `process_command`, `IpcCommand`, and `IpcResponse` when making
these changes.
- Around line 7-8: The current setup() spawns the listener asynchronously and
always returns Ok(()), and the hardcoded DEFAULT_PORT = 7474 can silently
conflict with Neo4j; change setup() to synchronously bind with
TcpListener::bind((DEFAULT_HOST, port)) (propagating any bind Err back to the
caller) before spawning the listener loop, and make the port configurable (read
from an env var or config with DEFAULT_PORT as fallback) so callers see bind
failures immediately and the port can be changed without code edits.
---
Nitpick comments:
In `@apps/tauri/src-tauri/src/lib.rs`:
- Around line 529-533: The current call to tcp_ipc::cleanup(tcp_socket_state)
only clears the stored address but does not stop the accept loop or close active
connections; update the shutdown so the TCP listener and any active connections
are closed and the accept loop signalled to exit. Either call a dedicated
function (e.g., tcp_ipc::stop_listener or tcp_ipc::shutdown_listener) from this
location, or extend tcp_ipc::cleanup to close the listener socket stored in
tcp_ipc::TcpSocketState, close active connections and signal/join the accept
loop so it terminates cleanly rather than leaving it running until process exit.
In `@apps/tauri/src-tauri/src/tcp_ipc.rs`:
- Around line 45-62: The accept loop in tcp_listener_loop currently allows
unlimited concurrent connections; add a tokio::sync::Semaphore to limit max
concurrent clients (acquire a permit before spawning handle_client and release
when that client's task finishes) and wrap per-connection read/write operations
inside tokio::time::timeout in handle_client to enforce a per-connection
inactivity/read timeout; ensure tcp_listener_loop attempts to accept new
connections even when semaphore is exhausted (e.g., reject/close the stream
immediately with a log) and propagate/handle timeout errors from handle_client
so they are logged.
| mod cli_installer; | ||
| #[cfg(unix)] | ||
| mod ipc; | ||
| mod tcp_ipc; |
There was a problem hiding this comment.
TCP IPC module is unconditionally compiled — security implications of an unauthenticated local TCP listener.
Unlike the Unix socket (gated behind #[cfg(unix)] with 0o600 permissions), the TCP listener on 127.0.0.1:7474 has no authentication or authorization. Any local process (including those in containers, sandboxes, or running as different users) can connect and issue commands like open (to open arbitrary files the app user can read) or show (to manipulate window focus).
On multi-user systems or shared hosts this is a local attack surface. Consider adding at minimum:
- A shared secret / token-based auth (e.g., random token written to a file with restrictive permissions, client must present it)
- Or restricting TCP IPC behind a feature flag / cfg gate so it's opt-in
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/tauri/src-tauri/src/lib.rs` at line 15, The tcp_ipc module is currently
compiled unconditionally, exposing an unauthenticated TCP listener
(127.0.0.1:7474); gate this to be opt-in and add simple auth: protect the module
with a cfg feature (e.g., #[cfg(feature = "tcp-ipc")] on the mod tcp_ipc
declaration and corresponding Cargo.toml feature) and/or add token-based auth
inside the tcp_ipc listener (generate a random token stored in a file with 0o600
perms and require clients to present it when connecting, validating in the tcp
listener accept/handle path); update any public APIs that assume tcp_ipc exists
to compile without it (use #[cfg(any(...))] or conditional stubs) and document
the feature and token file location so consumers can opt in securely.
| // TCP IPC setup (works on all platforms, including from containers) | ||
| if let Err(e) = tcp_ipc::setup(app) { | ||
| eprintln!("Failed to setup TCP IPC: {}", e); | ||
| } |
There was a problem hiding this comment.
TCP IPC setup error is logged but doesn't prevent app startup — acceptable.
This mirrors the Unix socket setup pattern. Since the TCP IPC is supplementary, failing gracefully is reasonable. However, note that as discussed in tcp_ipc.rs, setup() currently always returns Ok(()) even on bind failure, making this error branch unreachable for the most important failure case.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/tauri/src-tauri/src/lib.rs` around lines 416 - 419, The tcp_ipc::setup()
function currently masks bind failures by always returning Ok(()), so the error
branch here (tcp_ipc::setup(app)) is effectively unreachable; update
tcp_ipc::setup in tcp_ipc.rs to propagate and return an Err when socket
bind/listen fails (return a Result with the real io::Error or a boxed error),
and keep this call site as-is so the Err path (eprintln!("Failed to setup TCP
IPC: {}", e)) is reachable and logs the actual failure; ensure the
tcp_ipc::setup signature and any callers are updated to handle the Result
change.
| const DEFAULT_HOST: &str = "127.0.0.1"; | ||
| const DEFAULT_PORT: u16 = 7474; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Neo4j default port database
💡 Result:
Neo4j’s default ports depend on how you connect:
- Bolt (drivers /
neo4j:///bolt://): 7687 [1][3] - HTTP (Neo4j Browser / HTTP API): 7474 [1][3]
- HTTPS: 7473 [1][3]
(Other common defaults include Backup: 6362.) [1]
🏁 Script executed:
# First, let's find and examine the tcp_ipc.rs file
find . -name "tcp_ipc.rs" -type f | head -5Repository: devitools/arandu
Length of output: 97
🏁 Script executed:
cat -n ./apps/tauri/src-tauri/src/tcp_ipc.rsRepository: devitools/arandu
Length of output: 3886
🏁 Script executed:
# Now let's check lib.rs to see how setup() is called and if it handles the result
find . -name "lib.rs" -path "*/src-tauri/src/lib.rs" -type fRepository: devitools/arandu
Length of output: 93
🏁 Script executed:
cat -n ./apps/tauri/src-tauri/src/lib.rsRepository: devitools/arandu
Length of output: 23262
Hardcoded port 7474 conflicts with Neo4j and fails silently.
Port 7474 is Neo4j's default HTTP port. The TCP bind happens asynchronously inside a spawned task (lines 17–30), but setup() returns Ok(()) immediately (line 34) before the bind attempt completes. If another process holds the port, the error is only logged to stderr; the caller in lib.rs (lines 417–419) cannot distinguish success from failure since setup() always returns Ok(()).
Consider either:
- Synchronously binding before spawning the listener loop, or
- Using a oneshot channel to propagate bind errors back to the caller, or
- Making the port configurable (via config file or environment variable) with a fallback port range.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/tauri/src-tauri/src/tcp_ipc.rs` around lines 7 - 8, The current setup()
spawns the listener asynchronously and always returns Ok(()), and the hardcoded
DEFAULT_PORT = 7474 can silently conflict with Neo4j; change setup() to
synchronously bind with TcpListener::bind((DEFAULT_HOST, port)) (propagating any
bind Err back to the caller) before spawning the listener loop, and make the
port configurable (read from an env var or config with DEFAULT_PORT as fallback)
so callers see bind failures immediately and the port can be changed without
code edits.
| pub fn setup(app: &tauri::App) -> Result<(), String> { | ||
| let addr = format!("{}:{}", DEFAULT_HOST, DEFAULT_PORT); | ||
| let app_handle = app.handle().clone(); | ||
| tauri::async_runtime::spawn(async move { | ||
| match TcpListener::bind(&addr).await { | ||
| Ok(listener) => { | ||
| eprintln!("[TCP IPC] Listening on {}", addr); | ||
| let state = app_handle.state::<TcpSocketState>(); | ||
| if let Ok(mut guard) = state.0.lock() { | ||
| *guard = Some(addr.clone()); | ||
| } | ||
| tcp_listener_loop(listener, app_handle).await; | ||
| } | ||
| Err(e) => { | ||
| eprintln!("[TCP IPC] Failed to bind to {}: {}", addr, e); | ||
| } | ||
| } | ||
| }); | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
setup() always returns Ok(()) regardless of bind outcome.
Because TcpListener::bind happens inside the spawned async task, setup() returns Ok(()) before the bind attempt completes. The caller in lib.rs (line 417) checks for Err but will never see one from a bind failure. This makes the error handling at the call site dead code for the most critical failure mode.
Compare with the Unix socket ipc::setup() which also has this pattern — but at minimum, consider a oneshot channel to relay the bind result, or bind synchronously before spawning the accept loop.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/tauri/src-tauri/src/tcp_ipc.rs` around lines 12 - 35, The setup()
function currently spawns an async task and returns Ok(()) before
TcpListener::bind runs, so binding failures are never propagated; fix by
performing the bind synchronously (await the bind) or by using a oneshot channel
to relay the bind result back to the caller before returning. Concretely, call
TcpListener::bind(&addr).await (or bind on the sync thread) and handle Err by
returning Err(String) from setup(), or if you must spawn first, create a
tokio::sync::oneshot sender/receiver, have the spawned task send Ok(()) or
Err(e.to_string()) after attempting TcpListener::bind, and have setup()
block/await on the receiver and return the corresponding Result; update
references in this flow around setup, TcpListener::bind, tcp_listener_loop, and
the app_handle state assignment so errors are returned to the caller instead of
always returning Ok(()).
| pub fn cleanup(state: tauri::State<TcpSocketState>) { | ||
| if let Ok(mut guard) = state.0.lock() { | ||
| if let Some(addr) = guard.take() { | ||
| eprintln!("[TCP IPC] Shutting down listener on {}", addr); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
cleanup() does not actually stop the TCP listener.
cleanup() only clears the stored address string from state. The TcpListener and its accept loop (and any active client connections) continue running because the listener is owned by the spawned task, not by TcpSocketState. Unlike the Unix socket ipc::cleanup() which removes the socket file (preventing new connections), this cleanup is purely cosmetic.
To actually shut down, you'd need a cancellation mechanism (e.g., a tokio::sync::watch or CancellationToken) that the accept loop checks, or store a JoinHandle to abort the task.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/tauri/src-tauri/src/tcp_ipc.rs` around lines 37 - 43, cleanup() only
removes the stored address string and does not stop the spawned accept loop or
active connections; update TcpSocketState to hold a cancellation mechanism
(e.g., a tokio::sync::watch, tokio_util::sync::CancellationToken, or the
JoinHandle of the spawned task) and modify the accept loop to listen for that
cancellation token or be abortable, then in cleanup() signal cancellation or
abort the JoinHandle and await/cleanup resources before dropping the address;
reference TcpSocketState, cleanup(), the spawned accept loop (the task that owns
the TcpListener) and whichever chosen token/handle so you can set and trigger it
from cleanup().
| async fn handle_client(stream: TcpStream, app: tauri::AppHandle) -> Result<(), String> { | ||
| let peer_addr = stream.peer_addr().map_err(|e| e.to_string())?; | ||
| let (reader, mut writer) = stream.into_split(); | ||
| let reader = BufReader::new(reader); | ||
| let mut lines = reader.lines(); | ||
| while let Some(line) = lines.next_line().await.map_err(|e| e.to_string())? { | ||
| eprintln!("[TCP IPC] Received from {}: {}", peer_addr, line); | ||
| let response = match serde_json::from_str::<IpcCommand>(&line) { | ||
| Ok(cmd) => { | ||
| eprintln!("[TCP IPC] Processing command: {}", cmd.command); | ||
| process_command(cmd, &app) | ||
| } | ||
| Err(e) => IpcResponse { | ||
| success: false, | ||
| error: Some(format!("Invalid JSON: {}", e)), | ||
| }, | ||
| }; | ||
| let json = serde_json::to_string(&response).unwrap_or_default(); | ||
| eprintln!("[TCP IPC] Sending response to {}: {}", peer_addr, json); | ||
| writer | ||
| .write_all(format!("{}\n", json).as_bytes()) | ||
| .await | ||
| .map_err(|e| e.to_string())?; | ||
| } | ||
| eprintln!("[TCP IPC] Connection closed: {}", peer_addr); | ||
| Ok(()) |
There was a problem hiding this comment.
Potential log injection via unsanitized TCP input.
Line 71 logs the raw line received from the TCP socket, and line 75 logs cmd.command which is attacker-controlled input from any local process. While eprintln! is less exploitable than structured logging frameworks, newlines within the JSON string could forge log entries. Consider sanitizing or escaping logged values, or at minimum truncating them.
Also, Line 84: serde_json::to_string(&response).unwrap_or_default() will send an empty line "\n" to the client if serialization fails. Since IpcResponse is a simple struct this is unlikely, but sending an empty line could confuse a client expecting valid JSON. Consider sending a hardcoded error JSON instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/tauri/src-tauri/src/tcp_ipc.rs` around lines 64 - 94, The handle_client
function currently logs raw, attacker-controlled TCP input and command strings
(the `line` contents and `cmd.command`) which can contain newlines and inject
fake log entries; sanitize or escape/newline-normalize and/or truncate these
values before logging (e.g., replace newlines with escaped sequences and limit
length) when using the eprintln! calls and when logging inside the match branch
that processes `process_command`. Also replace the use of
serde_json::to_string(&response).unwrap_or_default() by detecting serialization
errors and returning a definite error payload (a hardcoded JSON error string) to
the client if serialization fails, so you never send an empty line; reference
`handle_client`, `process_command`, `IpcCommand`, and `IpcResponse` when making
these changes.
The app icon had a solid background that extended to the edges, preventing macOS from properly applying its automatic corner rounding. This resulted in a square icon appearance instead of the expected rounded corners. Changes: - Applied ~12% corner radius to all icon assets (matching macOS guidelines) - Regenerated icon.icns with proper alpha channel masks - Updated all PNG sizes (32x32, 64x64, 128x128, 256x256, 512x512, 1024x1024) The icon now displays with proper rounded corners in Dock, Finder, and app switcher.
Summary
Fixes#11 - Relative paths lead to a blank screen
read_filecommand to correctly resolve relative paths before reading filesOpenedevent to canonicalize paths when opening files via file associations (critical bug fix)feat/unix-socket-ipcbranch to prevent future conflictsTechnical Details
The root cause was that relative paths (e.g.,
./README.md) were not being canonicalized at all entry points:When a relative path reached
read_file, it would try to read relative to the process working directory (not the file's directory), causing file not found errors and blank screens.Merge Compatibility
This PR restructures the builder to match
feat/unix-socket-ipcbranch structure:MERGE_GUIDE.mdwith complete merge instructionsThis ensures clean merges between branches with zero conflicts.
Test plan
cd apps/tauri && make build-dev && make installcd ~/Documents && arandu ./README.mdarandu /full/path/to/file.mdcargo check --manifest-path src-tauri/Cargo.toml🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements