use crate::caller_identity::CallerIdentity;
use std::path::Path;
const SUMMARY_LIMIT: usize = 200;
const TARGET_LIMIT: usize = 64;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PrivilegedOperation<'a> {
Restart { module_id: &'a str },
Swap { module_id: &'a str },
Reload { module_id: &'a str },
Rescan,
SetEnabled { module_id: &'a str, enabled: bool },
ReleaseReserved { module_id: &'a str },
}
impl PrivilegedOperation<'_> {
fn description(self) -> String {
let (verb, target) = match self {
Self::Restart { module_id } => ("Restart module", module_id),
Self::Swap { module_id } => ("Swap module", module_id),
Self::Reload { module_id } => ("Reload module", module_id),
Self::SetEnabled { module_id, enabled } => (
if enabled {
"Start module"
} else {
"Stop module"
},
module_id,
),
Self::ReleaseReserved { module_id } => ("Release reserved module id", module_id),
Self::Rescan => {
return "Apply the daemon config file as it reads when approved (rescan)".into();
}
};
format!("{verb} {}", truncate_target(&sanitize(target)))
}
}
pub fn prompt_text(operation: PrivilegedOperation<'_>, caller: &CallerIdentity) -> String {
match caller.pid().zip(caller.executable()) {
Some((pid, path)) => resolved_prompt_text(operation, pid, path),
None => format!(
"{} — requested by an unidentified local process",
operation.description()
),
}
}
fn resolved_prompt_text(operation: PrivilegedOperation<'_>, pid: u32, path: &Path) -> String {
let prefix = format!("{} — requested by pid {pid} (", operation.description());
let path_budget = SUMMARY_LIMIT - prefix.chars().count() - 1;
let path = shorten_path(&sanitize(&path.to_string_lossy()), path_budget);
format!("{prefix}{path})")
}
fn sanitize(text: &str) -> String {
text.chars()
.map(|c| if forbidden(c) { '?' } else { c })
.collect()
}
fn forbidden(c: char) -> bool {
c.is_control()
|| matches!(c,
'\u{00ad}' | '\u{034f}' | '\u{061c}' | '\u{115f}' | '\u{1160}' |
'\u{17b4}' | '\u{17b5}' | '\u{180b}'..='\u{180f}' |
'\u{200b}'..='\u{200f}' | '\u{2028}'..='\u{202e}' |
'\u{2060}'..='\u{206f}' | '\u{3164}' | '\u{fe00}'..='\u{fe0f}' |
'\u{feff}' | '\u{ffa0}' | '\u{fff9}'..='\u{fffb}' |
'\u{1bca0}'..='\u{1bca3}' | '\u{1d173}'..='\u{1d17a}' |
'\u{e0000}'..='\u{e0fff}')
}
fn truncate_target(target: &str) -> String {
if target.chars().count() <= TARGET_LIMIT {
target.to_owned()
} else {
target.chars().take(TARGET_LIMIT - 1).chain(['…']).collect()
}
}
fn shorten_path(path: &str, budget: usize) -> String {
let count = path.chars().count();
if count <= budget {
return path.to_owned();
}
let component = path.rsplit(['/', '\\']).next().unwrap_or(path);
if component.chars().count() < budget {
std::iter::once('…')
.chain(path.chars().skip(count - (budget - 1)))
.collect()
} else {
std::iter::once('…')
.chain(component.chars().take(budget - 2))
.chain(['…'])
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn resolved(operation: PrivilegedOperation<'_>) -> String {
resolved_prompt_text(operation, 4121, Path::new("/bin/node"))
}
#[test]
fn operation_and_target_precede_the_resolved_caller() {
let cases = [
(
PrivilegedOperation::Restart { module_id: "aft" },
"Restart module aft",
),
(
PrivilegedOperation::Swap { module_id: "aft" },
"Swap module aft",
),
(
PrivilegedOperation::Reload { module_id: "aft" },
"Reload module aft",
),
(
PrivilegedOperation::SetEnabled {
module_id: "aft",
enabled: true,
},
"Start module aft",
),
(
PrivilegedOperation::SetEnabled {
module_id: "aft",
enabled: false,
},
"Stop module aft",
),
(
PrivilegedOperation::ReleaseReserved { module_id: "aft" },
"Release reserved module id aft",
),
];
for (operation, description) in cases {
assert_eq!(
resolved(operation),
format!("{description} — requested by pid 4121 (/bin/node)")
);
}
}
#[test]
fn rescan_covers_the_config_read_after_approval_without_a_module_count() {
assert_eq!(
resolved(PrivilegedOperation::Rescan),
"Apply the daemon config file as it reads when approved (rescan) — requested by pid 4121 (/bin/node)"
);
}
#[test]
fn failed_lookup_names_an_unidentified_local_process() {
assert_eq!(
prompt_text(
PrivilegedOperation::Restart { module_id: "aft" },
&CallerIdentity::default()
),
"Restart module aft — requested by an unidentified local process"
);
}
#[cfg(any(target_os = "macos", target_os = "linux", windows))]
#[tokio::test]
async fn resolved_connection_identity_flows_into_the_public_prompt_builder() {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let _client = tokio::net::TcpStream::connect(listener.local_addr().unwrap())
.await
.unwrap();
let (accepted, _) = listener.accept().await.unwrap();
let caller = CallerIdentity::lookup(&accepted);
let summary = prompt_text(PrivilegedOperation::Restart { module_id: "aft" }, &caller);
assert!(summary.starts_with(&format!(
"Restart module aft — requested by pid {} (",
std::process::id()
)));
let executable = std::env::current_exe().unwrap();
let filename = executable.file_name().unwrap().to_string_lossy();
assert!(summary.ends_with(&format!("{filename})")));
}
#[test]
fn target_limits_count_scalars_and_mark_only_a_cut_target() {
for count in [63, 64, 65, 300] {
let target = "界".repeat(count);
let expected = if count <= 64 {
target.clone()
} else {
format!("{}…", "界".repeat(63))
};
assert_eq!(
resolved(PrivilegedOperation::Restart { module_id: &target }),
format!("Restart module {expected} — requested by pid 4121 (/bin/node)")
);
}
}
#[test]
fn controls_zero_width_and_bidi_are_replaced_in_target_and_path() {
let bad: String = (0..=0x9f)
.filter_map(char::from_u32)
.filter(|c| c.is_control())
.chain(['\u{00ad}', '\u{034f}', '\u{061c}', '\u{180e}', '\u{feff}'])
.chain(('\u{200b}'..='\u{200f}').chain('\u{2028}'..='\u{202e}'))
.chain('\u{2060}'..='\u{206f}')
.collect();
for c in bad.chars() {
let target = format!("a{c}ft");
let path = format!("/bin/n{c}de");
assert_eq!(
resolved_prompt_text(
PrivilegedOperation::Restart { module_id: &target },
4121,
Path::new(&path)
),
"Restart module a?ft — requested by pid 4121 (/bin/n?de)"
);
}
}
#[test]
fn path_shortening_keeps_the_final_component_for_unix_and_windows_paths() {
assert_eq!(shorten_path("/bin/node", 9), "/bin/node");
assert_eq!(shorten_path("/long/directory/bin/node", 10), "…/bin/node");
assert_eq!(
shorten_path(r"C:\long\directory\bin\node.exe", 14),
r"…\bin\node.exe"
);
assert_eq!(
shorten_path("/long/path/界界界界界界界界界界", 10),
"…界界界界界界界界…"
);
assert_eq!(shorten_path("/path/abcdefghij", 10), "…abcdefgh…");
}
fn oversized_facts() -> (String, String) {
let target = format!("{}\u{202e}{}", "界".repeat(31), "界".repeat(268));
let path = format!("/{}\u{200b}/bin/node", "路".repeat(489));
assert_eq!(target.chars().count(), 300);
assert_eq!(path.chars().count(), 500);
(target, path)
}
#[test]
fn oversized_unicode_facts_fit_without_losing_operation_or_pid() {
let (target, path) = oversized_facts();
let summary = resolved_prompt_text(
PrivilegedOperation::Restart { module_id: &target },
4121,
Path::new(&path),
);
assert_eq!(summary.chars().count(), 200);
assert!(summary.starts_with(&format!(
"Restart module {}?{}… — requested by pid 4121 (…",
"界".repeat(31),
"界".repeat(31)
)));
assert!(summary.ends_with("/bin/node)"));
assert!(!summary.chars().any(forbidden));
let long_filename = format!("/{}", "路".repeat(499));
for operation in [
PrivilegedOperation::ReleaseReserved { module_id: &target },
PrivilegedOperation::Rescan,
] {
let summary = resolved_prompt_text(operation, u32::MAX, Path::new(&long_filename));
assert_eq!(summary.chars().count(), 200);
assert!(summary.contains(" — requested by pid 4294967295 (…"));
assert!(summary.ends_with("…)"));
}
}
#[tokio::test]
async fn oversized_unicode_summary_passes_operator_confirm_admission_rule() {
use crate::{
forwarding::{ChannelFlow, ModuleEndpointId, RouteBinding},
operator_confirm::{
OperatorConfirms, OperatorLimits, OperatorProvider, OperatorWithdraw,
ProviderResult,
},
router::{FrameSink, RouteCtx},
ConnectionId, Frame,
};
use std::sync::Arc;
use subc_protocol::{
error_codes, session::OperatorConfirmRequest, Flags, FrameType, Principal, Priority,
PROTOCOL_VERSION,
};
struct UnavailableProvider;
impl OperatorProvider for UnavailableProvider {
fn prompt(
&self,
_: &str,
_: Box<dyn FnOnce(Arc<dyn OperatorWithdraw>) + Send>,
) -> ProviderResult {
ProviderResult::Unavailable
}
}
let confirms = Arc::new(OperatorConfirms::default());
confirms.configure(Arc::new(UnavailableProvider), OperatorLimits::default());
let (tx, mut rx) = tokio::sync::mpsc::channel(4);
let ctx = RouteCtx {
connection_id: ConnectionId::new(1),
egress: FrameSink::new(tx),
};
let binding = RouteBinding {
client_connection_id: ConnectionId::new(2),
client_sink: ctx.egress.clone(),
client_negotiated_ver: PROTOCOL_VERSION,
client_channel: 1,
client_epoch: 1,
module_id: "fixture".into(),
module_endpoint: ModuleEndpointId {
connection_id: ctx.connection_id,
generation: 1,
},
module_sink: ctx.egress.clone(),
module_negotiated_ver: PROTOCOL_VERSION,
module_channel: 1,
module_epoch: 1,
principal: Principal::Direct,
project_root: None,
bound_at: tokio::time::Instant::now(),
flow: Arc::new(ChannelFlow::new(64 * 1024)),
scope: None,
};
let frame = Frame::build(
FrameType::Request,
Flags::new(false, Priority::Passive, false),
0,
0,
1,
b"{}".to_vec(),
)
.unwrap();
for invalid in ["界".repeat(201), "hidden\u{202e}text".into()] {
let refusal = confirms.admit(
&ctx,
frame.clone(),
"fixture".into(),
true,
OperatorConfirmRequest::new(invalid, 1, 1),
Some(&binding),
);
assert_eq!(refusal.len(), 1);
let body: serde_json::Value = serde_json::from_slice(&refusal[0].body).unwrap();
assert_eq!(body["code"], error_codes::OPERATOR_SUMMARY_INVALID);
}
let (target, path) = oversized_facts();
let summary = resolved_prompt_text(
PrivilegedOperation::Restart { module_id: &target },
4121,
Path::new(&path),
);
assert!(confirms
.admit(
&ctx,
frame,
"fixture".into(),
true,
OperatorConfirmRequest::new(summary, 1, 1),
Some(&binding),
)
.is_empty());
let outcome = tokio::time::timeout(std::time::Duration::from_secs(4), rx.recv())
.await
.unwrap()
.unwrap();
let body: serde_json::Value = serde_json::from_slice(&outcome.frame.body).unwrap();
assert_eq!(body["code"], error_codes::OPERATOR_PRESENCE_UNAVAILABLE);
assert_eq!(body["detail"]["reason"], "provider_error");
}
}