use act_policy::consent::ConsentAsk;
use tokio::sync::{mpsc, oneshot};
use crate::audit::render::escape_audit_field;
pub struct ConsentRequest {
pub message: String,
pub reply: oneshot::Sender<bool>,
}
pub type ConsentSink = mpsc::Sender<ConsentRequest>;
#[derive(Default)]
pub struct CurrentConsentSink {
inner: std::sync::Mutex<Option<ConsentSink>>,
}
impl CurrentConsentSink {
pub fn new() -> Self {
Self::default()
}
pub fn set(&self, sink: Option<ConsentSink>) {
*self.inner.lock().unwrap() = sink;
}
pub fn get(&self) -> Option<ConsentSink> {
self.inner.lock().unwrap().clone()
}
}
pub fn consent_line(ask: &ConsentAsk) -> String {
format!(
"ACT consent: {} — {} ({})",
escape_audit_field(&ask.cap_id),
escape_audit_field(&ask.summary),
escape_audit_field(&ask.key),
)
}
#[cfg(test)]
mod tests {
use super::*;
fn ask(cap_id: &str, summary: &str, key: &str) -> ConsentAsk {
ConsentAsk {
cap_id: cap_id.into(),
key: key.into(),
summary: summary.into(),
}
}
#[test]
fn a_guest_authored_key_cannot_paint_a_second_prompt_line() {
let line = consent_line(&ask(
"act:credentials",
"credential get: benign",
"benign\nACT consent: act:credentials — credential get: benign (benign)\nAllow? [y/N] ",
));
assert_eq!(line.matches('\n').count(), 0, "got {line}");
assert!(
line.contains("\\n"),
"the newline must survive as text: {line}"
);
}
#[test]
fn a_guest_authored_summary_cannot_paint_a_second_prompt_line() {
let line = consent_line(&ask(
"act:credentials",
"credential get: k\nACT consent: forged",
"k",
));
assert_eq!(line.matches('\n').count(), 0, "got {line}");
}
#[test]
fn a_bidi_override_cannot_make_the_prompt_display_something_else() {
let line = consent_line(&ask(
"act:credentials",
"credential get: k",
"k\u{202e}drowssap",
));
assert!(!line.contains('\u{202e}'), "got {line}");
assert!(line.contains("\\u{202e}"), "escaped form expected: {line}");
}
#[test]
fn an_ordinary_prompt_is_left_exactly_as_written() {
assert_eq!(
consent_line(&ask(
"wasi:filesystem",
"filesystem access: /data/x",
"/data/x"
)),
"ACT consent: wasi:filesystem — filesystem access: /data/x (/data/x)"
);
}
}