Skip to content

fix(upload): Forward-compatible query params#6076

Open
jjbayer wants to merge 5 commits into
masterfrom
ref/location-forward-compat
Open

fix(upload): Forward-compatible query params#6076
jjbayer wants to merge 5 commits into
masterfrom
ref/location-forward-compat

Conversation

@jjbayer

@jjbayer jjbayer commented Jun 10, 2026

Copy link
Copy Markdown
Member

For the sake of forward-compatibility, add an "other" variant to the location query params enum. Instead of denylisting certain keys, like sentry_key, prefix all upload-relevant keys with upload_ such that only those get picked up by the upload service.

Fixes: INGEST-807

@linear-code

linear-code Bot commented Jun 10, 2026

Copy link
Copy Markdown

INGEST-807

Comment on lines +491 to +524
impl<'de> Deserialize<'de> for UploadParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct UploadParamsVisitor;

impl<'de> serde::de::Visitor<'de> for UploadParamsVisitor {
type Value = UploadParams;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("upload query parameters")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut upload_params = BTreeMap::new();

while let Some(key) = map.next_key::<&str>()? {
if key.starts_with("upload_") {
upload_params.insert(key.to_owned(), map.next_value()?);
} else {
map.next_value::<serde::de::IgnoredAny>()?;
}
}

Ok(UploadParams(upload_params))
}
}

deserializer.deserialize_map(UploadParamsVisitor)
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this custom parser to skip over query params that do not start with upload_ (e.g. sentry_key).

@jjbayer jjbayer marked this pull request as ready for review June 11, 2026 19:47
@jjbayer jjbayer requested a review from a team as a code owner June 11, 2026 19:47

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 74c2a68. Configure here.

let public_key = config.public_key().ok_or(Error::SigningFailed)?;
let is_valid = self.signature.verify(
self.location.as_uri().as_bytes(),
self.location.try_to_uri()?.as_bytes(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify breaks legacy signed locations

Medium Severity

verify checks the signature against bytes from Location::try_to_uri, which always serializes upload_length. Locations signed before this change used length in that string. Deserialization aliases still accept old query names, but verification rewrites the signed payload, so valid legacy upload URLs and attachment refs fail with InvalidSignature.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 74c2a68. Configure here.

pub upload_length: L,
#[serde(alias = "signature")]
pub upload_signature: String,
#[serde(flatten)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The use of #[serde(flatten)] with a custom deserializer causes query parameters like upload_length to be captured twice, leading to duplicate parameters and breaking signature verification.
Severity: CRITICAL

Suggested Fix

Modify the custom deserializer for UploadParams to explicitly ignore keys that are already handled by named fields in the parent SignedLocationQueryParams struct, such as upload_length and upload_signature. This will prevent them from being added to the other map and creating duplicates during serialization.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: relay-server/src/services/upload.rs#L477

Potential issue: The `SignedLocationQueryParams` struct uses `#[serde(flatten)]` on the
`other` field, which has a custom deserializer that captures all query parameters
prefixed with `upload_`. When a request is deserialized, parameters like `upload_length`
and `upload_signature` are captured by both their dedicated fields and, due to serde's
behavior with non-self-describing formats, also by the flattened `other` field's
deserializer. When the URI is later reconstructed for signature verification, this
results in duplicate query parameters. The reconstructed URI will not match the original
signed URI, causing signature verification to fail for all file uploads.

Also affects:

  • relay-server/src/services/upload.rs:505~517

Did we get this right? 👍 / 👎 to inform future reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant