@@ -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
15241539fn 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
15371564async 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