xmtp_proto: bump to xmtp/proto XIP-82 revisions (typed invite primitives) + adapt invite::payload#3751
xmtp_proto: bump to xmtp/proto XIP-82 revisions (typed invite primitives) + adapt invite::payload#3751tylerhawkes wants to merge 1 commit into
Conversation
ApprovabilityVerdict: Needs human review Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
51ada44 to
a96dde2
Compare
| fn validate_https_url(raw: &str) -> Result<(), InvitePayloadError> { | ||
| let parsed = | ||
| url::Url::parse(raw).map_err(|e| InvitePayloadError::InvalidHttpsUrl(e.to_string()))?; | ||
| if parsed.scheme() != "https" { | ||
| return Err(InvitePayloadError::InvalidHttpsUrl(format!( | ||
| "scheme must be https (got {})", | ||
| parsed.scheme() | ||
| ))); | ||
| } | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
🟢 Low invite/payload.rs:134
validate_https_url accepts URLs with an empty authority like https:///blob because it only checks the scheme, not that a host is present. These URLs parse successfully but provide no fetch target, so the joiner cannot resolve the service pointer. Consider requiring parsed.host_str().is_some() to reject authority-less URLs.
fn validate_https_url(raw: &str) -> Result<(), InvitePayloadError> {
let parsed =
url::Url::parse(raw).map_err(|e| InvitePayloadError::InvalidHttpsUrl(e.to_string()))?;
if parsed.scheme() != "https" {
return Err(InvitePayloadError::InvalidHttpsUrl(format!(
"scheme must be https (got {})",
parsed.scheme()
)));
}
+ if parsed.host_str().is_none() {
+ return Err(InvitePayloadError::InvalidHttpsUrl(
+ "URL must have a host".to_string(),
+ ));
+ }
Ok(())
}🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @crates/xmtp_mls_common/src/invite/payload.rs around lines 134-144:
`validate_https_url` accepts URLs with an empty authority like `https:///blob` because it only checks the scheme, not that a host is present. These URLs parse successfully but provide no fetch target, so the joiner cannot resolve the service pointer. Consider requiring `parsed.host_str().is_some()` to reject authority-less URLs.
a96dde2 to
bd245bf
Compare
|
Claude finished @tylerhawkes's task —— View job Code Review - 2026-06-11 23:42 UTCReview SummaryI've reviewed the XIP-82 proto bump and invite payload adaptation. The changes are well-structured and follow good defensive programming practices. Here are my findings: ✅ Strengths
🔍 Issues Found1. URL Host Validation Gap (already noted by macroscopeapp) The Suggested fix: if parsed.host_str().is_none() {
return Err(InvitePayloadError::InvalidHttpsUrl(
"URL must have a host".to_string(),
));
}2. Write-Once Tag Preservation Incomplete (acknowledged in code) In Risk: When the external-commit join path lands in the stack, the Update arm could inadvertently clear write-once tags, which validators would reject. Recommendation: Add a TODO or tracking issue reference at line 68 to ensure this gets addressed when #3674 lands. 📝 Minor Observations
🎯 VerdictApprove with minor recommendation: The code quality is high and the adaptation is correct for this branch. The URL validation gap should be addressed (already flagged by macroscopeapp), and I recommend adding a tracking reference for the tag preservation issue to ensure it's handled when the dependent PRs land. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3751 +/- ##
==========================================
- Coverage 84.45% 84.45% -0.01%
==========================================
Files 408 408
Lines 59772 59869 +97
==========================================
+ Hits 50483 50561 +78
- Misses 9289 9308 +19 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
bd245bf to
bb4f688
Compare
…ves) + adapt invite::payload Regenerates xmtp_proto from xmtp/proto#336 (typed SymmetricKey / GroupStateHash / ServicePointer newtypes, ExternalCommitPolicyV1 max_uses + refresh_pointers, EncryptedGroupInfoBlobV1 AAD + effective-expiry semantics, GroupMembershipEntry.V1 admitted_via_external_group_id). Consuming changes kept to what main already contains: - invite::payload rewritten for the typed shape: symmetric_key is a SymmetricKey submessage (MissingSymmetricKey + material-length checks), service_pointer is an optional ServicePointer (absent = application-resolved; present-but-empty oneof fails closed; https_url parsed and scheme-checked via url). New https_service_pointer / opaque_service_pointer / validate_service_pointer helpers. - GroupMembershipEntry.V1 construction sites gain the new admitted_via_external_group_id field, empty everywhere: the migrator's synthesized entries are all Welcome/legacy members (absent is permanently correct), and the membership-update rewrite path documents that tag preservation MUST land together with the validator's write-once enforcement in the external-commit stack — nothing on main can set the tag yet, so empty is correct today. The rest of the QR-invite stack (#3671 -> #3666 -> #3667 -> #3668 -> #3673 -> #3674) rebases onto this and picks up the new fields layer by layer.
bb4f688 to
650ba97
Compare
What
Bumps
xmtp_prototo xmtp/proto#336 (the XIP-82 review revisions: typed invite primitives,max_uses,refresh_pointers, blob AAD + effective-expiry semantics, theadmitted_via_external_group_idmembership tag) and adapts the code already on main to the new wire shape. New behavior (AAD encryption, validators, policy fields) lands layer-by-layer in the open QR-invite stack, which rebases onto this PR: #3671 → #3666 → #3667 → #3668 → #3673 → #3674.invite::payload(merged in #3670, adapted here)symmetric_keyis now aSymmetricKeysubmessage:validatedistinguishes absent (MissingSymmetricKey— absence is only a legal encoding on the policy component, never the payload) from wrong-lengthmaterial.service_pointeris now an optionalServicePointer: absent = application-resolved (first-party scanner with a baked-in endpoint; the QR carries no fetch target), present requires exactly onelocationvariant (present-but-empty fails closed), andhttps_urllocations are parsed and scheme-checked (urlcrate, already a workspace dep).https_service_pointer/opaque_service_pointer/validate_service_pointer;build_payloadtakes the typed pointer.admitted_via_external_group_idconstruction sitesAll set to empty (= absent on the wire), which is correct on this branch because nothing can write the tag yet (the external-commit join path doesn't exist until #3674):
migration.rs): synthesized entries are all Welcome/legacy members — absent is permanently correct for them.update_group_membership.rs(encode_membership_entry): the Update arm rewrites entries from scratch, so once joins can tag entries this must carry the existing tag through (the tag is write-once; validators will reject member commits that clear it). That preservation deliberately lands together with the validator's write-once enforcement in the stack, so the two halves are reviewable as one unit — documented in a comment at the site.Verification
cargo check --lockedclean;just lint-rustclean.cargo nextest -p xmtp_mls_common: 806 passed. Touchedxmtp_mlsmodule tests (app_data / membership): 149 passed.🤖 Generated with Claude Code
Note
Bump
xmtp_prototo XIP-82 and adapt invite payload to typed primitivesSymmetricKey,GroupStateHash,ServicePointer,ExternalInvitePayloadV1, andExternalCommitPolicyV1message types in xmtp.mls.message_contents.rs.Option<ServicePointer>inbuild_payload, wrap symmetric key bytes in nestedSymmetricKey.material, and validate https URLs and pointer presence.InvitePayloadErrorwithMissingSymmetricKey,EmptyServicePointer, andInvalidHttpsUrlvariants for finer-grained validation failures.admitted_via_external_group_idfield (defaulting to empty bytes) toGroupMembershipEntry.V1across membership construction, migration, and sync paths.Macroscope summarized 650ba97.