GHSA-FQF6-GXHH-2XHW: Root-Cause Fix for Backup Activation on --suffix
Summary
The patch corrects a logic gap in uutils coreutils backup handling: specifying --suffix without --backup now activates backup mode, defaulting to Existing or honoring VERSION_CONTROL. This aligns behavior with expected GNU-style semantics and prevents silent overwrite/data loss in cp, mv, ln, and install when a destination already exists.
Analysis
Vulnerability
GHSA-FQF6-GXHH-2XHW describes a behavioral mismatch in uutils coreutils where providing --suffix alone did not enable backup mode. As a result, commands such as cp, mv, ln, and install could overwrite or replace an existing destination without creating the expected backup artifact, causing silent data loss. The issue is rooted in backup-mode resolution: suffix selection was treated as a passive formatting parameter instead of a signal that backup behavior should be active.
The patch source in PR #9741 shows that the vulnerable path failed to derive a backup mode when only --suffix was present. This diverged from expected utility semantics where a custom suffix implies backup creation, with mode defaulting to existing unless overridden by VERSION_CONTROL.
} else if matches.contains_id(arguments::OPT_SUFFIX) {
// Suffix option is enough to determine mode even if --backup is not set.
// If VERSION_CONTROL is not set, the default backup type is 'existing'.
if let Ok(method) = env::var("VERSION_CONTROL") {
match_method(&method, "$VERSION_CONTROL")
} else {
Ok(BackupMode::Existing)
}
Patch
The patch updates determine_backup_mode in src/uucore/src/lib/features/backup_control.rs so that presence of --suffix alone is sufficient to activate backup mode. If VERSION_CONTROL is set, its value is parsed and used; otherwise the code defaults to BackupMode::Existing. This is a direct correction to the decision logic rather than a downstream mitigation.
The change is reinforced with unit tests for backup-mode derivation and integration tests across affected utilities. Added tests verify that:
cpcreatesdestination + suffixwhen only--suffixis specified.mvpreserves the overwritten destination as a backup under the custom suffix.lncreates a backup of the replaced link/target path under suffix-only invocation.installalso honors suffix-only backup activation.- Environment-driven mode selection via
VERSION_CONTROLstill applies when--suffixis used without--backup.
This broadens coverage beyond the core parser and materially reduces regression risk. The relevant implementation and tests are visible in the patch review.
Review
Pros
- The fix addresses the actual control-flow defect in backup-mode determination rather than adding command-specific workarounds.
- Behavior now matches the advisory's expected semantics:
--suffixis treated as an implicit request for backup creation. - The fallback to
BackupMode::Existingis explicit and source-grounded, while still honoringVERSION_CONTROLwhen present. - Test coverage spans both unit-level mode resolution and end-to-end utility behavior for
cp,mv,ln, andinstall. - The integration tests validate the security-relevant outcome: the original destination content survives in a backup file instead of being silently discarded.
Cons
- The patch summary does not show whether all utilities sharing backup logic are covered; review confidence is strongest for the explicitly tested commands.
- The diff excerpt focuses on positive-path behavior and does not demonstrate additional negative tests for malformed
VERSION_CONTROLvalues in the suffix-only path. - Because this is a semantics-alignment fix, downstream scripts that accidentally relied on the old non-backup behavior may observe changed filesystem side effects, though that change is desirable from a safety perspective.
Verdict
Root-cause.
This patch corrects the central state-resolution bug that caused suffix-only invocations to bypass backup activation. The implementation change is minimal but semantically complete: it moves backup enablement into the shared decision point and applies the documented defaulting behavior. The accompanying tests demonstrate that the fix propagates to affected file-manipulation commands and preserves overwritten data as intended. Based on the advisory at GitHub Security Advisory and the code changes in PR #9741, this is a solid root-cause remediation rather than a bandaid or partial fix.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- Unzip.go
This hands-on defensive lab focuses on Zip Slip-style unsafe file extraction and overwrite risks, which is closely related to the advisory’s theme of unexpected file replacement and missing safety checks. It helps build the habit of enforcing safe destination handling before writes occur.
- Untar.py
Although in Python, this lab trains the same core defensive pattern relevant here: preventing filesystem operations from writing where developers did not intend. It is useful for learning how to validate archive paths and avoid silent overwrite-style outcomes caused by unsafe file handling logic.
- Path Traversal.go
This lab covers path traversal leading to unintended file access or writes. That maps well to the uutils backup-logic issue because both involve unsafe filesystem behavior when protective expectations are not enforced. It is a practical defensive exercise in constraining file operations and validating paths before modification.