fix(s3): enforce presigned POST policy conditions (eq, starts-with, content-type)#2
Open
fix(s3): enforce presigned POST policy conditions (eq, starts-with, content-type)#2
Conversation
Original prompt from Matej
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…ontent-type) Co-Authored-By: Matej Snuderl <ematej.snuderl@gmail.com>
Co-Authored-By: Matej Snuderl <ematej.snuderl@gmail.com>
a70433a to
6351e75
Compare
Co-Authored-By: Matej Snuderl <ematej.snuderl@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Presigned POST uploads were only validating
content-length-rangepolicy conditions. All other conditions (bucket,key,Content-Type, etc.) were silently ignored, allowing uploads that violate the policy.This PR replaces the narrow
validateContentLengthRangemethod withvalidatePolicyConditions, which enforces:{"bucket": "X"},{"key": "X"},{"Content-Type": "X"}eq:["eq", "$Content-Type", "image/png"]starts-with:["starts-with", "$key", "uploads/"]content-length-range:["content-length-range", min, max](existing, preserved)Policy violations return
403 AccessDeniedwith AWS-compatible error messages:Discovered via seadn#1921 — a unit test expects S3 to reject presigned POST uploads when Content-Type doesn't match the policy.
Type of change
fix:)AWS Compatibility
Incorrect behavior: Floci accepted presigned POST uploads regardless of policy conditions (except content-length-range). Real AWS S3 rejects with
403 AccessDeniedwhen any condition is not met.Corrected behavior: Now matches AWS S3 — mismatched
Content-Type,key,bucket, or failedstarts-withprefix checks return 403.Updates since last revision
Policy JSON is now parsed via Jackson
ObjectMapper.readTree()→JsonNode, matching the pattern used elsewhere in the codebase (e.g.AwsJsonController). Removed all hand-rolled string manipulation helpers (findMatchingBracket,skipWhitespace,unquote,splitJsonArray).All error-path tests assert exact XML error responses via XPath instead of
containsString:contentType("application/xml")hasXPath("/Error/Code", equalTo(...))hasXPath("/Error/Message", equalTo(...))This applies to the 4 new policy-enforcement tests and the 4 pre-existing error tests (
EntityTooLarge,InvalidArgument×2,NoSuchBucket).Things to review carefully
["eq", ...]array-form — the tests cover object-form exact match ({"Content-Type": "X"}→validateExactMatchCondition) andstarts-witharray-form, but not the["eq", "$field", "value"]array syntax which exercises a different code path (validateArrayCondition'seqbranch).fields.get("Content-Type")depends on the multipart form field name matching exactly. This matches AWS behavior but is worth verifying against real SDK-generated policies.asLong()for content-length-range bounds —JsonNode.asLong()returns0for non-numeric nodes. If a malformed policy somehow has string values for min/max, the range check would silently use0. Real SDK-generated policies always emit numeric values here.Checklist
./mvnw testpasses locallyLink to Devin session: https://app.devin.ai/sessions/06b7e28f3e664783b17ed18060e5c267
Requested by: @Meemaw