1mod parser;
30pub(crate) use parser::NEWLINE;
31pub use parser::PowerShellSession;
54pub use parser::PsValue;
70pub use parser::ScriptResult;
90pub use parser::Token;
122pub use parser::Variables;
147pub use parser::{CommandToken, ExpressionToken, MethodToken, StringExpandableToken};
148
149#[cfg(test)]
150mod tests {
151 use std::collections::HashMap;
152
153 use super::*;
154 use crate::{ExpressionToken, StringExpandableToken};
155
156 #[test]
157 fn deobfuscation() {
158 let mut p = PowerShellSession::new();
160 let input = r#" $script:var = [char]([int]("9e4e" -replace "e")+3); [int]'a';$var"#;
161 let script_res = p.parse_input(input).unwrap();
162 assert_eq!(script_res.result(), 'a'.into());
163 assert_eq!(
164 script_res.deobfuscated(),
165 vec!["$script:var = 'a'", "[int]'a'", "'a'"].join(NEWLINE)
166 );
167 assert_eq!(script_res.errors().len(), 1);
168 assert_eq!(
169 script_res.errors()[0].to_string(),
170 "ValError: Failed to convert value \"a\" to type Int"
171 );
172
173 let mut p = PowerShellSession::new();
175 let input = r#" $global:var = [char]([int]("9e4e" -replace "e")+3) "#;
176 let script_res = p.parse_input(input).unwrap();
177
178 assert_eq!(script_res.errors().len(), 0);
179
180 let script_res = p.parse_input(" [int]'a';$var ").unwrap();
181 assert_eq!(
182 script_res.deobfuscated(),
183 vec!["[int]'a'", "'a'"].join(NEWLINE)
184 );
185 assert_eq!(script_res.output(), vec!["a"].join(NEWLINE));
186 assert_eq!(script_res.errors().len(), 1);
187 assert_eq!(
188 script_res.errors()[0].to_string(),
189 "ValError: Failed to convert value \"a\" to type Int"
190 );
191 }
192
193 #[test]
194 fn deobfuscation_non_existing_value() {
195 let mut p = PowerShellSession::new();
197 let input = r#" $local:var = $env:programfiles;[int]'a';$var"#;
198 let script_res = p.parse_input(input).unwrap();
199 assert_eq!(script_res.result(), PsValue::Null);
200 assert_eq!(
201 script_res.deobfuscated(),
202 vec!["$local:var = $env:programfiles", "[int]'a'", "$var"].join(NEWLINE)
203 );
204 assert_eq!(script_res.errors().len(), 3);
205 assert_eq!(
206 script_res.errors()[0].to_string(),
207 "VariableError: Variable \"programfiles\" is not defined"
208 );
209 assert_eq!(
210 script_res.errors()[1].to_string(),
211 "ValError: Failed to convert value \"a\" to type Int"
212 );
213 assert_eq!(
214 script_res.errors()[2].to_string(),
215 "VariableError: Variable \"var\" is not defined"
216 );
217
218 let mut p = PowerShellSession::new().with_variables(Variables::force_eval());
220 let input = r#" $local:var = $env:programfiles;[int]'a';$script:var"#;
221 let script_res = p.parse_input(input).unwrap();
222 assert_eq!(script_res.result(), PsValue::Null);
223 assert_eq!(
224 script_res.deobfuscated(),
225 vec!["$local:var = $null", "[int]'a'"].join(NEWLINE)
226 );
227 assert_eq!(script_res.errors().len(), 1);
228 }
229
230 #[test]
231 fn deobfuscation_env_value() {
232 let mut p = PowerShellSession::new().with_variables(Variables::env());
234 let input = r#" $local:var = $env:programfiles;$var"#;
235 let script_res = p.parse_input(input).unwrap();
236 assert_eq!(
237 script_res.result(),
238 PsValue::String(std::env::var("PROGRAMFILES").unwrap())
239 );
240 let program_files = std::env::var("PROGRAMFILES").unwrap();
241 assert_eq!(
242 script_res.deobfuscated(),
243 vec![
244 format!("$local:var = \"{}\"", program_files),
245 format!("\"{}\"", program_files)
246 ]
247 .join(NEWLINE)
248 );
249 assert_eq!(script_res.errors().len(), 0);
250 }
251
252 #[test]
253 fn deobfuscation_from_base_64() {
254 let mut p = PowerShellSession::new();
255 let input = r#" $encoded = [syStem.texT.EncoDInG]::unIcoDe.geTstRiNg([char]97);$encoded"#;
256 let script_res = p.parse_input(input).unwrap();
257 assert_eq!(script_res.result(), String::from("\u{FFFD}").into());
258
259 let input = r#" [syStem.texT.EncoDInG]::unIcoDe.geTstRiNg([SYSTem.cOnVERT]::froMbasE64striNg("ZABlAGMAbwBkAGUAZAA="))"#;
260 let script_res = p.parse_input(input).unwrap();
261 assert_eq!(script_res.result(), String::from("decoded").into());
262 }
263
264 #[test]
265 fn hash_table() {
266 let mut p = PowerShellSession::new().with_variables(Variables::env().values_persist());
268 let input = r#"
269$nestedData = @{
270 Users = @(
271 @{ Name = "Alice"; Age = 30; Skills = @("PowerShell", "Python") }
272 @{ Name = "Bob"; Age = 25; Skills = @("Java", "C#") }
273 )
274 Settings = @{
275 Theme = "Dark"
276 Language = "en-US"
277 }
278}
279"$nestedData"
280 "#;
281 let script_res = p.parse_input(input).unwrap();
282 assert_eq!(
283 script_res.result(),
284 PsValue::String("System.Collections.Hashtable".to_string())
285 );
286
287 assert_eq!(
288 p.parse_input("$nesteddata.settings").unwrap().result(),
289 PsValue::HashTable(HashMap::from([
290 ("language".to_string(), PsValue::String("en-US".to_string())),
291 ("theme".to_string(), PsValue::String("Dark".to_string())),
292 ]))
293 );
294
295 assert_eq!(
296 p.safe_eval("$nesteddata.settings.theme").unwrap(),
297 "Dark".to_string()
298 );
299
300 assert_eq!(
301 p.parse_input("$nesteddata.users[0]").unwrap().result(),
302 PsValue::HashTable(HashMap::from([
303 (
304 "skills".to_string(),
305 PsValue::Array(vec![
306 PsValue::String("PowerShell".to_string()),
307 PsValue::String("Python".to_string().into())
308 ])
309 ),
310 ("name".to_string(), PsValue::String("Alice".to_string())),
311 ("age".to_string(), PsValue::Int(30)),
312 ]))
313 );
314
315 assert_eq!(
316 p.safe_eval("$nesteddata.users[0]['name']").unwrap(),
317 "Alice".to_string()
318 );
319
320 assert_eq!(
321 p.safe_eval("$nesteddata.users[0].NAME").unwrap(),
322 "Alice".to_string()
323 );
324
325 let input = r#" $a=@{val = 4};$a.val"#;
326 let script_res = p.parse_input(input).unwrap();
327 assert_eq!(script_res.result(), PsValue::Int(4));
328 assert_eq!(
329 script_res.deobfuscated(),
330 vec!["$a = @{", "\tval = 4", "}", "4"].join(NEWLINE)
331 );
332 }
333
334 #[test]
335 fn test_simple_arithmetic() {
336 let input = r#"
337Write-Host "=== Test 3: Arithmetic Operations ===" -ForegroundColor Green
338$a = 10
339$b = 5
340Write-Output "Addition: $(($a + $b))"
341Write-Output "Subtraction: $(($a - $b))"
342Write-Output "Multiplication: $(($a * $b))"
343Write-Output "Division: $(($a / $b))"
344Write-Output "Modulo: $(($a % $b))"
345"#;
346
347 let script_result = PowerShellSession::new().parse_input(input).unwrap();
348
349 assert_eq!(script_result.result(), PsValue::String("Modulo: 0".into()));
350 assert_eq!(
351 script_result.output(),
352 vec![
353 r#"=== Test 3: Arithmetic Operations ==="#,
354 r#"Addition: 15"#,
355 r#"Subtraction: 5"#,
356 r#"Multiplication: 50"#,
357 r#"Division: 2"#,
358 r#"Modulo: 0"#
359 ]
360 .join(NEWLINE)
361 );
362 assert_eq!(script_result.errors().len(), 0);
363 assert_eq!(script_result.tokens().expandable_strings().len(), 6);
364 assert_eq!(
365 script_result.tokens().expandable_strings()[1],
366 StringExpandableToken::new(
367 "\"Addition: $(($a + $b))\"".to_string(),
368 "Addition: 15".to_string()
369 )
370 );
371 assert_eq!(script_result.tokens().expressions().len(), 12);
372 assert_eq!(
373 script_result.tokens().expressions()[2],
374 ExpressionToken::new("$a + $b".to_string(), PsValue::Int(15))
375 );
376 }
377
378 #[test]
379 fn test_scripts() {
380 use std::fs;
381 let Ok(entries) = fs::read_dir("test_scripts") else {
382 panic!("Failed to read 'test_scripts' directory");
383 };
384 for entry in entries {
385 let dir_entry = entry.unwrap();
386 if std::fs::FileType::is_dir(&dir_entry.file_type().unwrap()) {
387 let input_script = dir_entry.path().join("input.ps1");
389 let expected_deobfuscated_script = dir_entry.path().join("deobfuscated.txt");
390 let expected_output_script = dir_entry.path().join("output.txt");
391
392 let Ok(input) = fs::read_to_string(&input_script) else {
393 panic!("Failed to read test file: {}", input_script.display());
394 };
395
396 let Ok(expected_deobfuscated) = fs::read_to_string(&expected_deobfuscated_script)
397 else {
398 panic!(
399 "Failed to read test file: {}",
400 expected_deobfuscated_script.display()
401 );
402 };
403
404 let Ok(expected_output) = fs::read_to_string(&expected_output_script) else {
405 panic!(
406 "Failed to read test file: {}",
407 expected_output_script.display()
408 );
409 };
410
411 let script_result = PowerShellSession::new()
412 .with_variables(Variables::env())
413 .parse_input(&input)
414 .unwrap();
415
416 let expected_deobfuscated_vec = expected_deobfuscated
417 .lines()
418 .map(|s| s.trim_end())
419 .collect::<Vec<&str>>();
420
421 let current_deobfuscated = script_result.deobfuscated();
422 let current_output = script_result.output();
423
424 let expected_output_vec = expected_output
425 .lines()
426 .map(|s| s.trim_end())
427 .collect::<Vec<&str>>();
428
429 let current_deobfuscated_vec = current_deobfuscated
431 .lines()
432 .map(|s| s.trim_end())
433 .collect::<Vec<&str>>();
434
435 let current_output_vec = current_output
436 .lines()
437 .map(|s| s.trim_end())
438 .collect::<Vec<&str>>();
439
440 for i in 0..expected_deobfuscated_vec.len() {
441 assert_eq!(
442 expected_deobfuscated_vec[i],
443 current_deobfuscated_vec[i],
444 "File: {}, Deobfuscated line: {}",
445 file_name(&dir_entry),
446 i + 1
447 );
448 }
449
450 for i in 0..expected_output_vec.len() {
451 assert_eq!(
452 expected_output_vec[i],
453 current_output_vec[i],
454 "File: {}, Output line: {}",
455 file_name(&dir_entry),
456 i + 1
457 );
458 }
459 }
460 }
461 }
462
463 fn file_name(dir_entry: &std::fs::DirEntry) -> String {
464 dir_entry
465 .path()
466 .components()
467 .last()
468 .unwrap()
469 .as_os_str()
470 .to_string_lossy()
471 .to_string()
472 }
473
474 #[allow(dead_code)]
475 fn save_files(dir_entry: &std::fs::DirEntry, deobfuscated: &str, output: &str) {
476 let name = file_name(dir_entry);
477 std::fs::write(format!("{}_deobfuscated.txt", name), deobfuscated).unwrap();
478 std::fs::write(format!("{}_output.txt", name), output).unwrap();
479 }
480
481 #[test]
482 fn test_range() {
483 let mut p = PowerShellSession::new().with_variables(Variables::env());
485 let input = r#" $numbers = 1..10; $numbers"#;
486 let script_res = p.parse_input(input).unwrap();
487 assert_eq!(
488 script_res.deobfuscated(),
489 vec![
490 "$numbers = @(1,2,3,4,5,6,7,8,9,10)",
491 "@(1,2,3,4,5,6,7,8,9,10)"
492 ]
493 .join(NEWLINE)
494 );
495 assert_eq!(script_res.errors().len(), 0);
496 }
497
498 #[test]
499 fn even_numbers() {
500 let mut p = PowerShellSession::new().with_variables(Variables::env());
502 let input = r#" $numbers = 1..10; $evenNumbers = $numbers | Where-Object { $_ % 2 -eq 0 }; $evenNumbers"#;
503 let script_res = p.parse_input(input).unwrap();
504 assert_eq!(
505 script_res.result(),
506 PsValue::Array(vec![
507 PsValue::Int(2),
508 PsValue::Int(4),
509 PsValue::Int(6),
510 PsValue::Int(8),
511 PsValue::Int(10)
512 ])
513 );
514 assert_eq!(
515 script_res.deobfuscated(),
516 vec![
517 "$numbers = @(1,2,3,4,5,6,7,8,9,10)",
518 "$evennumbers = @(2,4,6,8,10)",
519 "@(2,4,6,8,10)"
520 ]
521 .join(NEWLINE)
522 );
523 assert_eq!(script_res.errors().len(), 0);
524 }
525
526 #[test]
527 fn divisible_by_2_and_3() {
528 let mut p = PowerShellSession::new().with_variables(Variables::env());
530 let input = r#" $numbers = 1..10; $numbers | Where { $_ % 2 -eq 0 } | ? { $_ % 3 -eq 0 }"#;
531 let script_res = p.parse_input(input).unwrap();
532 assert_eq!(script_res.result(), PsValue::Int(6));
533 assert_eq!(
534 script_res.deobfuscated(),
535 vec!["$numbers = @(1,2,3,4,5,6,7,8,9,10)", "6"].join(NEWLINE)
536 );
537 assert_eq!(script_res.errors().len(), 0);
538 }
539
540 fn _test_function() {
542 let mut p = PowerShellSession::new().with_variables(Variables::env());
544 let input = r#"
545function Get-Square($number) {
546 return $number * $number
547}
548"Square of 5: $(Get-Square 5)" "#;
549 let script_res = p.parse_input(input).unwrap();
550 assert_eq!(
551 script_res.deobfuscated(),
552 vec![
553 "function Get-Square($number) {",
554 " return $number * $number",
555 "}",
556 " \"Square of 5: $(Get-Square 5)\""
557 ]
558 .join(NEWLINE)
559 );
560 assert_eq!(script_res.errors().len(), 2);
561 }
562
563 #[test]
564 fn test_if() {
565 let mut p = PowerShellSession::new().with_variables(Variables::env());
567 let input = r#"
568 # Test 10: Conditional Statements
569if ($true) {
570 $if_result = "condition true"
571}
572
573if ($false) {
574 $else_result = "false branch"
575} else {
576 $else_result = "true branch"
577}
578
579$score = 85
580if ($score -ge 90) {
581 $grade = "A"
582} elseif ($score -ge 80) {
583 $grade = "B"
584} else {
585 $grade = "C"
586}
587
588 "#;
589 let script_res = p.parse_input(input).unwrap();
590 assert_eq!(
591 script_res.deobfuscated(),
592 vec![
593 "$if_result = \"condition true\"",
594 "$else_result = \"true branch\"",
595 "$score = 85",
596 "$grade = \"B\""
597 ]
598 .join(NEWLINE)
599 );
600 assert_eq!(script_res.errors().len(), 0);
601 }
602
603 #[test]
604 fn format_operator() {
605 let mut p = PowerShellSession::new().with_variables(Variables::env());
606 let input = r#" ("{5}{2}{0}{1}{3}{6}{4}" -f 'ut',('oma'+'t'+'ion.'),'.A',('Ems'+'iUt'),'ls',('S'+'ystem.'+'Danage'+'men'+'t'),'i')"#;
607 let script_res = p.parse_input(input).unwrap();
608 assert_eq!(
609 script_res.result(),
610 PsValue::String("System.Danagement.Automation.EmsiUtils".into())
611 );
612 assert_eq!(
613 script_res.deobfuscated(),
614 vec![r#""System.Danagement.Automation.EmsiUtils""#].join(NEWLINE)
615 );
616 assert_eq!(script_res.errors().len(), 0);
617 }
618
619 #[test]
620 fn encod_command() {
621 let mut p = PowerShellSession::new().with_variables(Variables::env());
622 let input = r#" ("{5}{2}{0}{1}{3}{6}{4}" -f 'ut',('oma'+'t'+'ion.'),'.A',('Ems'+'iUt'),'ls',('S'+'ystem.'+'Danage'+'men'+'t'),'i')"#;
623 let script_res = p.parse_input(input).unwrap();
624 assert_eq!(
625 script_res.result(),
626 PsValue::String("System.Danagement.Automation.EmsiUtils".into())
627 );
628 assert_eq!(
629 script_res.deobfuscated(),
630 vec![r#""System.Danagement.Automation.EmsiUtils""#].join(NEWLINE)
631 );
632 assert_eq!(script_res.errors().len(), 0);
633 }
634
635 #[test]
636 fn array_literals() {
637 let mut p = PowerShellSession::new().with_variables(Variables::env());
638
639 let input = r#" $a = 1,2,3;$a"#;
641 let script_res = p.parse_input(input).unwrap();
642 assert_eq!(
643 script_res.result(),
644 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(2), PsValue::Int(3)])
645 );
646 assert_eq!(
647 script_res.deobfuscated(),
648 vec!["$a = @(1,2,3)", "@(1,2,3)"].join(NEWLINE)
649 );
650
651 let input = r#" $a = "x", 'yyy', "z";$a"#;
653 let script_res = p.parse_input(input).unwrap();
654 assert_eq!(
655 script_res.result(),
656 PsValue::Array(vec![
657 PsValue::String("x".into()),
658 PsValue::String("yyy".into()),
659 PsValue::String("z".into())
660 ])
661 );
662 assert_eq!(
663 script_res.deobfuscated(),
664 vec![r#"$a = @("x","yyy","z")"#, r#"@("x","yyy","z")"#].join(NEWLINE)
665 );
666
667 let input = r#" $a = 1,2+ 3,[long]4;$a"#;
669 let script_res = p.parse_input(input).unwrap();
670 assert_eq!(
671 script_res.result(),
672 PsValue::Array(vec![
673 PsValue::Int(1),
674 PsValue::Int(2),
675 PsValue::Int(3),
676 PsValue::Int(4),
677 ])
678 );
679 assert_eq!(
680 script_res.deobfuscated(),
681 vec!["$a = @(1,2,3,4)", "@(1,2,3,4)"].join(NEWLINE)
682 );
683
684 let input = r#" $x = 3; $a = $x, $x+1, "count=$x";$a"#;
686 let script_res = p.parse_input(input).unwrap();
687 assert_eq!(
688 script_res.result(),
689 PsValue::Array(vec![
690 PsValue::Int(3),
691 PsValue::Int(3),
692 PsValue::Int(1),
693 PsValue::String("count=3".into()),
694 ])
695 );
696 assert_eq!(
697 script_res.deobfuscated(),
698 vec![
699 "$x = 3",
700 "$a = @(3,3,1,\"count=3\")",
701 "@(3,3,1,\"count=3\")"
702 ]
703 .join(NEWLINE)
704 );
705
706 let input = r#" $a = (1, 2), (3, 4);$a"#;
708 let script_res = p.parse_input(input).unwrap();
709 assert_eq!(
710 script_res.result(),
711 PsValue::Array(vec![
712 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(2)]),
713 PsValue::Array(vec![PsValue::Int(3), PsValue::Int(4)]),
714 ])
715 );
716 assert_eq!(
717 script_res.deobfuscated(),
718 vec!["$a = @(@(1,2),@(3,4))", "@(@(1,2),@(3,4))"].join(NEWLINE)
719 );
720
721 let input = r#" $a = 1, "two", 3.0, $false, (Get-Date);$a"#;
723 let script_res = p.parse_input(input).unwrap();
724 assert_eq!(
725 script_res.result(),
726 PsValue::Array(vec![
727 PsValue::Int(1),
728 PsValue::String("two".into()),
729 PsValue::Float(3.0),
730 PsValue::Bool(false),
731 PsValue::String("Get-Date".into()),
732 ])
733 );
734 assert_eq!(
735 script_res.deobfuscated(),
736 vec![
737 "$a = @(1,\"two\",3,$false,Get-Date)",
738 "@(1,\"two\",3,$false,Get-Date)"
739 ]
740 .join(NEWLINE)
741 );
742
743 let input = r#" $a = 1, 2,3;$b = $a,4,5;$b"#;
745 let script_res = p.parse_input(input).unwrap();
746 assert_eq!(
747 script_res.result(),
748 PsValue::Array(vec![
749 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(2), PsValue::Int(3)]),
750 PsValue::Int(4),
751 PsValue::Int(5),
752 ])
753 );
754 assert_eq!(
755 script_res.deobfuscated(),
756 vec!["$a = @(1,2,3)", "$b = @(@(1,2,3),4,5)", "@(@(1,2,3),4,5)"].join(NEWLINE)
757 );
758
759 let input = r#" $a = 1,-2,(-3) | ForEach-Object { $_ * 2 };$a"#;
761 let script_res = p.parse_input(input).unwrap();
762 assert_eq!(
763 script_res.result(),
764 PsValue::Array(vec![PsValue::Int(2), PsValue::Int(-4), PsValue::Int(-6),])
765 );
766
767 let input = r#" $a = (1,2,3) | ForEach-Object { $_ };$a"#;
769 let script_res = p.parse_input(input).unwrap();
770 assert_eq!(
771 script_res.result(),
772 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(2), PsValue::Int(3),])
773 );
774 assert_eq!(
775 script_res.deobfuscated(),
776 vec!["$a = @(1,2,3)", "@(1,2,3)"].join(NEWLINE)
777 );
778
779 let input = r#" $a = @{
781 A = 1,2,3
782 B = (4,5),6
783}
784$a"#;
785 let script_res = p.parse_input(input).unwrap();
786 assert_eq!(
787 script_res.result(),
788 PsValue::HashTable(HashMap::from([
789 (
790 "a".into(),
791 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(2), PsValue::Int(3),])
792 ),
793 (
794 "b".into(),
795 PsValue::Array(vec![
796 PsValue::Array(vec![PsValue::Int(4), PsValue::Int(5)]),
797 PsValue::Int(6),
798 ])
799 ),
800 ]))
801 );
802
803 let input = r#" function Foo($x) { $x.GetType().name + $x[2]};Foo(1,2,3)"#;
805 let script_res = p.parse_input(input).unwrap();
806 assert_eq!(script_res.result(), PsValue::String("Object[]3".into()));
807
808 let input = r#" [object[]](1,2,3)"#;
810 let script_res = p.parse_input(input).unwrap();
811 assert_eq!(
812 script_res.result(),
813 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(2), PsValue::Int(3)])
814 );
815
816 let input = r#" $a = ,(42,2);$a"#;
818 let script_res = p.parse_input(input).unwrap();
819 assert_eq!(
820 script_res.result(),
821 PsValue::Array(vec![PsValue::Array(vec![
822 PsValue::Int(42),
823 PsValue::Int(2)
824 ])])
825 );
826
827 let input = r#" function Foo($x) { $x.GetType().name + $x[2]};Foo(1,2,3)"#;
829 let script_res = p.parse_input(input).unwrap();
830 assert_eq!(script_res.result(), PsValue::String("Object[]3".into()));
831
832 let input = r#" function b($x) {$x};b(1,2+3,4)"#;
834 let script_res = p.parse_input(input).unwrap();
835 assert_eq!(
836 script_res.result(),
837 PsValue::Array(vec![
838 PsValue::Int(1),
839 PsValue::Int(2),
840 PsValue::Int(3),
841 PsValue::Int(4),
842 ])
843 );
844
845 let input =
847 r#" $a=@{val = 4};function b($x) {$x};b(1, [long]($a | Where-Object val -eq 4).val)"#;
848 let script_res = p.parse_input(input).unwrap();
849 assert_eq!(
850 script_res.result(),
851 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(4)])
852 );
853 }
854
855 #[test]
856 fn cast_expression() {
857 let mut p = PowerShellSession::new().with_variables(Variables::env());
858
859 let input = r#" $a=@{val = 4};[long]($a).val"#;
861 let script_res = p.parse_input(input).unwrap();
862 assert_eq!(script_res.result(), PsValue::Int(4));
863 assert_eq!(
864 script_res.deobfuscated(),
865 vec!["$a = @{", "\tval = 4", "}", "4"].join(NEWLINE)
866 );
867
868 let input = r#" $a=@{val = 4};[long]($a | Where-Object Val -eq 4).val"#;
869 let script_res = p.parse_input(input).unwrap();
870 assert_eq!(script_res.result(), PsValue::Int(4));
871 assert_eq!(
872 script_res.deobfuscated(),
873 vec!["$a = @{", "\tval = 4", "}", "4"].join(NEWLINE)
874 );
875 }
876
877 #[test]
878 fn as_expression() {
879 let mut p = PowerShellSession::new().with_variables(Variables::env());
880
881 let input = r#" '1a1' -replace 'a' -as [int] "#;
883 let script_res = p.parse_input(input).unwrap();
884 assert_eq!(script_res.result(), PsValue::Int(11));
885
886 let input = r#" '1a1' -replace ('a' -as [int])"#;
887 let script_res = p.parse_input(input).unwrap();
888 assert_eq!(script_res.result(), PsValue::String("1a1".into()));
889
890 let input = r#" '2' -as [int] -shl 1"#;
891 let script_res = p.parse_input(input).unwrap();
892 assert_eq!(script_res.result(), PsValue::Int(4));
893
894 let input = r#" [system.text.encoding]::unicode -shl 1 "#;
895 let script_res = p.parse_input(input).unwrap();
896 assert_eq!(script_res.result(), PsValue::Null);
897 assert_eq!(
898 script_res.errors()[0].to_string(),
899 String::from("BitwiseError: -shl not defined for UnicodeEncoding")
900 );
901
902 let input = r#" [int] -shl 1 "#;
903 let script_res = p.parse_input(input).unwrap();
904 assert_eq!(script_res.result(), PsValue::Null);
905 assert_eq!(
906 script_res.errors()[0].to_string(),
907 String::from("BitwiseError: -shl not defined for Int32")
908 );
909
910 let input = r#" '2' -as ([string] -shl 1) "#;
911 let script_res = p.parse_input(input).unwrap();
912 assert_eq!(script_res.result(), PsValue::Null);
913 assert_eq!(
914 script_res.errors()[0].to_string(),
915 String::from("BitwiseError: -shl not defined for String")
916 );
917
918 let input = r#" '2' -as ([int]) "#;
919 let script_res = p.parse_input(input).unwrap();
920 assert_eq!(script_res.result(), PsValue::Int(2));
921
922 let input = r#" '2' -As ([int]) "#;
923 let script_res = p.parse_input(input).unwrap();
924 assert_eq!(script_res.result(), PsValue::Int(2));
925 }
926
927 #[test]
928 fn cast_assignment() {
929 let mut p = PowerShellSession::new().with_variables(Variables::env());
930
931 let input = r#" [int] $elo = "1"; $elo "#;
932 let script_res = p.parse_input(input).unwrap();
933 assert_eq!(script_res.result(), PsValue::Int(1));
934
935 let input = r#" [int] $elo = "1a": $elo"#;
936 let script_res = p.parse_input(input).unwrap();
937 assert_eq!(script_res.result(), PsValue::Null);
938 assert_eq!(
939 script_res.errors()[0].to_string(),
940 String::from("ValError: Failed to convert value \"1a\" to type Int")
941 );
942
943 let input = r#" [double] $elo = "1a": $elo"#;
944 let script_res = p.parse_input(input).unwrap();
945 assert_eq!(script_res.result(), PsValue::Null);
946 assert_eq!(
947 script_res.errors()[0].to_string(),
948 String::from("ValError: Failed to convert value \"1a\" to type Float")
949 );
950
951 let input = r#" [int[]] $elo = "1", "2"; $elo"#;
952 let script_res = p.parse_input(input).unwrap();
953 assert_eq!(
954 script_res.result(),
955 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(2)])
956 );
957
958 let input = r#" [byte[]] $elo = "1", "2"; $elo"#;
959 let script_res = p.parse_input(input).unwrap();
960 assert_eq!(
961 script_res.result(),
962 PsValue::Array(vec![PsValue::Char(49), PsValue::Char(50)])
963 );
964 }
965
966 #[test]
967 fn splatten_arg() {
968 let mut p = PowerShellSession::new().with_variables(Variables::env());
969
970 let input = r#" $a = @{ elo= 2; name= "radek"}; write-output @a "#;
971 let script_res = p.parse_input(input).unwrap();
972 assert!(script_res.output().contains("-elo 2"));
973 assert!(script_res.output().contains("-name radek"));
974 }
975
976 #[test]
977 fn strange_assignment() {
978 let mut p = PowerShellSession::new().with_variables(Variables::env());
979
980 let input = r#" @(1,2)[0] = 1 "#;
981 let script_res = p.parse_input(input).unwrap();
982 assert_eq!(script_res.errors()[0].to_string(), "Skip".to_string());
983
984 let input = r#" "elo"[0] = 1 "#;
985 let script_res = p.parse_input(input).unwrap();
986 assert_eq!(script_res.errors()[0].to_string(), "Skip".to_string());
987
988 let input = r#" $a = @(1,2); $a[1] = 5; $a "#;
989 let script_res = p.parse_input(input).unwrap();
990 assert_eq!(
991 script_res.result(),
992 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(5)])
993 );
994
995 let input = r#" $a = @(1,@(2,3));$a[1] = 6;$a "#;
996 let script_res = p.parse_input(input).unwrap();
997 assert_eq!(
998 script_res.result(),
999 PsValue::Array(vec![PsValue::Int(1), PsValue::Int(6)])
1000 );
1001
1002 let input = r#" $a = @(1,@(2,3));$a[1][1] = 6;$a "#;
1003 let script_res = p.parse_input(input).unwrap();
1004 assert_eq!(
1005 script_res.result(),
1006 PsValue::Array(vec![
1007 PsValue::Int(1),
1008 PsValue::Array(vec![PsValue::Int(2), PsValue::Int(6)])
1009 ])
1010 );
1011 }
1012
1013 #[test]
1014 fn script_param_block() {
1015 let mut p = PowerShellSession::new().with_variables(Variables::env());
1016
1017 let input = r#"
1018[CmdletBinding(DefaultParameterSetName = "Path", HelpURI = "https://go.microsoft.com/fwlink/?LinkId=517145")]
1019param(
1020 [Parameter(ParameterSetName="Path", Position = 0)]
1021 [System.String[]]
1022 $Path
1023
1024
1025)
1026
1027begin
1028{
1029 # Construct the strongly-typed crypto object
1030}
1031
1032process
1033{
1034 Write-output elo
1035}
1036"#;
1037 let _script_res = p.parse_input(input).unwrap();
1038 }
1039}