source-cooperative / source-cooperative/data.source.coop

Versioned reads (?versionId=) are classified as writes and denied to anonymous callers

Open Beginner friendly
#216 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.