|
| 1 | +pub(crate) fn build_powershell_notification_args(title: &str, body: &str) -> Vec<String> { |
| 2 | + use base64::Engine; |
| 3 | + |
| 4 | + let b64_title = base64::engine::general_purpose::STANDARD.encode( |
| 5 | + title |
| 6 | + .encode_utf16() |
| 7 | + .flat_map(|c| c.to_le_bytes()) |
| 8 | + .collect::<Vec<u8>>(), |
| 9 | + ); |
| 10 | + let b64_body = base64::engine::general_purpose::STANDARD.encode( |
| 11 | + body.encode_utf16() |
| 12 | + .flat_map(|c| c.to_le_bytes()) |
| 13 | + .collect::<Vec<u8>>(), |
| 14 | + ); |
| 15 | + |
| 16 | + let script = format!( |
| 17 | + "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('{}')), [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('{}')))", |
| 18 | + b64_body, b64_title |
| 19 | + ); |
| 20 | + let b64_script = base64::engine::general_purpose::STANDARD.encode( |
| 21 | + script |
| 22 | + .encode_utf16() |
| 23 | + .flat_map(|c| c.to_le_bytes()) |
| 24 | + .collect::<Vec<u8>>(), |
| 25 | + ); |
| 26 | + |
| 27 | + vec![ |
| 28 | + "-NoProfile".to_string(), |
| 29 | + "-NonInteractive".to_string(), |
| 30 | + "-EncodedCommand".to_string(), |
| 31 | + b64_script, |
| 32 | + ] |
| 33 | +} |
| 34 | + |
1 | 35 | use crate::{StdFunction, StdlibModule, StdlibRegistry}; |
2 | 36 | use std::collections::HashMap; |
3 | 37 | use std::rc::Rc; |
@@ -32,21 +66,10 @@ impl StdlibRegistry { |
32 | 66 | let body = args[1].to_string(); |
33 | 67 | #[cfg(target_os = "windows")] |
34 | 68 | { |
35 | | - use base64::Engine; |
36 | 69 | use std::process::Command; |
37 | 70 |
|
38 | | - let b64_title = base64::engine::general_purpose::STANDARD.encode(title.encode_utf16().flat_map(|c| c.to_le_bytes()).collect::<Vec<u8>>()); |
39 | | - let b64_body = base64::engine::general_purpose::STANDARD.encode(body.encode_utf16().flat_map(|c| c.to_le_bytes()).collect::<Vec<u8>>()); |
40 | | - |
41 | | - let script = format!( |
42 | | - "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('{}')), [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('{}')))", |
43 | | - b64_body, b64_title |
44 | | - ); |
45 | | - let b64_script = base64::engine::general_purpose::STANDARD.encode(script.encode_utf16().flat_map(|c| c.to_le_bytes()).collect::<Vec<u8>>()); |
46 | | - |
47 | | - let _ = Command::new("powershell") |
48 | | - .args(["-EncodedCommand", &b64_script]) |
49 | | - .spawn(); |
| 71 | + let args = build_powershell_notification_args(&title, &body); |
| 72 | + let _ = Command::new("powershell").args(&args).spawn(); |
50 | 73 | } |
51 | 74 | #[cfg(not(target_os = "windows"))] |
52 | 75 | { |
@@ -127,4 +150,31 @@ mod tests { |
127 | 150 | assert_eq!(alert.name(), "alert"); |
128 | 151 | assert_eq!(alert.arity(), 1); |
129 | 152 | } |
| 153 | + |
| 154 | + #[test] |
| 155 | + fn test_powershell_notification_args_security() { |
| 156 | + let title = "Test Title'; Remove-Item -Path C:\\ -Recurse -Force; '"; |
| 157 | + let body = "Test Body\n\"'$$\x00"; |
| 158 | + |
| 159 | + let args = build_powershell_notification_args(title, body); |
| 160 | + |
| 161 | + assert_eq!(args[0], "-NoProfile"); |
| 162 | + assert_eq!(args[1], "-NonInteractive"); |
| 163 | + assert_eq!(args[2], "-EncodedCommand"); |
| 164 | + |
| 165 | + use base64::Engine; |
| 166 | + let script_bytes = base64::engine::general_purpose::STANDARD |
| 167 | + .decode(&args[3]) |
| 168 | + .unwrap(); |
| 169 | + let u16_chars: Vec<u16> = script_bytes |
| 170 | + .chunks_exact(2) |
| 171 | + .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) |
| 172 | + .collect(); |
| 173 | + let script = String::from_utf16(&u16_chars).unwrap(); |
| 174 | + |
| 175 | + assert!(script.contains("Add-Type -AssemblyName PresentationFramework")); |
| 176 | + assert!(script.contains("[System.Windows.MessageBox]::Show")); |
| 177 | + |
| 178 | + assert!(!script.contains("Remove-Item")); |
| 179 | + } |
130 | 180 | } |
0 commit comments