1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! A fixed program name is not enough: the *arguments* must not be able to
//! turn a benign-looking builtin into arbitrary execution.
//!
//! Two distinct defects, both found on 2026-08-04 and both verified by
//! execution rather than by reading:
//!
//! 1. **PowerShell injection (CWE-78).** Windows builtins build commands by
//! interpolating values into single-quoted PowerShell literals, e.g.
//! `format!("Start-Service '{}'", name)`. A value containing `'` closes the
//! string and the rest is executed. Proof: a service name of
//! `x'; New-Item -ItemType File -Path '<tmp>' -Force; '` created the file.
//!
//! 2. **Option injection (CWE-88).** `tar -cvf out.tar <files>` with a "file"
//! named `--use-compress-program=sh -c '…'` runs that command; Info-ZIP's
//! `-TT` does the same. Both were reachable with no policy gate, so they
//! bypassed the `Effect::Exec` approval added the same day.
use aethershell::safety::{applescript_quote, ps_quote, reject_option_like};
/// The reason single-quoting is the fix rather than escaping `"`.
///
/// A *double*-quoted PowerShell string expands `$`, so `$(command)` runs even
/// with no quote character anywhere in the payload. Several sites escaped only
/// `"` (as `` `" ``) and were therefore still injectable; this was demonstrated
/// by `base64_encode("$(New-Item …)")` creating the file. Moving those sites to
/// a single-quoted literal removes expansion entirely.
#[test]
fn ps_quote_defeats_subexpression_payloads_that_quote_escaping_missed() {
// No apostrophes here, so the only transformation under test is that `$`
// passes through untouched — it needs no escaping once the literal is
// single-quoted, which is the whole point.
let payload = "$(New-Item -ItemType File -Path C:\\tmp\\pwned -Force)";
let quoted = ps_quote(payload);
assert_eq!(quoted, format!("'{payload}'"));
assert!(
!quoted.starts_with('"'),
"a double-quoted literal would re-enable $() expansion"
);
// And when the payload *does* carry apostrophes, they are doubled so it
// still cannot break out.
let with_quotes = "$(Get-Content 'C:\\secret')";
assert_eq!(
ps_quote(with_quotes),
"'$(Get-Content ''C:\\secret'')'",
"apostrophes must be doubled while `$` stays inert"
);
}
/// AppleScript literals escape with a backslash, so the backslash itself must be
/// escaped first — otherwise escaping the quote is undone by the payload.
#[test]
fn applescript_quote_escapes_backslash_before_quote() {
assert_eq!(applescript_quote("plain"), "\"plain\"");
// `\"` would close the literal if the backslash were not doubled first.
assert_eq!(applescript_quote(r#"a\"#), r#""a\\""#);
let attack = r#"" & (do shell script "touch /tmp/pwned") & ""#;
let quoted = applescript_quote(attack);
assert!(quoted.starts_with('"') && quoted.ends_with('"'));
// No bare quote may remain in the interior.
let interior = "ed[1..quoted.len() - 1];
let mut chars = interior.chars().peekable();
while let Some(c) = chars.next() {
if c == '\\' {
chars.next();
} else {
assert_ne!(c, '"', "an unescaped quote closes the AppleScript literal");
}
}
}
/// The escaping rule itself. PowerShell closes a single-quoted string on `'`
/// and escapes one by doubling it; nothing else is special in that context.
#[test]
fn ps_quote_neutralizes_the_quote_that_ends_the_string() {
assert_eq!(ps_quote("plain"), "'plain'");
// The exact payload that was demonstrated to execute.
let attack = "x'; New-Item -ItemType File -Path 'C:\\tmp\\pwned' -Force; '";
let quoted = ps_quote(attack);
assert!(quoted.starts_with('\'') && quoted.ends_with('\''));
// Every quote in the interior must be doubled, so the literal cannot be
// terminated early. Strip the delimiters, then check the interior has no
// odd-length run of quotes.
let interior = "ed[1..quoted.len() - 1];
for run in interior.split(|c| c != '\'').filter(|r| !r.is_empty()) {
assert!(
run.len() % 2 == 0,
"an odd run of quotes escapes the literal: {run:?}"
);
}
}
/// Nothing should be altered other than quotes — the value still has to be the
/// path the caller asked for.
#[test]
fn ps_quote_leaves_everything_else_alone() {
for s in [
r"C:\Program Files\thing.txt",
"$env:PATH",
"back`tick",
"semi;colon",
"a b c",
] {
assert_eq!(
ps_quote(s),
format!("'{s}'"),
"only the quote character needs escaping in a single-quoted literal"
);
}
}
/// Option-like positional arguments are refused before the tool ever sees them.
#[test]
fn option_like_paths_are_refused() {
for payload in [
"--use-compress-program=sh -c 'id'",
"--to-command=sh -c 'id'",
"-TTsh -c 'id'",
"-I/bin/sh",
"--exclude=x",
] {
let r = reject_option_like("tar_create", &[payload.to_string()]);
assert!(
r.is_err(),
"{payload:?} must be refused — several archivers execute it"
);
}
}
/// The check must not break ordinary paths, or callers will route around it.
#[test]
fn ordinary_paths_are_accepted() {
let ok: Vec<String> = [
"file.txt",
"./file.txt",
"dir/file.txt",
r"C:\Users\x\file.txt",
"/abs/path",
// The documented escape hatch for a file genuinely named "-weird".
"./-weird",
]
.iter()
.map(|s| s.to_string())
.collect();
assert!(
reject_option_like("tar_create", &ok).is_ok(),
"normal paths must pass, including the './-name' workaround"
);
}