source-cooperative / source-cooperative/data.source.coop
Harden user-created data connections: federated-credential scoping and connection validation
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 24
- Forks
- 6
- Avg merge
- 1h 32m
- Merged PRs (30d)
- 1
Description
Hardening work for user-created ("bring your own bucket") data connections, ahead of granting create_data_connections more broadly.
Threat model and analysis are in a private security advisory: GHSA-2rjm-4mm8-gj29. Please read it before picking up an item — the ordering below is not arbitrary, and the first item gates the value of several others.
Spans three repos. Checkboxes note where each lands.
Federated-credential scoping
-
Key the OIDC credential cache on
{role_arn}|{subject}rather than the role ARN alone —AwsBackendAuth::resolve_aws. (upstream:developmentseed/multistore)
Include the ARN as well as the subject: a connection'srole_arncan change while its ID, and therefore its subject, stays fixed.|cannot occur in an ARN, so it is a safe delimiter. This restores the behaviourdocs/plans/2026-06-02-federated-backend-auth-design.mdstep 7 already specifies.
⚠️ The same design doc calls for single-flight around AssumeRole. Do not implement it as an isolate-shared async mutex — see #197. Refresh-ahead is safe. -
Render the OIDC subject with scope derived from
owner(this repo)- unowned (platform-managed) →
scv1:conn:{id}— cardinality 1, no change from today - owned (account-created) →
scv1:conn:{id}:product:{account}/{product}
No
subject_scopeschema field — the contract is already documented onownerinsource.coop/src/types/data-connection.ts. Uniform product-scope is explicitly ruled out by the design doc (line 180) on cache-cardinality grounds. Needsowneradded toDataConnectioninsrc/source_api/types.rs, and account/product threaded intoapply_backend_auth; both are already in scope atregistry.rs:66. - unowned (platform-managed) →
-
Fix the documented-vs-emitted subject mismatch.
DataConnectionForm.tsx:196tells customers to pinscv1:conn:{id}:*, but the proxy emits barescv1:conn:{id}, which an IAMStringLikeofscv1:conn:{id}:*does not match. Implementing the item above makes the existing UI text correct; until then anyone who followed it has a role the proxy cannot assume. (this repo +source.coop) -
Condition the platform role's trust policy on subject structure —
deploy/lib/data-proxy-construct.ts. Trial against live IAM in staging before relying on it as a boundary. (source.coop)
Connection validation
- Constrain
prefix_templateandbase_prefixfor owned connections. Both are barez.string()today. Force the canonical{{repository.account_id}}/{{repository.repository_id}}/(or at minimum require theaccount_idtoken), and normalizebase_prefix— reject leading/,.., and empty segments. (source.coop) - Reject platform-owned buckets and ARNs on non-admin connections. One
isPlatformResource(details, authentication)predicate, called from bothcreateDataConnectionandupdateDataConnection. Fails loudly at create time rather than opaquely at request time. (source.coop) - Freeze
details.bucket,base_prefix, andauthenticationonce a connection is referenced by a product mirror (or force re-verification on change).listProductsByConnectionIdalready exists for the delete guard. Without this, creation-time validation is bypassable by editing after approval. (source.coop)
Defence in depth
- Correct the session-policy claim in
deploy/lib/data-proxy-construct.ts:58-59, which describes a per-request STS session policy that is not implemented. Worth doing immediately and independently — it currently misleads anyone reasoning about the blast radius. (source.coop) - Send a per-request STS session policy scoped to
arn:aws:s3:::{bucket}/{resolved_prefix}*, moving prefix confinement from application logic to IAM. Needs aPolicyfield upstream. (upstream + this repo) - Prove bucket ownership before a connection is usable — nonce-object challenge/response at
{base_prefix}.source-coop-verificationfor all providers; fors3_web_identity_role, require the customer's trust policy to pin their per-connection subject and verify by attempting the assume-role at creation (depends on the subject-rendering item). (source.coop) - CloudTrail alarm on
AssumeRoleWithWebIdentityagainst the platform role with an unexpectedsub. (source.coop)
Known residual
Even with the above, all products in a platform-managed bucket share one bucket-wide credential, so cross-account prefix confinement there remains application logic rather than IAM. Candidate follow-up: give the platform connection account scope plus a session policy on {base_prefix}{account}/* — cardinality becomes ~per-active-account instead of per-product. Prerequisite: confirm the platform connection's prefix_template leads with {{repository.account_id}}.
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 with GHSA-2rjm-4mm8-gj29 and docs/plans/2026-06-02-federated-backend-auth-design.md, then trace registry.rs:66, src/source_api/types.rs, DataConnectionForm.tsx:196, and deploy/lib/data-proxy-construct.ts. Map the owner and account/product flow before selecting one independently tracked checkbox. Done requires the stated validation, subject, IAM, or policy behavior plus tests or staging verification where the issue calls for it.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, rust, typescript
- Domain
- backend-api-design, cloud, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100