Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
aab

Check warning on line 1 in .github/actions/spelling/expect.txt

View workflow job for this annotation

GitHub Actions / Check Spelling

Skipping `.github/actions/spelling/expect.txt` because it seems to have more noise (381) than unique words (1) (total: 382 / 1). (noisy-file)
AAFFBB
aarch
abe
Expand Down Expand Up @@ -207,6 +207,7 @@
opencode
opensource
parseable
peekable
PERCPU
pgpkey
pgrep
Expand Down Expand Up @@ -378,4 +379,4 @@
xsi
xxxx
xxxxxxxx
xxxxxxxxxxx

Check warning on line 382 in .github/actions/spelling/expect.txt

View workflow job for this annotation

GitHub Actions / Check Spelling

Missing newline at end of file (no-newline-at-eof)
Expand Down
5 changes: 4 additions & 1 deletion proxy_agent/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub struct User {

const UNDEFINED: &str = "undefined";
const EMPTY: &str = "empty";
const MAX_CMD_ARGS: usize = 4;

async fn get_user(
logon_id: u64,
Expand Down Expand Up @@ -112,10 +113,12 @@ fn get_process_info(process_id: u32) -> (PathBuf, String) {
let cmdline_path = format!("/proc/{}/cmdline", process_id);
let process_cmd_line = match std::fs::read(&cmdline_path) {
Ok(bytes) => {
// cmdline is null-separated, convert to space-separated string
// cmdline is null-separated; take only the first few arguments
// to avoid capturing credentials in later args
bytes
.split(|&b| b == 0)
.filter(|s| !s.is_empty())
.take(MAX_CMD_ARGS)
.map(|s| String::from_utf8_lossy(s).into_owned())
.collect::<Vec<String>>()
.join(" ")
Expand Down
72 changes: 71 additions & 1 deletion proxy_agent/src/proxy/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,46 @@ pub fn get_process_cmd(handler: isize) -> Result<String> {
std::slice::from_raw_parts(cmd_buffer.Buffer, (cmd_buffer.Length / 2) as usize)
});

Ok(cmd)
// Only keep the first few arguments to avoid capturing credentials
// that may appear in later command-line arguments
Ok(truncate_cmd_args(&cmd, super::MAX_CMD_ARGS))
}

/// Truncate a command line string to at most `max_args` arguments,
/// respecting double-quoted strings that may contain whitespace.
fn truncate_cmd_args(cmd: &str, max_args: usize) -> String {
let mut args = Vec::new();
let mut chars = cmd.chars().peekable();

while args.len() < max_args {
// Skip whitespace between arguments
while chars.peek().is_some_and(|c| c.is_whitespace()) {
chars.next();
}
if chars.peek().is_none() {
break;
}

let mut arg = String::new();
if chars.peek() == Some(&'"') {
// Quoted argument — consume until closing quote
arg.push(chars.next().unwrap()); // opening "
for c in chars.by_ref() {
arg.push(c);
if c == '"' {
break;
}
}
} else {
// Unquoted argument — consume until whitespace
while chars.peek().is_some_and(|c| !c.is_whitespace()) {
arg.push(chars.next().unwrap());
}
}
args.push(arg);
}

args.join(" ")
}

#[allow(dead_code)]
Expand Down Expand Up @@ -379,4 +418,35 @@ mod tests {
);
assert!(!cmd.is_empty(), "process cmd should not be empty");
}

#[test]
fn truncate_cmd_args_tests() {
// no arguments
let result = super::truncate_cmd_args("", 4);
assert_eq!(result, "", "empty input should return empty string");

// fewer than max args
let result = super::truncate_cmd_args("app.exe arg1", 4);
assert_eq!(
result, "app.exe arg1",
"should return all args when fewer than max"
);

// exactly max args
let result = super::truncate_cmd_args("app.exe arg1 arg2 arg3 --secret=password", 4);
assert_eq!(
result, "app.exe arg1 arg2 arg3",
"should truncate to first 4 args"
);

// more than max args and quoted arg with spaces
let result = super::truncate_cmd_args(
r#""C:\Program Files\app.exe" arg1 "arg with spaces" arg3 --secret=password"#,
4,
);
assert_eq!(
result, r#""C:\Program Files\app.exe" arg1 "arg with spaces" arg3"#,
"should treat quoted strings with whitespace as single args"
);
}
}
Loading