sort.SliceIsSorted with a strict '<' predicate treats equal neighbours
as sorted, so a completion list like [1,1] passed the order check and
the same part was assembled into the object twice: a single uploaded
5 MiB part produced a 10 MiB object. Replace the check with an explicit
strictly-increasing scan that rejects repeats with InvalidPartOrder
before anything is assembled. Gaps and lists not starting at part 1
remain legal, matching AWS semantics.
The regression test drives the real CompleteMultipartUpload handler on
both Erasure backends and asserts that rejected completions leave no
object behind and keep the upload retryable.
Closes#49
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
httpStreamResponse, streamHTTPResponse, waitForHTTPStream, and their
8k buffer pool lost their last caller when ReadMultipleHandler went
away (73ac52472). Nothing references them anywhere in the tree, and
the storage REST wire surface is untouched - these encoded a framing
no registered handler still speaks.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
ReadPartsHandler completed its keepalive stream before reporting storage failures, then tried to write an ordinary error response after the body was already owned. Clients consequently decoded the error text as msgpack and lost the real failure.
Send failures through the keepalive completion channel and mark success only after ReadParts returns cleanly, preserving the existing wire framing and error identity.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
AppendFile, DeleteVersions, and ReadFile sized memory directly from peer-controlled Content-Length or query parameters. Tiny requests could therefore reserve gigabytes before delivering a body, while negative declarations could reach make and panic.
Cap preallocation without capping accepted append bodies, grow version slices as entries decode, reject negative counts, and enforce the format-implied 5 GiB ceiling on legacy whole-file reads. Regression tests drive raw handler inputs, measure total allocations, and retain legitimate round trips.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Malformed erasure layouts can divide by zero, while negative part sizes collapse expected shard sizes to zero and make truncated data appear healthy. Boundary validation alone is insufficient because poisoned metadata may already exist on disk or arrive through local heal paths.
Reject non-positive block sizes at the sole Erasure constructor, guard the metadata arithmetic helpers and rebalance calculation, refuse negative part sizes before persistence, and make CheckParts and VerifyFile reject previously stored poison. Tests cover both shard-size implementations, construction, persistence, local verification, and the wire boundary.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Storage REST request bodies and Grid RPC frames bypass the HTTP validity middleware, allowing wire-supplied paths and malformed FileInfo values to reach xlStorage unchecked.
Wrap the remotely exposed StorageAPI with a guard that covers every path-bearing method, including nested metadata fields. Reject traversal and destructive volume-root aliases before path cleaning can erase them, and validate erasure geometry and part sizes at the same wire boundary. Keep a raw-volume check in getVolDir for peer-S3 calls that bypass the wrapper.
Reflection, fuzz, traversal, peer-S3, compatibility, and malformed-erasure tests pin the complete method surface and prove that legal object names remain accepted.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Mark trackingResponseWriter committed when Write or an effective Flush implicitly sends a 200 response. This keeps duplicate-response detection aligned with the actual writer chain while preserving no-op Flush behavior when unsupported.
Add direct and gzip-streaming regression coverage for implicit headers, Flush delegation, and suppression of a second response.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Replace bytebufferpool-backed return values with bytes.Buffer storage so
the downloaded and compressed slices remain valid after downloadBinary
returns. Close the zstd encoder on copy failure and propagate final close
errors.
Add round-trip and pool-reuse regression coverage for both returned
buffers.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Wire the preallocated pooled shard slices into parallelReader instead
of discarding them and allocating a buffer for each disk.
Keep readerToBuf as a permutation while preferred readers are reordered.
The former assignments could duplicate a buffer slot after multiple
swaps, causing concurrent writes and a possible decode stall. Add pool
aliasing and mapping regression coverage.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
hash.Checksum.AddPart returned before seeding the accumulator when the
part was empty, so a multipart object with no content at all ended up
with no checksum instead of the checksum of zero bytes. Completing such
an upload failed with XAmzContentChecksumMismatch when the client
supplied the correct object checksum, and stored an empty checksum when
it did not.
Run the type check and the first checksum seeding before the zero size
early return. Appending zero bytes still leaves an existing accumulator
unchanged, so only the all empty case changes: a zero length part
followed by content already merged correctly, because prepending no bytes
does not alter a CRC.
AddPart has a single production caller, the multipart completion path, and
its part checksum type is derived from the upload's own checksum type, so
the type check now reached for zero sized parts cannot fire there.
Add a table test over CRC32, CRC32C and CRC64NVME covering every position
an empty part can take, and an API level zero length full object upload
that exercises the persisted AppendTo/ReadCheckSums round trip.
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
CompleteMultipartUpload compared the per-part checksum taken from the
request body against the stored part checksum unconditionally, so a
client that sent only PartNumber and ETag for each part failed with
InvalidPart.
AWS S3 requires part level checksums in the completion body only for
composite checksum types. For full object types the client sends the
object level checksum in the request headers instead and does not retain
per-part values - that is the point of FULL_OBJECT. Reproduced with
boto3 1.43.58: it puts x-amz-checksum-crc32 on each UploadPart and the
full object checksum on the completion headers, but emits only ETag and
PartNumber in the completion body. A caller could only get such an upload
through by collecting the per-part checksums from the UploadPart
responses and echoing them back, which is exactly the bookkeeping
FULL_OBJECT exists to avoid and which no off-the-shelf SDK call does.
minio-go does echo them, which is why mc never hit this.
Treat the part checksum as optional when the upload declared a full
object checksum type and the client sent no part checksum at all. A part
carrying any checksum is still validated against the stored one -
including one sent under the wrong algorithm, which cannot match and is
rejected - composite uploads keep requiring a checksum for every part,
and the merged object checksum is still computed from the server stored,
upload time validated part checksums, never from client supplied values,
so integrity is unchanged.
Covered by API level tests over CRC32, CRC32C and CRC64NVME on both the
single drive and erasure backends, with guards for a wrong object
checksum, a wrong part checksum, a part checksum under another algorithm,
a mix of present and omitted part checksums, an absent object checksum,
and composite uploads still requiring every part checksum.
Fixes#31
Reported-by: Christophe Bornet <cbornet@users.noreply.github.com>
Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Replace upstream docs.min.io references with the SILO documentation domain and remove the upstream community path prefix.
Co-authored-by: waterkip <6317502+waterkip@users.noreply.github.com>
The internode storage-REST ReadMultiple endpoint (/rmpl) joined
attacker-controlled Bucket/Prefix/Files into a filesystem path with no
validation, letting a peer with internode credentials read files outside
the drive root (GHSA-xh8f-g2qw-gcm7).
ReadMultiple has had no production caller since upstream #20390 removed
the last one (listParts) in Sep 2024; multipart now uses ReadParts (/rps).
Following the upstream fix, remove the whole API instead of validating
paths: route constant and registration, server handler, REST client
wrapper, the StorageAPI/xlStorage/xlStorageDiskIDCheck methods, the
storageMetricReadMultiple metric, and the ReadMultipleReq/Resp datatypes.
Regenerated the msgp and stringer outputs; storageRESTVersion stays at v63.
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Code <claude-code@anthropic.com>
Remove the per-username LDAP STS throttle bucket and keep the limiter keyed only by source IP. A username bucket is shared across all clients and lets one source keep a known account's bucket drained with bad-password attempts, locking the legitimate user out before LDAP bind.
For trusted proxies, stop trusting the left-most forwarded address. Resolve X-Forwarded-For right-to-left, skip trusted proxy hops, reject catch-all trusted-proxy CIDRs, and intentionally ignore RFC 7239 Forwarded for this security-sensitive bucket. Document that X-Real-IP is trusted verbatim and must be overwritten by the trusted proxy, not passed through from clients.
Update focused limiter, source-IP, trusted-proxy, and LDAP config tests to cover source-only buckets, spoofed appended XFF, multi-hop trusted proxies, Forwarded fallback, catch-all rejection, and the X-Real-IP deployment contract.
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Code <claude-code@anthropic.com>
Allows the trackingResponseWriter to properly proxy flush
requests to the underlying http.Flusher. This ensures
compatibility with streaming responses.
Prevent LDAP STS reservation cancel paths from over-crediting rate-limit buckets by capping refill and refund capacity against in-flight reservations.
Add an explicit trusted-proxy allowlist for LDAP STS source bucketing, prefer clean X-Real-IP values on trusted peers, and extend tests/docs for the new behavior.
Use the socket peer address for LDAP STS per-IP rate limiting instead of the generic forwarded-header-aware helper. This keeps the security-sensitive rate-limit key from trusting spoofable X-Forwarded-For, X-Real-IP, and Forwarded headers while leaving the rest of the source-IP behavior unchanged.
Add focused regression coverage for RemoteAddr parsing, header spoofing, and peer-address bucket selection.
Track issue #28 / GHSA-9c4q-hq6p-c237 as fake CVE-2026-40028. Close the Snowball auto-extract auth gap in PutObjectExtractHandler by treating authTypeStreamingUnsignedTrailer the same as ordinary PUTs: honor X-Amz-Decoded-Content-Length, initialize newUnsignedV4ChunkedReader(), and verify the SigV4 request before any tar bytes reach untar(). This removes the forged-signature write primitive that let a single request fan out into arbitrary extracted object creation.
Add regression coverage for forged-signature Snowball unsigned-trailer writes, anonymous Snowball requests against non-public buckets, and legitimate signed Snowball extraction with trailing CRC32 trailers. Validate the new tests against the vulnerable parent and patched tree, and confirm with containerized before/after smoke runs that the exploit succeeds pre-fix, fails post-fix, and normal signed Snowball uploads still extract correctly.
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Code <claude-code@anthropic.com>
Track issue #27 / GHSA-hv4r-mvr4-25vw as fake CVE-2026-40027. Close the unsigned-trailer trust flaw that let query-string credentials skip signature verification in PutObject and PutObjectPart by moving presigned rejection and SigV4 verification into newUnsignedV4ChunkedReader(), so authTypeStreamingUnsignedTrailer can no longer silently downgrade query auth into an anonymous body read.
Add focused regression coverage for forged query-string-only unsigned-trailer PUTs and multipart uploads, mixed header/query auth rejection, and anonymous unsigned-trailer writes that remain allowed only when bucket policy explicitly permits them. Validate the new tests against the vulnerable parent and confirm with before/after live-server runs that presigned unsigned-trailer attacks are rejected while legitimate header-authenticated and policy-driven flows still work.
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Code <claude-code@anthropic.com>
Close the replication-header trust flaw that allowed ordinary PutObject and CopyObject requests to smuggle X-Minio-Replication-* headers into X-Minio-Internal-* SSE metadata and write objects into an unreadable state. Stop accepting replication-only metadata in the default extraction path, restore it only after a trusted replication write has passed ReplicateObjectAction, and tighten CopyObject by sanitizing replication-only request headers before metadata, precondition, and SSE-C source handling consume them. Also gate replica status writes on the same trusted replication path and restore replication SSE metadata in multipart and snowball upload flows so legitimate replication continues to work.
Add focused regression coverage for untrusted PUT and COPY header poisoning at the handler layer, plus helper tests for trusted vs untrusted metadata extraction and CopyObject header sanitization. Validate the new tests against both the patched tree and the vulnerable HEAD baseline, and confirm with live server before/after runs that malicious PUT/COPY requests no longer turn objects unreadable.
Co-authored-by: Codex <codex@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Prevent username enumeration in AssumeRoleWithLDAPIdentity by returning the same external STS error for unknown users and invalid passwords, while preserving LDAP infrastructure failures as upstream errors so they continue to surface as 500s and remain visible in server logs.
Add a small in-memory rate limiter for LDAP STS login attempts, keyed by source IP and normalized username, and add regression coverage for auth failure classification, throttling, and Docker-backed LDAP end-to-end flows.
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Code <claude-code@anthropic.com>
Go 1.26.1 tightens a few toolchain checks that older builds tolerated.\n\nCast aliased replication status values back to their defining type before calling the generated msgp helpers, and replace Sprintf+WriteString pairs with direct Fprintf calls where needed.\n\nThese are compatibility-only source changes to keep the cmd package building cleanly under the newer linker/toolchain.
Switch to community-maintained console fork (georgmangold/console v1.9.1)
and update dependencies accordingly. Fix go vet format directive in
grid_test.go and adapt test status code for Go 1.26 HTTP semantics.
fix(api): Don't send responses twice.
In some cases multiple responses are being sent for one request, causing
the API server to incorrectly drop connections.
This change introduces a ResponseWriter which tracks whether a
response has already been sent. This is used to prevent a response being
sent if something already has (e.g. by a preconditions check function).
Fixes#21633.
Co-authored-by: Menno Finlay-Smits <hello@menno.io>
This fixes a security issue where sub-policy attached to a service
account or STS account is not properly validated under certain "own"
account operations (like creating new service accounts). This allowed a
service account to create new service accounts for the same user
bypassing the inline policy restriction.
fix: use correct dummy ARN for claim-based OIDC provider
When listing OIDC access keys, use the correct ARN when looking up the provider configuration for the claim-based provider. Without this it was impossible to list access keys for a claim-based provider, only for a role-policy-based provider.
Fixesminio/minio#21548
`go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...` executed.
`go generate ./...` ran afterwards to keep generated.
RoleARN is a required parameter in AssumeRoleWithWebIdentity,
according to the standard AWS implementation, and the official
AWS SDKs and CLI will not allow you to assume a role from a JWT
without also specifying a RoleARN. This meant that it was not
possible to use the official SDKs for claim-based OIDC with Minio
(minio/minio#21421), since Minio required you to _omit_ the RoleARN in this case.
minio/minio#21468 attempted to fix this by disabling the validation
of the RoleARN when a claim-based provider was configured, but this had
the side effect of making it impossible to have a mixture of claim-based
and role-based OIDC providers configured at the same time - every
authentication would be treated as claim-based, ignoring the RoleARN entirely.
This is an alternative fix, whereby:
- _if_ the `RoleARN` is one that Minio knows about, then use the associated role policy
- if the `RoleARN` is not recognised, but there is a claim-based provider configured, then ignore the role ARN and attempt authentication with the claim-based provider
- if the `RoleARN` is not recognised, and there is _no_ claim-based provider, then return an error.