From ba28e2a8d33c5f4fc6455c7fb5ec5dee9d4555d3 Mon Sep 17 00:00:00 2001 From: Jer Miller Date: Wed, 23 Sep 2026 09:27:55 -0600 Subject: [PATCH] build: adopt spl-rust v0.11.0, where the capability is the whole bridge admission check The private-link bridge used to forward only an eight-header allowlist (plus the route-class marker) and refuse any request that carried Authorization or a reserved header, even when it held the capability. A request holding the capability is this app's own code, so that was a policy against itself. v0.11.0 forwards every header the bridge does not reserve and strips (never refuses) the reserved ones; the exact loopback Host and the capability remain the admission check. The explicit header allowlist is removed with the library's policy knob. The test that pinned the refusal now pins what replaces it: reserved caller headers are stripped on the way through, and a request without the capability is still refused before it reaches the journal. Co-Authored-By: Claude Opus 5.5 (1M context) --- Cargo.lock | 6 +-- Cargo.toml | 4 +- crates/solstone-linux/src/private_link.rs | 43 ++++++++++---------- licensing/rust-dependency-notices.index.json | 8 ++-- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d32d2d2..8345e51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2920,7 +2920,7 @@ dependencies = [ [[package]] name = "spl-core" version = "0.1.0" -source = "git+https://github.com/solpbc/spl-rust?rev=0071355cb1ba91c6838c4c844bb20dc99aa9ce13#0071355cb1ba91c6838c4c844bb20dc99aa9ce13" +source = "git+https://github.com/solpbc/spl-rust?rev=72f6c1590a698194e689c230300d72c1b84ed9d4#72f6c1590a698194e689c230300d72c1b84ed9d4" dependencies = [ "argon2", "base64", @@ -2937,7 +2937,7 @@ dependencies = [ [[package]] name = "spl-home" version = "0.1.0" -source = "git+https://github.com/solpbc/spl-rust?rev=0071355cb1ba91c6838c4c844bb20dc99aa9ce13#0071355cb1ba91c6838c4c844bb20dc99aa9ce13" +source = "git+https://github.com/solpbc/spl-rust?rev=72f6c1590a698194e689c230300d72c1b84ed9d4#72f6c1590a698194e689c230300d72c1b84ed9d4" dependencies = [ "rustls", "spl-core", @@ -2949,7 +2949,7 @@ dependencies = [ [[package]] name = "spl-transport" version = "0.1.0" -source = "git+https://github.com/solpbc/spl-rust?rev=0071355cb1ba91c6838c4c844bb20dc99aa9ce13#0071355cb1ba91c6838c4c844bb20dc99aa9ce13" +source = "git+https://github.com/solpbc/spl-rust?rev=72f6c1590a698194e689c230300d72c1b84ed9d4#72f6c1590a698194e689c230300d72c1b84ed9d4" dependencies = [ "base64", "futures-util", diff --git a/Cargo.toml b/Cargo.toml index db338c6..c974f3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,8 @@ unimplemented = "deny" [workspace.dependencies] clap = { version = "4.5", features = ["derive"] } -spl-core = { version = "0.1.0", git = "https://github.com/solpbc/spl-rust", rev = "0071355cb1ba91c6838c4c844bb20dc99aa9ce13" } -spl-transport = { version = "0.1.0", git = "https://github.com/solpbc/spl-rust", rev = "0071355cb1ba91c6838c4c844bb20dc99aa9ce13" } +spl-core = { version = "0.1.0", git = "https://github.com/solpbc/spl-rust", rev = "72f6c1590a698194e689c230300d72c1b84ed9d4" } +spl-transport = { version = "0.1.0", git = "https://github.com/solpbc/spl-rust", rev = "72f6c1590a698194e689c230300d72c1b84ed9d4" } tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal", "time"] } tracing = "0.1" tracing-subscriber = "0.3" diff --git a/crates/solstone-linux/src/private_link.rs b/crates/solstone-linux/src/private_link.rs index 3f5bd24..1da2481 100644 --- a/crates/solstone-linux/src/private_link.rs +++ b/crates/solstone-linux/src/private_link.rs @@ -18,7 +18,7 @@ use std::{ #[cfg(test)] use reqwest::Method; use reqwest::{RequestBuilder, StatusCode, Url, multipart}; -use spl_core::bridge::{BridgeNames, RequestHead, RequestHeaderPolicy}; +use spl_core::bridge::{BridgeNames, RequestHead}; use spl_core::pairlink::{self, ParsedPairLink}; use spl_transport::credential::Credential; use spl_transport::{ @@ -2873,22 +2873,6 @@ async fn start_private_link_session_inner( stream_response: Arc::new(streams_journal_response), local_response: Arc::new(|_, _| None), attribution_headers: Arc::new(|_| Vec::new()), - request_headers: RequestHeaderPolicy::Allow( - [ - "accept", - "accept-language", - "content-type", - "cache-control", - "if-none-match", - "if-modified-since", - "range", - "user-agent", - ROUTE_CLASS_MARKER_HEADER_NAME, - ] - .into_iter() - .map(str::to_owned) - .collect(), - ), max_request_body_bytes: MAX_REQUEST_BODY_BYTES as usize, }; // This private bridge gives optional requests an actual dial source. The pinned @@ -4418,22 +4402,35 @@ pub(crate) mod tests { } #[tokio::test] - async fn caller_reserved_auth_headers_are_rejected_before_dial() { + async fn capability_is_the_admission_check_and_reserved_caller_headers_are_stripped() { let peer = PrivateLinkPeer::start().await; let (_temp, session) = start_keyless_peer_session(&peer).await; + peer.set_route("/stripped", 200, b"{}".to_vec()); + // A request holding the capability is this app's own code: reserved + // headers it sets are stripped on the way through, never refused. for (name, value) in [ (OBSERVER_HEADER_NAME, "forged"), (PROTOCOL_VERSION_HEADER_NAME, "2"), ("authorization", "Bearer forged"), ] { let response = session - .request(Method::GET, "/blocked") + .request(Method::GET, "/stripped") .unwrap() .header(name, value) .send() .await .unwrap(); - assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!(response.status(), StatusCode::OK, "{name}"); + } + let forwarded = peer.requests(); + assert_eq!(forwarded.len(), 3); + for request in &forwarded { + assert!( + !request + .headers + .iter() + .any(|(_, value)| value == "forged" || value == "Bearer forged") + ); } let bare = reqwest::Client::builder() .no_proxy() @@ -4452,7 +4449,11 @@ pub(crate) mod tests { StatusCode::FORBIDDEN ); } - assert!(peer.requests().is_empty()); + assert_eq!( + peer.requests().len(), + 3, + "a refused request never reaches the journal" + ); session.shutdown().await.unwrap(); peer.shutdown().await; } diff --git a/licensing/rust-dependency-notices.index.json b/licensing/rust-dependency-notices.index.json index 46162c5..ab1c4b1 100644 --- a/licensing/rust-dependency-notices.index.json +++ b/licensing/rust-dependency-notices.index.json @@ -1,6 +1,6 @@ { "schema": "solstone-linux.rust-dependency-notices.v1", - "cargo_lock_sha256": "3c330721dc4dfe7a9364b3d78cc238756aca066bd4f1bae0b89993c98bcc4e86", + "cargo_lock_sha256": "ffa751b0a72f81ed76489a16aaf534e83ae9e5214c865c2b3bc07b5a3113479f", "notices_sha256": "9543d6d2b62c2ccc21bf900f7a84a151c9e9e0126e9e5b5d3a96ca123fb98682", "population_query": "cargo metadata --locked --offline --format-version 1 --filter-platform x86_64-unknown-linux-gnu", "root_package": "solstone-linux", @@ -4952,7 +4952,7 @@ { "name": "spl-core", "version": "0.1.0", - "source": "git+https://github.com/solpbc/spl-rust?rev=0071355cb1ba91c6838c4c844bb20dc99aa9ce13#0071355cb1ba91c6838c4c844bb20dc99aa9ce13", + "source": "git+https://github.com/solpbc/spl-rust?rev=72f6c1590a698194e689c230300d72c1b84ed9d4#72f6c1590a698194e689c230300d72c1b84ed9d4", "license": "AGPL-3.0-only", "members": [ { @@ -4967,7 +4967,7 @@ { "name": "spl-home", "version": "0.1.0", - "source": "git+https://github.com/solpbc/spl-rust?rev=0071355cb1ba91c6838c4c844bb20dc99aa9ce13#0071355cb1ba91c6838c4c844bb20dc99aa9ce13", + "source": "git+https://github.com/solpbc/spl-rust?rev=72f6c1590a698194e689c230300d72c1b84ed9d4#72f6c1590a698194e689c230300d72c1b84ed9d4", "license": "AGPL-3.0-only", "members": [ { @@ -4982,7 +4982,7 @@ { "name": "spl-transport", "version": "0.1.0", - "source": "git+https://github.com/solpbc/spl-rust?rev=0071355cb1ba91c6838c4c844bb20dc99aa9ce13#0071355cb1ba91c6838c4c844bb20dc99aa9ce13", + "source": "git+https://github.com/solpbc/spl-rust?rev=72f6c1590a698194e689c230300d72c1b84ed9d4#72f6c1590a698194e689c230300d72c1b84ed9d4", "license": "AGPL-3.0-only", "members": [ { -- 2.51.2