feat(core): implement first draft of secure input workaround script
This commit is contained in:
parent
278a3fe008
commit
79be8d2988
|
@ -32,6 +32,7 @@ pub mod modulo;
|
||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
pub mod workaround;
|
||||||
pub mod worker;
|
pub mod worker;
|
||||||
|
|
||||||
pub struct CliModule {
|
pub struct CliModule {
|
||||||
|
|
53
espanso/src/cli/workaround/mod.rs
Normal file
53
espanso/src/cli/workaround/mod.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* This file is part of espanso.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2021 Federico Terzi
|
||||||
|
*
|
||||||
|
* espanso is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* espanso is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
use crate::{error_eprintln, exit_code::{WORKAROUND_FAILURE, WORKAROUND_SUCCESS}};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod secure_input;
|
||||||
|
|
||||||
|
pub fn new() -> CliModule {
|
||||||
|
CliModule {
|
||||||
|
subcommand: "workaround".to_string(),
|
||||||
|
entry: workaround_main,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workaround_main(args: CliModuleArgs) -> i32 {
|
||||||
|
let cli_args = args.cli_args.expect("missing cli_args");
|
||||||
|
|
||||||
|
if cli_args.subcommand_matches("secure-input").is_some() {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
if let Err(err) = secure_input::run_secure_input_workaround() {
|
||||||
|
error_eprintln!("secure-input workaround reported error: {}", err);
|
||||||
|
return WORKAROUND_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
{
|
||||||
|
error_eprintln!("secure-input workaround is only available on macOS");
|
||||||
|
return crate::exit_code::WORKAROUND_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WORKAROUND_SUCCESS
|
||||||
|
}
|
140
espanso/src/cli/workaround/secure_input.rs
Normal file
140
espanso/src/cli/workaround/secure_input.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* This file is part of espanso.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2021 Federico Terzi
|
||||||
|
*
|
||||||
|
* espanso is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* espanso is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
const BLUR_CHROME_WINDOWS_SCRIPT: &'static str =
|
||||||
|
include_str!("../../res/macos/scripts/blur_chrome_windows.scpt");
|
||||||
|
|
||||||
|
const GET_RUNNING_APPS_SCRIPT: &'static str =
|
||||||
|
include_str!("../../res/macos/scripts/get_running_apps.scpt");
|
||||||
|
|
||||||
|
const FOCUS_BITWARDEN_SCRIPT: &'static str =
|
||||||
|
include_str!("../../res/macos/scripts/focus_bitwarden.scpt");
|
||||||
|
|
||||||
|
const SECURE_INPUT_ASK_LOCK_SCREEN_SCRIPT: &'static str =
|
||||||
|
include_str!("../../res/macos/scripts/secure_input_ask_lock_screen.scpt");
|
||||||
|
|
||||||
|
const SUCCESS_DIALOG_SCRIPT: &'static str =
|
||||||
|
include_str!("../../res/macos/scripts/secure_input_disabled_dialog.scpt");
|
||||||
|
|
||||||
|
pub fn run_secure_input_workaround() -> Result<()> {
|
||||||
|
if espanso_mac_utils::get_secure_input_pid().is_none() {
|
||||||
|
println!("secure input is not active, no workaround needed");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_secure_input_workaround()?;
|
||||||
|
let _ = run_apple_script(SUCCESS_DIALOG_SCRIPT);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_secure_input_workaround() -> Result<()> {
|
||||||
|
println!(
|
||||||
|
"Secure input is enabled. Our guess is that it was activated by '{}',",
|
||||||
|
espanso_mac_utils::get_secure_input_application()
|
||||||
|
.map(|entry| entry.0)
|
||||||
|
.unwrap_or_default()
|
||||||
|
);
|
||||||
|
println!("so restarting that application could solve the problem.");
|
||||||
|
println!("");
|
||||||
|
println!("Unfortunately, that guess might be wrong if SecureInput was activated by");
|
||||||
|
println!("the application while in the background.");
|
||||||
|
println!("");
|
||||||
|
println!("This workaround will attempt to execute a series of known actions that often");
|
||||||
|
println!("help in disabling secure input.");
|
||||||
|
|
||||||
|
let running_apps = get_running_apps()?;
|
||||||
|
|
||||||
|
if running_apps.contains("com.google.Chrome") {
|
||||||
|
println!("-> Running chrome defocusing workaround");
|
||||||
|
if let Err(err) = run_apple_script(BLUR_CHROME_WINDOWS_SCRIPT) {
|
||||||
|
eprintln!("unable to run chrome defocusing workaround: {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if espanso_mac_utils::get_secure_input_pid().is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if running_apps.contains("com.bitwarden.desktop") {
|
||||||
|
println!("-> Focusing/Defocusing on Bitwarden");
|
||||||
|
if let Err(err) = run_apple_script(FOCUS_BITWARDEN_SCRIPT) {
|
||||||
|
eprintln!("unable to run bitwarden defocusing workaround: {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if espanso_mac_utils::get_secure_input_pid().is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask the user if he wants to try locking the screen
|
||||||
|
if run_apple_script(SECURE_INPUT_ASK_LOCK_SCREEN_SCRIPT)?.trim() == "yes" {
|
||||||
|
if let Err(err) = lock_screen() {
|
||||||
|
eprintln!("failed to lock the screen: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if espanso_mac_utils::get_secure_input_pid().is_some() {
|
||||||
|
bail!("failed to release secure input");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_apple_script(script: &str) -> Result<String> {
|
||||||
|
let mut child = Command::new("osascript")
|
||||||
|
.arg("-")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
let child_stdin = child.stdin.as_mut().unwrap();
|
||||||
|
child_stdin.write_all(script.as_bytes())?;
|
||||||
|
drop(child_stdin);
|
||||||
|
|
||||||
|
let output = child.wait_with_output()?;
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
Ok(stdout.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lock_screen() -> Result<()> {
|
||||||
|
let mut child = Command::new("osascript")
|
||||||
|
.arg("-e")
|
||||||
|
.arg(r#"tell application "System Events" to keystroke "q" using {command down,control down}"#)
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
child.wait()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_running_apps() -> Result<HashSet<String>> {
|
||||||
|
let apps_raw = run_apple_script(GET_RUNNING_APPS_SCRIPT)?;
|
||||||
|
let mut apps = HashSet::new();
|
||||||
|
for app in apps_raw.trim().split(", ") {
|
||||||
|
apps.insert(app.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(apps)
|
||||||
|
}
|
|
@ -82,7 +82,7 @@ fn secure_input_main(
|
||||||
let secure_input_app = espanso_mac_utils::get_secure_input_application();
|
let secure_input_app = espanso_mac_utils::get_secure_input_application();
|
||||||
|
|
||||||
if let Some((app_name, app_path)) = secure_input_app {
|
if let Some((app_name, app_path)) = secure_input_app {
|
||||||
info!("secure input has been acquired by {}, preventing espanso from working correctly. Full path: {}", app_name, app_path);
|
info!("secure input has been acquired, preventing espanso from working correctly. Our guess is that this is caused by '{}', but there are cases in which the detection is unreliable. Full path: {}", app_name, app_path);
|
||||||
|
|
||||||
if let Err(error) =
|
if let Err(error) =
|
||||||
secure_input_sender.send(SecureInputEvent::Enabled { app_name, app_path })
|
secure_input_sender.send(SecureInputEvent::Enabled { app_name, app_path })
|
||||||
|
|
|
@ -51,6 +51,10 @@ pub const SERVICE_NOT_REGISTERED: i32 = 2;
|
||||||
pub const SERVICE_ALREADY_RUNNING: i32 = 3;
|
pub const SERVICE_ALREADY_RUNNING: i32 = 3;
|
||||||
pub const SERVICE_NOT_RUNNING: i32 = 4;
|
pub const SERVICE_NOT_RUNNING: i32 = 4;
|
||||||
|
|
||||||
|
pub const WORKAROUND_SUCCESS: i32 = 0;
|
||||||
|
pub const WORKAROUND_FAILURE: i32 = 1;
|
||||||
|
pub const WORKAROUND_NOT_AVAILABLE: i32 = 2;
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
|
@ -68,8 +68,8 @@ lazy_static! {
|
||||||
cli::migrate::new(),
|
cli::migrate::new(),
|
||||||
cli::env_path::new(),
|
cli::env_path::new(),
|
||||||
cli::service::new(),
|
cli::service::new(),
|
||||||
|
cli::workaround::new(),
|
||||||
];
|
];
|
||||||
|
|
||||||
static ref ALIASES: Vec<CliAlias> = vec![
|
static ref ALIASES: Vec<CliAlias> = vec![
|
||||||
CliAlias {
|
CliAlias {
|
||||||
subcommand: "start".to_owned(),
|
subcommand: "start".to_owned(),
|
||||||
|
@ -345,6 +345,14 @@ fn main() {
|
||||||
// .subcommand(SubCommand::with_name("refresh")
|
// .subcommand(SubCommand::with_name("refresh")
|
||||||
// .about("Update espanso package index"))
|
// .about("Update espanso package index"))
|
||||||
// )
|
// )
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("workaround")
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("secure-input")
|
||||||
|
.about("Attempt to disable secure input by automating the common steps."),
|
||||||
|
)
|
||||||
|
.about("A collection of workarounds to solve some common problems."),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("worker")
|
SubCommand::with_name("worker")
|
||||||
.setting(AppSettings::Hidden)
|
.setting(AppSettings::Hidden)
|
||||||
|
@ -402,7 +410,6 @@ fn main() {
|
||||||
.find(|cli| matches.subcommand_matches(&cli.subcommand).is_some())
|
.find(|cli| matches.subcommand_matches(&cli.subcommand).is_some())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// When started from the macOS App Bundle, override the default
|
// When started from the macOS App Bundle, override the default
|
||||||
// handler with "launcher" if not present, otherwise the GUI could not be started.
|
// handler with "launcher" if not present, otherwise the GUI could not be started.
|
||||||
if handler.is_none() {
|
if handler.is_none() {
|
||||||
|
|
15
espanso/src/res/macos/scripts/blur_chrome_windows.scpt
Normal file
15
espanso/src/res/macos/scripts/blur_chrome_windows.scpt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
-- Activate each Window of Google Chrome and press CMD+L to focus the address bar
|
||||||
|
-- This makes it possible to "blur" any focused password field that might be keeping
|
||||||
|
-- SecureInput enabled
|
||||||
|
tell application "Google Chrome"
|
||||||
|
activate
|
||||||
|
-- For each window
|
||||||
|
repeat with win in windows
|
||||||
|
-- Bring to front
|
||||||
|
set index of item 1 of win to 1
|
||||||
|
delay 0.5
|
||||||
|
-- And press CMD+L
|
||||||
|
tell application "System Events" to keystroke "l" using command down
|
||||||
|
delay 0.5
|
||||||
|
end repeat
|
||||||
|
end tell
|
4
espanso/src/res/macos/scripts/focus_bitwarden.scpt
Normal file
4
espanso/src/res/macos/scripts/focus_bitwarden.scpt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
tell application "Bitwarden" to activate
|
||||||
|
delay 1
|
||||||
|
tell application "Finder" to activate
|
||||||
|
delay 1
|
5
espanso/src/res/macos/scripts/get_running_apps.scpt
Normal file
5
espanso/src/res/macos/scripts/get_running_apps.scpt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
tell application "System Events"
|
||||||
|
set listOfProcesses to (bundle identifier of every process where background only is false)
|
||||||
|
end tell
|
||||||
|
|
||||||
|
return listOfProcesses
|
|
@ -0,0 +1,8 @@
|
||||||
|
display alert "Espanso wasn't able to automatically disable secure input. Sometimes locking and unlocking the screen helps, do you want to try?" buttons {"No", "Yes"} default button "Yes"
|
||||||
|
if button returned of result = "No" then
|
||||||
|
return "no"
|
||||||
|
else
|
||||||
|
if button returned of result = "Yes" then
|
||||||
|
return "yes"
|
||||||
|
end if
|
||||||
|
end if
|
|
@ -0,0 +1 @@
|
||||||
|
display alert "Secure input successfully disabled!" buttons {"Great!"}
|
Loading…
Reference in New Issue
Block a user