Skip to content

Commit 9e18d2e

Browse files
committed
Sort metadata entries when verifying builds.
1 parent 54ed47b commit 9e18d2e

1 file changed

Lines changed: 50 additions & 2 deletions

File tree

  • cmd/soroban-cli/src/commands/contract

cmd/soroban-cli/src/commands/contract/verify.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -728,9 +728,21 @@ fn build_container_command(
728728
meta: &ExtractedMetadata,
729729
supports_locked: bool,
730730
) -> (Vec<String>, Vec<String>) {
731+
// Replay the recorded options in a canonical (sorted) order so the rebuild
732+
// command — and the `bldopt`/`--meta` metadata it re-records — doesn't
733+
// depend on the order they happened to be recorded in. SEP-58 requires the
734+
// metadata entries be sorted, so sorting here rebuilds to that canonical
735+
// form even when the original recorded them out of order (a non-conformant
736+
// origin). Only `--meta` order affects the WASM's metadata section, but
737+
// sorting every option is harmless and keeps the replayed command
738+
// deterministic. Mirrors the sort `contract build` applies when writing the
739+
// section.
740+
let mut bldopts = meta.bldopts.clone();
741+
bldopts.sort();
742+
731743
let mut forwarded: Vec<String> = Vec::new();
732744
let mut env: Vec<String> = Vec::new();
733-
for o in &meta.bldopts {
745+
for o in &bldopts {
734746
// Every recorded bldopt is shell-escaped at the source (see
735747
// `build_forwarded_args` in verifiable.rs) so it's valid shell on its
736748
// own — e.g. `--meta=source_repo='github:foo'` or `--env=B='a b'`. The
@@ -756,7 +768,7 @@ fn build_container_command(
756768
source_uri: meta.source_uri.clone(),
757769
source_sha256: meta.source_sha256.clone(),
758770
};
759-
let metadata = verifiable::build_metadata_args(&meta.bldimg, &ids, &meta.bldopts);
771+
let metadata = verifiable::build_metadata_args(&meta.bldimg, &ids, &bldopts);
760772

761773
// When the image supports it, `--locked` is forced — even if the original
762774
// somehow lacked it (a non-conformant build) — so the verifier insists on a
@@ -1131,6 +1143,42 @@ mod tests {
11311143
.any(|w| w[0] == "--meta" && w[1] == "bldopt=--env=A=1"));
11321144
}
11331145

1146+
#[test]
1147+
fn build_container_command_sorts_replayed_bldopts() {
1148+
// Recorded in a deliberately unsorted order; the replayed command (and
1149+
// the `bldopt=` meta it re-records) must come out sorted so the rebuilt
1150+
// metadata is canonical regardless of the original's recording order.
1151+
let meta = ExtractedMetadata {
1152+
bldimg: good_bldimg(),
1153+
source_uri: Some("https://github.com/foo/bar".to_string()),
1154+
source_sha256: Some("b".repeat(64)),
1155+
bldopts: vec![
1156+
"--meta=zebra=1".to_string(),
1157+
"--meta=apple=2".to_string(),
1158+
"--meta=mango=3".to_string(),
1159+
],
1160+
};
1161+
let (cmd, _env) = build_container_command(&meta, false);
1162+
1163+
// The re-recorded `bldopt=` meta values appear in sorted order.
1164+
let bldopt_metas: Vec<&String> = cmd
1165+
.windows(2)
1166+
.filter(|w| w[0] == "--meta" && w[1].starts_with("bldopt="))
1167+
.map(|w| &w[1])
1168+
.collect();
1169+
let mut sorted = bldopt_metas.clone();
1170+
sorted.sort();
1171+
assert_eq!(bldopt_metas, sorted, "bldopt metas must be sorted: {cmd:?}");
1172+
assert_eq!(
1173+
bldopt_metas,
1174+
vec![
1175+
"bldopt=--meta=apple=2",
1176+
"bldopt=--meta=mango=3",
1177+
"bldopt=--meta=zebra=1",
1178+
]
1179+
);
1180+
}
1181+
11341182
#[test]
11351183
fn build_container_command_unescapes_quoted_meta_bldopt() {
11361184
// Recorded bldopts are shell-escaped at the source, so a `--meta` value

0 commit comments

Comments
 (0)