crates/veilvoice-cli/src/input.rs
what this file is for · veilvoice-cli · 120 lines · the same file on GitHub
The file as it is in the tree, in the colours you chose. A line number is a link, and so is every box in this file’s diagram: it opens here with the function it names marked.
// SPDX-License-Identifier: GPL-3.0-or-later
//! `veilvoice input` shows which running programs can see your keyboard and mouse.
//!
//! The whole command is arranged around one risk: that somebody runs it, sees
//! nothing listed, and concludes their machine is clean. That conclusion is
//! wrong and acting on it makes them less safe than not having looked, so the
//! limits are printed **with** the result rather than behind a flag, and they
//! are printed whether anything was found or not.
//!
//! # In plain words
//!
//! This tells you which of the programs currently open on your computer are
//! able to see what you type or where you click. Most of them will be things
//! you installed on purpose, such as a password manager, a screen reader, remote
//! support software you use for work.
//!
//! It cannot tell you whether anything is actually recording your typing. No
//! program can, and this one says so every time rather than letting a short
//! list look like good news.
use crate::theme::{colour, field, heading, paint, warn};
use veilvoice_watch::input::Reach;
/// Show what can see input on this machine.
pub fn look() -> Result<(), String> {
let report = veilvoice_watch::input::look();
println!("{}", heading("What could see your keyboard and mouse"));
println!();
if !report.problems.is_empty() {
for problem in &report.problems {
println!("{}", warn(problem));
}
println!();
}
if report.findings.is_empty() {
println!(
"{}",
paint(colour::MUTED, " nothing this build recognises is running")
);
println!();
}
for finding in &report.findings {
let watcher = finding.watcher;
let tint = match watcher.reach {
// Not red. A remote-support tool being open is information, not an
// alarm, and colouring it as an alarm is how somebody learns to
// ignore the colour.
Reach::Purpose => colour::YELLOW,
Reach::Incidental => colour::MUTED,
};
println!("{}", paint(tint, &format!(" {}", finding.phrasing())));
println!("{}", field(" what it is", watcher.what));
for line in crate::sentry::wrap(watcher.how, 66) {
println!(" {line}");
}
println!();
}
println!("{}", paint(colour::YELLOW, "WHAT THIS CANNOT TELL YOU"));
for line in crate::sentry::wrap(veilvoice_watch::input::LIMITS, 72) {
println!(" {line}");
}
println!();
for line in crate::sentry::wrap(veilvoice_watch::input::WHY_NOT_HOOKING, 72) {
println!(" {line}");
}
println!();
println!(
"{}",
paint(colour::MUTED, &format!(" {}", report.summary()))
);
Ok(())
}
/// Everything this build knows how to recognise, whether running or not.
///
/// Separate from [`look`] because they answer different questions, and because
/// a reader who gets an empty result deserves to be able to see *what* was
/// looked for rather than taking the emptiness on trust.
pub fn known() -> Result<(), String> {
println!(
"{}",
heading("Programs this build can recognise as able to see input")
);
println!();
println!(
"{}",
paint(
colour::MUTED,
" This is not a list of keyloggers. Nearly all of it is software\n \
somebody installed on purpose."
)
);
println!();
for reach in [Reach::Purpose, Reach::Incidental] {
let heading_text = match reach {
Reach::Purpose => "Reading input is what these are for",
Reach::Incidental => "These reach input to do their own job",
};
println!("{}", paint(colour::BLUE, &format!(" {heading_text}")));
for watcher in veilvoice_watch::input::ALL
.iter()
.filter(|w| w.reach == reach)
{
println!("{}", field(&format!(" {}", watcher.name), watcher.what));
}
println!();
}
println!("{}", paint(colour::YELLOW, "WHAT THIS CANNOT TELL YOU"));
for line in crate::sentry::wrap(veilvoice_watch::input::LIMITS, 72) {
println!(" {line}");
}
Ok(())
}