Azure / Azure/azure-sdk-for-rust
BlobClient silently aliases ./key to key, allowing unintended overwrite
- Dominant language
- Rust
- Stars
- 884
- Forks
- 365
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 111
Description
## Crate Name
`azure_storage_blob`
## Crate Version
`1.0.0`
Related resolved dependencies:
- `azure_core 1.0.0`
- `typespec_client_core 1.0.0`
- SDK transport: `reqwest 0.13.4`
## Description
Creating a Blob client with a blob name containing an independent dot segment does
not preserve object identity against Azure Blob Storage. A write through
`blob_container_client("bucket").blob_client("./key")` succeeds, but the service
stores the content under `key`. If `key` already exists, its content is silently
overwritten.
This is data-loss behavior for applications that treat Blob names as opaque strings
or translate from an object API where `./key` and `key` are distinct keys. The SDK
does not reject the name, report an identity conflict, or document that these two
inputs address the same resource.
A local transport-capture test shows that the SDK keeps the inputs distinct before
the request leaves the process:
| Blob client input | Observed HTTP path |
| --- | --- |
| `key` | `/bucket/key` |
| `./key` | `/bucket/.%2Fkey` |
The live SDK path nevertheless aliases to `key`. An independent raw HTTPS REST probe
also reproduces the service-side behavior with `./key`, `%2E/key`, `.%2Fkey`, and
`%2E%2Fkey`: each PUT returns 201, the dot-segment name is absent from List Blobs,
and GET of the alias returns the replacement bytes. A literal-percent control and a
consecutive-slash control remain distinct.
The raw REST result means this may require coordination with the Azure Storage service
rather than only changing SDK URI serialization. We are filing against the SDK because
the public `blob_client(blob_name)` API accepts the value and the resulting successful
write can overwrite a different blob without warning. Please clarify the supported
contract and either preserve identity, reject the unsafe name before I/O, or route this
to the owning Storage service component.
Expected behavior: one of the following, explicitly documented and consistent across
operations:
1. `./key` is stored and addressable as a blob distinct from `key`; or
2. the SDK/service rejects the request before modifying `key`.
Actual behavior: upload reports success while replacing `key`.
Environment:
- Linux
- Rust Azure SDK GA crates listed above
- Azure Blob REST service version requested by the independent probe: `2025-11-05`
- Path-style Blob endpoint over HTTPS
- Reproduced with single Put Blob and block upload/commit paths
No credentials, signatures, SAS tokens, account identifiers, or response bodies are
included here. Correlated Azure request IDs can be provided privately to maintainers.
## Steps to Reproduce
Use a dedicated disposable container and distinct payloads. The following focuses on
the SDK API shape; use the repository's normal credential construction for `client`.
```rust
use azure_core::http::{Body, NoFormat, RequestContent};
use azure_core::stream::BytesStream;
use azure_storage_blob::BlobServiceClient;
use bytes::Bytes;
async fn put(
client: &BlobServiceClient,
container: &str,
name: &str,
data: Bytes,
) -> azure_core::Result<()> {
let length = data.len() as u64;
let content: RequestContent =
Body::SeekableStream(Box::new(BytesStream::new(data))).into();
client
.blob_container_client(container)
.blob_client(name)
.block_blob_client()
.upload(length, content, None)
.await?;
Ok(())
}
// In a new disposable container:
put(&client, container, "key", Bytes::from_static(b"baseline")).await?;
put(&client, container, "./key", Bytes::from_static(b"replacement")).await?;
// Observe:
// - both uploads succeed;
// - List Blobs contains `key`, but not `./key`;
// - downloading `key` returns `replacement`.
```
The full diagnostic uses 257-byte deterministic bodies and verifies byte equality,
not only listing output. It also checks that the container did not exist before the
run and deletes the run-owned container afterward.
## Checklist
- [x] I will follow the Code of Conduct.
- [x] I searched open and closed issues in `Azure/azure-sdk-for-rust` for Blob
dot-segment/path-normalization reports and found no match on 2026-09-10.
- [x] The provided reproduction is minimal and does not require the original S3 proxy.
## Maintainer Questions
1. Is a dot segment in `blob_client(blob_name)` supported as literal Blob name data?
2. If not, should `azure_storage_blob` validate and reject it to prevent alias overwrite?
3. Is `/bucket/.%2Fkey` the intended SDK wire representation for `./key`?
4. Which team owns the successful 201 alias resolution observed with raw REST requests?
5. Are other operations guaranteed to resolve the same alias, including Get Properties,
Delete Blob, Put Block, Put Block List, copy source/destination, and version access?
Contributor guide
Assessment
This issue has not been assessed yet.