From 22c5cf3e4b614f8f033224bb87a18cdaf86308b7 Mon Sep 17 00:00:00 2001 From: vatsanbalaji-ossagent <325768530+vatsanbalaji-ossagent@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:14:04 -0400 Subject: [PATCH 1/2] chgrp: reject the :gid format for the group operand GNU chgrp only accepts a group name or numeric ID as the group operand and rejects ":1000" as an invalid group. Only --from accepts the :gid format, so parse the operand separately and add a regression test. Co-Authored-By: Claude Opus 5.5 --- src/uu/chgrp/src/chgrp.rs | 24 +++++++++++++++--------- tests/by-util/test_chgrp.rs | 12 ++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index 065b52ccae6..5b1d4263f5a 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -27,6 +27,18 @@ enum ChgrpError { impl UError for ChgrpError {} +fn parse_group(group: &str) -> Result { + // Try as group name first + match entries::grp2gid(group) { + Ok(g) => Ok(g), + // If group name lookup fails, try parsing as raw number + Err(_) => group + .parse::() + .map_err(|_| translate!("chgrp-error-invalid-group", "group" => group)), + } +} + +/// Parse the `--from` value, which also accepts the `:gid` format. fn parse_gid_from_str(group: &str) -> Result { if let Some(gid_str) = group.strip_prefix(':') { // Handle :gid format @@ -34,14 +46,7 @@ fn parse_gid_from_str(group: &str) -> Result { .parse::() .map_err(|_| translate!("chgrp-error-invalid-group-id", "gid_str" => gid_str)) } else { - // Try as group name first - match entries::grp2gid(group) { - Ok(g) => Ok(g), - // If group name lookup fails, try parsing as raw number - Err(_) => group - .parse::() - .map_err(|_| translate!("chgrp-error-invalid-group", "group" => group)), - } + parse_group(group) } } @@ -67,7 +72,8 @@ fn get_dest_gid(matches: &ArgMatches) -> UResult<(Option, String)> { if group.is_empty() { None } else { - match parse_gid_from_str(group) { + // Unlike --from, GNU does not accept the `:gid` format here. + match parse_group(group) { Ok(g) => Some(g), Err(e) => return Err(USimpleError::new(1, e)), } diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index 3f29e130a69..9dd6c7c5ce7 100644 --- a/tests/by-util/test_chgrp.rs +++ b/tests/by-util/test_chgrp.rs @@ -62,6 +62,18 @@ fn test_invalid_group() { .stderr_is("chgrp: invalid group: '__nosuchgroup__'\n"); } +#[test] +fn test_invalid_group_with_colon_prefix() { + // The `:gid` format is only accepted by --from, not for the group operand + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("file"); + let gid = getegid().as_raw(); + ucmd.arg(format!(":{gid}")) + .arg("file") + .fails_with_code(1) + .stderr_is(format!("chgrp: invalid group: ':{gid}'\n")); +} + #[test] fn test_error_1() { if !getegid().is_root() { From 2a0c5fc3fc50a7f0b6a2da9d616f187783b7093b Mon Sep 17 00:00:00 2001 From: vatsanbalaji-ossagent <325768530+vatsanbalaji-ossagent@users.noreply.github.com> Date: Tue, 13 Oct 2026 11:57:26 -0400 Subject: [PATCH 2/2] chgrp: use a numeric gid for the target group in test test_numeric_group_formats still passed ":gid" as the group operand, which is now rejected. Use the plain numeric gid instead. Co-Authored-By: Claude Opus 5.5 --- tests/by-util/test_chgrp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index 9dd6c7c5ce7..b128e64d0da 100644 --- a/tests/by-util/test_chgrp.rs +++ b/tests/by-util/test_chgrp.rs @@ -616,11 +616,11 @@ fn test_numeric_group_formats() { let new_gid = at.plus("test_file").metadata().unwrap().gid(); assert_eq!(new_gid, second_group.as_raw()); - // Test :gid format in target group + // Test numeric gid in target group (the :gid format is only valid for --from) scene .ucmd() .arg(format!("--from={}", second_group.as_raw())) - .arg(format!(":{}", first_group.as_raw())) + .arg(first_group.as_raw().to_string()) .arg("test_file") .succeeds() .no_stderr();