source-cooperative / source-cooperative/data.source.coop
Versioned reads (?versionId=) are classified as writes and denied to anonymous callers
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 24
- Forks
- 6
- Avg merge
- 1h 32m
- Merged PRs (30d)
- 1
Description
Summary
A version-scoped read — GET /{account}/{product}/{key}?versionId=X — is classified as a write. Anonymous callers are denied outright, including on fully public products; authenticated callers additionally need the product write permission and a signable, non-read-only connection.
Cause
is_write_action exempts only three actions:
https://github.com/source-cooperative/data.source.coop/blob/main/src/authz.rs#L20-L25
pub(crate) fn is_write_action(action: Action) -> bool {
!matches!(
action,
Action::GetObject | Action::HeadObject | Action::ListBucket
)
}
But multistore's action enum already contains a fourth read-only variant, Action::GetObjectVersion (multistore-0.7.2/src/types.rs:200), and real traffic routes into it:
S3Operation::GetObject { version: None, .. } => Action::GetObject,
S3Operation::GetObject { version: Some(_), .. } => Action::GetObjectVersion,
So a versioned read falls through the denylist and is treated as a mutation. decide_backend_auth then denies it at the subject_present check for anonymous callers, and at the write permission check for authenticated ones.
Impact
A hard 403 on a read of public data whenever a client passes versionId. The denylist is fail-safe in direction, but its list of reads is stale relative to the enum it denylists against — so this is not a hypothetical future action, it is a live one.
Suggested fix
Add Action::GetObjectVersion to the exempted set, and add coverage to tests/authz.rs (reads_are_not_writes / mutations_are_writes currently cover neither direction for it).
Worth considering a compile-time guard so a newly added upstream action forces a decision here rather than defaulting silently — e.g. an exhaustive match over Action instead of a negated matches!.
Notes
Found while auditing the ADRs in #115 against the implementation.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/authz.rs, then inspect the Action enum in multistore-0.7.2/src/types.rs and the routing from versioned S3 operations. Update the read/write classification and add coverage in tests/authz.rs, especially reads_are_not_writes and mutations_are_writes. Done means public versioned reads are authorized as reads while mutation checks remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- authorization
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100