Skip to content

Commit c91cbd5

Browse files
committed
fix: sanitize dynamic completion values
1 parent c8bb673 commit c91cbd5

1 file changed

Lines changed: 33 additions & 6 deletions

File tree

‎src/main.rs‎

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,21 @@ mod tests {
15191519
"outer pipe should receive stdout after restoration, got: {outer_output:?}"
15201520
);
15211521
}
1522+
1523+
#[test]
1524+
fn test_sanitize_completion_value_strips_shell_metacharacters() {
1525+
let value = "$(touch /tmp/pwn); hello|world && rm -rf /";
1526+
assert_eq!(
1527+
sanitize_completion_value(value),
1528+
"touch /tmp/pwn helloworld rm -rf /"
1529+
);
1530+
}
1531+
1532+
#[test]
1533+
fn test_sanitize_completion_value_keeps_common_identifiers() {
1534+
let value = "LIN-123 team_alpha/user@example.com";
1535+
assert_eq!(sanitize_completion_value(value), value);
1536+
}
15221537
}
15231538

15241539
fn sanitize_completion_field(value: &str) -> String {
@@ -1533,6 +1548,18 @@ fn sanitize_completion_field(value: &str) -> String {
15331548
.to_string()
15341549
}
15351550

1551+
fn sanitize_completion_value(value: &str) -> String {
1552+
sanitize_completion_field(value)
1553+
.chars()
1554+
.filter(|ch| {
1555+
ch.is_ascii_alphanumeric()
1556+
|| matches!(ch, ' ' | '.' | '_' | '-' | ':' | '/' | '@' | '+')
1557+
})
1558+
.collect::<String>()
1559+
.trim()
1560+
.to_string()
1561+
}
1562+
15361563
/// Handle the context command - detect current Linear issue from git branch
15371564
async fn handle_context(
15381565
output: &OutputOptions,
@@ -1919,7 +1946,7 @@ async fn complete_teams(cache: Option<&cache::Cache>, prefix: &str) -> Result<()
19191946
{
19201947
println!(
19211948
"{}\t{}",
1922-
sanitize_completion_field(key),
1949+
sanitize_completion_value(key),
19231950
sanitize_completion_field(name)
19241951
);
19251952
}
@@ -1945,7 +1972,7 @@ async fn complete_projects(cache: Option<&cache::Cache>, prefix: &str) -> Result
19451972
if prefix.is_empty() || name.to_lowercase().starts_with(&prefix_lower) {
19461973
println!(
19471974
"{}\t{}",
1948-
sanitize_completion_field(name),
1975+
sanitize_completion_value(name),
19491976
sanitize_completion_field(state)
19501977
);
19511978
}
@@ -1991,7 +2018,7 @@ async fn complete_issues(prefix: &str) -> Result<()> {
19912018
if prefix.is_empty() || id.to_uppercase().starts_with(&prefix_upper) {
19922019
println!(
19932020
"{}\t{}",
1994-
sanitize_completion_field(id),
2021+
sanitize_completion_value(id),
19952022
sanitize_completion_field(title)
19962023
);
19972024
}
@@ -2081,7 +2108,7 @@ async fn complete_statuses(
20812108
if prefix.is_empty() || name.to_lowercase().starts_with(&prefix_lower) {
20822109
println!(
20832110
"{}\t{}",
2084-
sanitize_completion_field(name),
2111+
sanitize_completion_value(name),
20852112
sanitize_completion_field(type_)
20862113
);
20872114
}
@@ -2111,7 +2138,7 @@ async fn complete_users(cache: Option<&cache::Cache>, prefix: &str) -> Result<()
21112138
{
21122139
println!(
21132140
"{}\t{}",
2114-
sanitize_completion_field(display),
2141+
sanitize_completion_value(display),
21152142
sanitize_completion_field(name)
21162143
);
21172144
}
@@ -2137,7 +2164,7 @@ async fn complete_labels(cache: Option<&cache::Cache>, prefix: &str) -> Result<(
21372164
if prefix.is_empty() || name.to_lowercase().starts_with(&prefix_lower) {
21382165
println!(
21392166
"{}\t{}",
2140-
sanitize_completion_field(name),
2167+
sanitize_completion_value(name),
21412168
sanitize_completion_field(color)
21422169
);
21432170
}

0 commit comments

Comments
 (0)