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
4 changes: 2 additions & 2 deletions crates/socket-patch-core/src/vendor/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,8 @@ async fn revert_lock_fragment_splice_inner(
warnings.push(VendorWarning::new(
"vendor_lock_entry_drifted",
format!(
"{lock_file} fragment for {:?} changed since vendoring; left untouched",
rec.key
"{lock_file} fragment for {} changed since vendoring; left untouched",
rec.key_label()
),
));
}
Expand Down
2 changes: 1 addition & 1 deletion crates/socket-patch-core/src/vendor/composer_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ pub async fn revert_composer_opts(
"vendor_lock_entry_drifted",
format!(
"composer.lock entry for {} no longer points into .socket/vendor/composer/; left alone",
w.key.as_deref().unwrap_or("<unknown>")
w.key_label()
),
)),
Err(e) => {
Expand Down
2 changes: 1 addition & 1 deletion crates/socket-patch-core/src/vendor/gem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ pub async fn revert_gem_opts(
continue;
}
};
let key = w.key.as_deref().unwrap_or("<unknown>");
let key = w.key_label();
match restored {
Ok(RecordRevert::Done) => {}
Ok(RecordRevert::Drifted) => {
Expand Down
2 changes: 1 addition & 1 deletion crates/socket-patch-core/src/vendor/jvm/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ pub fn check_entry(
if !gradle::metadata_record_present(text, w) {
return Err(format!(
"upstream verification entry drifted: {}",
w.key.as_deref().unwrap_or("")
w.key_label()
));
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/socket-patch-core/src/vendor/maven_repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ pub async fn revert_maven_opts(
format!(
"{} no longer carries the vendored <repository> {}; left alone",
w.file,
w.key.as_deref().unwrap_or("<unknown>")
w.key_label()
),
)),
Err(e) => {
Expand Down
4 changes: 2 additions & 2 deletions crates/socket-patch-core/src/vendor/nuget_feed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ pub async fn revert_nuget_opts(
.iter()
.find(|c| c.kind == CONFIG_SOURCE_WIRING_KIND)
.map_or("nuget.config", |c| c.file.as_str()),
w.key.as_deref().unwrap_or("<unknown>"),
w.key_label(),
w.file
),
));
Expand Down Expand Up @@ -905,7 +905,7 @@ pub async fn revert_nuget_opts(
format!(
"{} no longer carries what vendor wrote for {}; left alone",
w.file,
w.key.as_deref().unwrap_or("<unknown>")
w.key_label()
),
)),
Err(e) => {
Expand Down
14 changes: 8 additions & 6 deletions crates/socket-patch-core/src/vendor/pypi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3332,8 +3332,7 @@ pub async fn revert_pypi_opts(
.filter(|f| !KNOWN_PYPI_FLAVORS.contains(f))
{
return RevertOutcome::failed(format!(
"unknown pypi vendor flavor {:?}; cannot revert",
Some(flavor)
"unknown pypi vendor flavor {flavor:?}; cannot revert"
));
}
let uuid_dir_rel = vendor_uuid_dir_rel("pypi", &entry.uuid)
Expand All @@ -3360,9 +3359,12 @@ pub async fn revert_pypi_opts(
match flavor {
Some(flavor) => revert_by_record_kind(entry, project_root, flavor, dry_run).await,
None => {
let other = entry.flavor.as_deref();
let other = entry
.flavor
.as_deref()
.map_or_else(|| "<none>".to_string(), |f| format!("{f:?}"));
return RevertOutcome::failed(format!(
"unknown pypi vendor flavor {other:?}; cannot revert"
"unknown pypi vendor flavor {other}; cannot revert"
));
}
}
Expand Down Expand Up @@ -6858,7 +6860,7 @@ wheels = [
outcome
.error
.as_deref()
.is_some_and(|e| e.contains("unknown pypi vendor flavor")),
.is_some_and(|e| e.contains("unknown pypi vendor flavor \"frobnicate\";")),
"{outcome:?}"
);
let mut wired = revert_entry("uv", &rel_wheel, Vec::new());
Expand All @@ -6877,7 +6879,7 @@ wheels = [
outcome
.error
.as_deref()
.is_some_and(|e| e.contains("unknown pypi vendor flavor")),
.is_some_and(|e| e.contains("unknown pypi vendor flavor \"frobnicate\";")),
"{outcome:?}"
);
}
Expand Down
30 changes: 23 additions & 7 deletions crates/socket-patch-core/src/vendor/pypi_pipenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,8 @@ pub(super) async fn revert_pipenv(
VendorWarning::new(
"vendor_lock_entry_drifted",
format!(
"{LOCK_FILE} entry for {:?} changed since vendoring; left untouched",
rec.key
"{LOCK_FILE} entry for {} changed since vendoring; left untouched",
rec.key_label()
),
)
};
Expand Down Expand Up @@ -625,7 +625,7 @@ pub(super) async fn revert_pipenv(
{
VendorWarning::new(
"vendor_lock_entry_relocked",
format!("{LOCK_FILE} entry for {:?} was removed by a relock; the vendored reference is already gone, so the record is retired", rec.key),
format!("{LOCK_FILE} entry for {} was removed by a relock; the vendored reference is already gone, so the record is retired", rec.key_label()),
)
} else {
drifted()
Expand Down Expand Up @@ -683,8 +683,8 @@ pub(super) async fn revert_pipenv(
warnings.push(VendorWarning::new(
"vendor_lock_entry_relocked",
format!(
"{LOCK_FILE} entry for {:?} was regenerated to a registry reference by a relock; the vendored reference is already gone, so the record is retired",
rec.key
"{LOCK_FILE} entry for {} was regenerated to a registry reference by a relock; the vendored reference is already gone, so the record is retired",
rec.key_label()
),
));
continue;
Expand Down Expand Up @@ -1741,6 +1741,14 @@ mod tests {
"drifted entry + unknown kind: {:?}",
outcome.warnings
);
// The ledger key reads as recorded, never as `Some("…")` (#1608).
assert!(
outcome.warnings.iter().any(|w| w
.detail
.contains("Pipfile.lock entry for default:six changed since vendoring")),
"{:?}",
outcome.warnings
);
assert_eq!(
read_lock(tmp.path()).await,
drifted,
Expand Down Expand Up @@ -2013,7 +2021,9 @@ mod tests {
};
assert_eq!(outcome.warnings[0].code, expected, "{label}");
assert!(
outcome.warnings[0].detail.contains(key),
outcome.warnings[0]
.detail
.contains(&format!("entry for {key} ")),
"{label}: {}",
outcome.warnings[0].detail
);
Expand Down Expand Up @@ -2062,7 +2072,13 @@ mod tests {
assert!(!outcome.drift_skipped(), "{:?}", outcome.warnings);
assert_eq!(outcome.warnings.len(), 1, "{:?}", outcome.warnings);
assert_eq!(outcome.warnings[0].code, "vendor_lock_entry_relocked");
assert!(outcome.warnings[0].detail.contains("docs:six"));
assert!(
outcome.warnings[0]
.detail
.contains("entry for docs:six was removed by a relock"),
"{}",
outcome.warnings[0].detail
);
assert_eq!(read_lock(tmp.path()).await, relocked_text);

// The same record while another category still routes through the
Expand Down
8 changes: 8 additions & 0 deletions crates/socket-patch-core/src/vendor/pypi_poetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,14 @@ content-hash = "4b42a89b7ff7b26511b06acdc458dbd85312e5083db8f212b017482bc68cdd01
"drifted fragment + unknown kind: {:?}",
outcome.warnings
);
// The ledger key reads as recorded, never as `Some("…")` (#1608).
assert!(
outcome.warnings.iter().any(|w| w
.detail
.contains("poetry.lock fragment for six changed since vendoring")),
"{:?}",
outcome.warnings
);
assert_eq!(
read_lock(tmp.path()).await,
drifted,
Expand Down
12 changes: 10 additions & 2 deletions crates/socket-patch-core/src/vendor/pypi_requirements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,8 @@ fn drift_warning(file: &str, rec: &WiringRecord) -> VendorWarning {
VendorWarning::new(
"vendor_revert_line_drifted",
format!(
"{file}: the vendor line for {:?} changed since vendoring; left untouched",
rec.key
"{file}: the vendor line for {} changed since vendoring; left untouched",
rec.key_label()
),
)
}
Expand Down Expand Up @@ -2354,6 +2354,14 @@ mod tests {
.warnings
.iter()
.any(|w| w.code == "vendor_revert_line_drifted"));
// The ledger key reads as recorded, never as `Some("…")` (#1608).
assert!(
outcome.warnings.iter().any(|w| w.detail.contains(
"requirements.txt: the vendor line for requirements.txt:1 changed since vendoring"
)),
"{:?}",
outcome.warnings
);
// A drifted edit (still referencing the uuid dir) also raises the
// residual-reference warning.
assert!(outcome
Expand Down
29 changes: 20 additions & 9 deletions crates/socket-patch-core/src/vendor/pypi_uv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use super::common::{
ensure_unchanged, item_get, pep508_name, pyproject_dependency_specs, record, refuse_symlinked,
};
use super::parse_memo::ParseMemo;
use super::state::{UvMeta, VendorEntry, WiringAction, WiringRecord};
use super::state::{terminal_safe, UvMeta, VendorEntry, WiringAction, WiringRecord};
use super::toml_surgery::{
balanced_span, find_unit_span, line_index, remove_exact_line, remove_substring,
remove_table_if_empty, replace_fragment, split_top_level_commas, top_level_brace_groups,
Expand Down Expand Up @@ -946,9 +946,10 @@ pub(super) async fn revert_uv(entry: &VendorEntry, root: &Path, dry_run: bool) -
VendorWarning::new(
super::LOCK_ENTRY_REMOVED_CODE,
format!(
"{} entry for {:?} no longer exists and nothing else references {needle} \
"{} entry for {} no longer exists and nothing else references {needle} \
(the dependency was removed); nothing to restore",
rec.kind, rec.key
rec.kind,
rec.key_label()
),
)
})
Expand All @@ -973,8 +974,9 @@ pub(super) async fn revert_uv(entry: &VendorEntry, root: &Path, dry_run: bool) -
warnings.push(VendorWarning::new(
"vendor_lock_entry_drifted",
format!(
"uv.lock entry for {key:?} can't be restored to match \
pyproject.toml ({reason}); left untouched"
"uv.lock entry for {} can't be restored to match \
pyproject.toml ({reason}); left untouched",
rec.key_label()
),
));
continue;
Expand All @@ -988,8 +990,8 @@ pub(super) async fn revert_uv(entry: &VendorEntry, root: &Path, dry_run: bool) -
VendorWarning::new(
"vendor_lock_entry_drifted",
format!(
"{what} fragment for {:?} changed since vendoring; left untouched",
rec.key
"{what} fragment for {} changed since vendoring; left untouched",
rec.key_label()
),
)
};
Expand Down Expand Up @@ -1180,10 +1182,11 @@ pub(super) async fn revert_uv(entry: &VendorEntry, root: &Path, dry_run: bool) -
warnings.push(VendorWarning::new(
"vendor_lock_entry_drifted",
format!(
"uv.lock entry for {canon:?} that `uv add` routed through {needle} \
"uv.lock entry for {} that `uv add` routed through {needle} \
can't be put back on the registry ({reason}); left both files \
untouched (pin it with `==` in pyproject.toml or remove it, then \
revert again)"
revert again)",
terminal_safe(&canon)
),
));
}
Expand Down Expand Up @@ -5253,6 +5256,14 @@ wheels = [
"{}",
outcome.warnings[0].detail
);
// The ledger key reads as recorded, never as `Some("…")` (#1608).
assert!(
outcome.warnings[0]
.detail
.contains("fragment for six changed since vendoring"),
"{}",
outcome.warnings[0].detail
);
let (py, lock) = read_pair(tmp.path()).await;
assert_eq!(py, tampered, "the drifted pyproject is left alone");
assert_ne!(
Expand Down
46 changes: 46 additions & 0 deletions crates/socket-patch-core/src/vendor/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,29 @@ pub struct WiringRecord {
pub new: Option<serde_json::Value>,
}

impl WiringRecord {
/// The key as user-facing warning text: verbatim, or `<unknown>` for a
/// record without one (an older or hand-written ledger). SECURITY: the
/// key comes from the committed, tamper-able state.json, so control
/// characters are escaped rather than written to the terminal.
pub fn key_label(&self) -> std::borrow::Cow<'_, str> {
match self.key.as_deref() {
None | Some("") => "<unknown>".into(),
Some(key) => terminal_safe(key),
}
}
}

/// `s` with control characters escaped, for warning text built from
/// values read out of the committed, tamper-able state.json.
pub fn terminal_safe(s: &str) -> std::borrow::Cow<'_, str> {
if s.chars().any(char::is_control) {
s.escape_debug().to_string().into()
} else {
s.into()
}
}

/// Original Cargo.lock fields removed by the path-dep surgery; not
/// recomputable offline (the checksum is the sha256 of the registry `.crate`
/// tarball, not of the extracted tree).
Expand Down Expand Up @@ -1077,6 +1100,29 @@ mod tests {

const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f";

/// Warnings name the ledger key as recorded, never in debug form
/// (`Some("default:six")`, #1608).
#[test]
fn key_label_is_plain_text() {
let mut rec = WiringRecord {
file: "Pipfile.lock".into(),
kind: "pipenv_lock_entry".into(),
action: WiringAction::Rewritten,
key: Some("default:six".into()),
original: None,
new: None,
};
assert_eq!(rec.key_label(), "default:six");
rec.key = None;
assert_eq!(rec.key_label(), "<unknown>");
rec.key = Some(String::new());
assert_eq!(rec.key_label(), "<unknown>");
rec.key = Some("default:six\x1b[2K\nforged".into());
assert_eq!(rec.key_label(), "default:six\\u{1b}[2K\\nforged");
assert_eq!(terminal_safe("six"), "six");
assert_eq!(terminal_safe("six\x1b[2K"), "six\\u{1b}[2K");
}

fn sample_entry() -> VendorEntry {
VendorEntry::new(
"npm".into(),
Expand Down
Loading