mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-31 22:07:06 -06:00
#3692: review — say plainly the same-user Zotero check is Linux only; Windows and macOS rely on loopback alone
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CX1hMKX28764Lrx5moxs1o
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e251c960d9
commit
bf2a379ec6
@@ -90,7 +90,8 @@ pub struct Outcome {
|
||||
pub fn run_command(opts: &Options, command: Command, answer: &mut dyn FnMut(&Call) -> Result<Value, String>) -> Result<Outcome, ZoteroError> {
|
||||
crate::peer::check_addr(&opts.addr)?;
|
||||
let mut stream = TcpStream::connect_timeout(&opts.addr, opts.connect_timeout).map_err(|e| ZoteroError::NotRunning(e.to_string()))?;
|
||||
// Before Zotero sees anything: the command, and then the document, go only to our own user.
|
||||
// Before Zotero sees anything: the command, and then the document, go only to this computer
|
||||
// and, on Linux only, not to a listener the socket table shows belongs to another user.
|
||||
crate::peer::check(&stream)?;
|
||||
let _ = stream.set_nodelay(true);
|
||||
stream.set_read_timeout(Some(opts.idle_timeout)).map_err(|e| ZoteroError::Io(e.to_string()))?;
|
||||
|
||||
@@ -38,8 +38,8 @@ pub enum ZoteroError {
|
||||
/// Zotero sent something that isn't the protocol.
|
||||
#[error("unexpected data from Zotero: {0}")]
|
||||
Protocol(String),
|
||||
/// What answered on Zotero's port may not be Zotero (another user's program, or another
|
||||
/// computer); it gets no access to the document.
|
||||
/// What answered on Zotero's port may not be Zotero (another computer, on any system; on
|
||||
/// Linux only, another user's program); it gets no access to the document.
|
||||
#[error("WordCraft won't let this program edit the document: {0}")]
|
||||
Refused(String),
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
//! Who answered on Zotero's port. Zotero's protocol has no handshake that proves the other end
|
||||
//! is Zotero, and whatever answers gets to read and rewrite the open document. So WordCraft
|
||||
//! only connects to this computer (a loopback address), and on Linux checks that the program
|
||||
//! listening there runs as the same user, from the kernel's socket table (`/proc/net/tcp`):
|
||||
//! another account on a shared machine that took the port while Zotero wasn't running is
|
||||
//! refused. A listener the table doesn't show (WSL2 mirrored networking with Zotero on Windows,
|
||||
//! a local proxy, a sandbox) leaves only the loopback rule, as on other systems, which have no
|
||||
//! such table to read without OS calls this crate doesn't make (see `docs/zotero.md`).
|
||||
//! is Zotero, and whatever answers gets to read and rewrite the open document. Two checks:
|
||||
//!
|
||||
//! - **Every system:** WordCraft only connects to this computer (a loopback address).
|
||||
//! - **Linux only:** the program listening there must not run as another user, from the
|
||||
//! kernel's socket table (`/proc/net/tcp`): another account on a shared machine that took the
|
||||
//! port while Zotero wasn't running is refused. A listener the table doesn't show (WSL2
|
||||
//! mirrored networking with Zotero on Windows, a local proxy, a sandbox) leaves only the
|
||||
//! loopback rule.
|
||||
//!
|
||||
//! On Windows, macOS and the BSDs there is **no same-user check**: the loopback rule is the
|
||||
//! only one, so any local account's listener on the port is taken for Zotero. Those systems have
|
||||
//! no such table to read without OS calls this crate doesn't make (see `docs/zotero.md`, #3831).
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpStream};
|
||||
|
||||
@@ -49,7 +54,8 @@ fn owner_check(local: SocketAddr, peer: SocketAddr) -> Result<(), ZoteroError> {
|
||||
same_owner(&table, local, peer, me)
|
||||
}
|
||||
|
||||
/// No socket table to read here: the loopback rule alone decides.
|
||||
/// Windows, macOS, the BSDs: no same-user check (no socket table to read without OS calls this
|
||||
/// crate doesn't make, #3831); the loopback rule alone decides.
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn owner_check(_local: SocketAddr, _peer: SocketAddr) -> Result<(), ZoteroError> {
|
||||
Ok(())
|
||||
|
||||
+13
-10
@@ -64,22 +64,25 @@ Word's "Field" in the file.)
|
||||
## Who can drive the document
|
||||
|
||||
Zotero's protocol has no handshake that proves the program on port 23116 is Zotero, and that
|
||||
program can read the citations and edit the open document. WordCraft limits who that can be:
|
||||
program can read the citations and edit the open document. WordCraft limits who that can be.
|
||||
In short: **every system** gets the loopback rule; **only Linux** also checks that the listener
|
||||
isn't another user's. On Windows and macOS (and the BSDs) the loopback rule is the only check.
|
||||
|
||||
- It only connects to this computer (a loopback address such as `127.0.0.1`); any other address
|
||||
is refused before connecting.
|
||||
- On Linux it checks, in the kernel's socket table (`/proc/net/tcp`, `/proc/net/tcp6`), that the
|
||||
- Every system: it only connects to this computer (a loopback address such as `127.0.0.1`);
|
||||
any other address is refused before connecting.
|
||||
- Linux only: it checks, in the kernel's socket table (`/proc/net/tcp`, `/proc/net/tcp6`), that the
|
||||
program listening on the port runs as the same user as WordCraft, before sending anything. If
|
||||
another account on a shared machine took the port while Zotero wasn't running, WordCraft
|
||||
refuses with an error and the document isn't touched. When the table doesn't show the
|
||||
listener (WSL2 mirrored networking with Zotero running on Windows, a local proxy, a sandbox)
|
||||
or can't be read, its owner is unknown and only the loopback rule applies, as below.
|
||||
- Remaining risk: macOS, Windows and the BSDs have no such table to read without OS calls
|
||||
WordCraft doesn't make (it uses no `unsafe` code), so there only the loopback rule applies
|
||||
(and on Linux when the listener isn't in the table): on a computer shared with other
|
||||
accounts, a program another user leaves on port 23116 while Zotero is closed would be taken
|
||||
for Zotero. A program running as you is trusted on every
|
||||
system; it could change your documents anyway.
|
||||
- Windows, macOS and the BSDs: **no same-user check.** They have no such table to read
|
||||
without OS calls WordCraft doesn't make (it uses no `unsafe` code), so only the loopback rule
|
||||
applies there (and on Linux when the listener isn't in the table). Remaining risk: on a
|
||||
computer shared with other accounts, a program another user leaves on port 23116 while
|
||||
Zotero is closed would be taken for Zotero. An owner check for these systems is tracked in
|
||||
#3831. A program running as you is trusted on every system; it could change your documents
|
||||
anyway.
|
||||
- `wordcraft-cli zotero` prints Zotero's messages (and `--trace` lines) with control
|
||||
characters, line breaks and backslashes shown as escapes (`\n`, `\u{1b}`, `\\`), so a peer
|
||||
can't forge other output or drive the terminal.
|
||||
|
||||
Reference in New Issue
Block a user