Skip to content

ECOPROJECT-4722 | fix: Add missing organization check to GetSourceDownloadURL endpoint#1218

Merged
AvielSegev merged 1 commit into
kubev2v:mainfrom
ronenav:4722-missing-org-check
Jun 7, 2026
Merged

ECOPROJECT-4722 | fix: Add missing organization check to GetSourceDownloadURL endpoint#1218
AvielSegev merged 1 commit into
kubev2v:mainfrom
ronenav:4722-missing-org-check

Conversation

@ronenav

@ronenav ronenav commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

The GET /api/v1/sources/{id}/image-url endpoint was missing authorization checks, allowing any authenticated user to download OVA images from sources they don't own. This is a high-priority security vulnerability as OVA files
contain:

  • Long-lived agent JWT tokens (90-day lifetime)
  • Proxy and network configuration
  • SSH keys and certificates

This fix adds the same ownership verification pattern used by sibling endpoints (GetSource, UpdateSource, DeleteSource):

  • Fetch source first to verify existence and get ownership info
  • Extract authenticated user credentials
  • Verify user.Username == source.Username AND user.Organization == source.OrgID
  • Return 404 on unauthorized access (not 403 to prevent existence oracle)
  • Return 400 for other errors (per existing API spec)

Summary by CodeRabbit

  • Bug Fixes

    • Enforced ownership/organization checks for source downloads and ensured server returns 500 for internal errors on the download endpoint.
  • Tests

    • Added tests for authorized access, cross-organization denial, same-username/different-org denial, non-existent source handling, error-on-url-generation, and test-data cleanup.
  • Documentation

    • OpenAPI spec now documents 403 and 500 responses for the source download endpoint.
  • Chores

    • Client and server generated code updated to surface JSON 403/500 error responses.

@ronenav ronenav requested a review from a team as a code owner June 2, 2026 10:36
@ronenav ronenav requested review from amalimov and nirarg and removed request for a team June 2, 2026 10:36
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Handler enforces ownership for GetSourceDownloadURL: it loads the source, maps GetSource errors (not-found -> 404, others -> 500), requires authenticated user's Username/Organization to match source owner (mismatch -> 403), and maps downstream download errors to 500. OpenAPI, generated client/server, and tests updated.

Changes

Source Download Authorization

Layer / File(s) Summary
GetSourceDownloadURL handler changes
internal/handlers/v1alpha1/source.go
Load source, map GetSource errors (ErrResourceNotFound -> 404, others -> 500), require authenticated user's Username and Organization to match source Username/OrgID (mismatch -> 403), and return 500 for downstream GetSourceDownloadURL failures.
OpenAPI and generated client/server
api/v1alpha1/openapi.yaml, api/v1alpha1/spec.gen.go, internal/api/client/client.gen.go, internal/api/server/server.gen.go
Add 403 and 500 responses to GetSourceDownloadURL in OpenAPI and embedded spec; generated client parses HTTP 500 JSON Error into GetSourceDownloadURLResponse.JSON500; server adds 403 and 500 JSON response visitor types.
Tests for GetSourceDownloadURL authorization
internal/handlers/v1alpha1/source_test.go
Adds tests for owned-source success (200), cross-org and same-username/different-org requests returning 403, non-existent source returning 404, URL-generation failure returning 500, and AfterEach DB cleanup.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Handler as GetSourceDownloadURLHandler
  participant Auth as auth.MustHaveUser
  participant SourceSvc as sourceSrv
  Client->>Handler: GET /api/v1/sources/{id}/image-url
  Handler->>SourceSvc: GetSource(id)
  alt ErrResourceNotFound
    SourceSvc-->>Handler: ErrResourceNotFound
    Handler-->>Client: 404 Error
  else Other GetSource error
    SourceSvc-->>Handler: Error
    Handler-->>Client: 500 Error
  end
  Handler->>Auth: extract user (Username, Organization)
  Auth-->>Handler: User
  alt User/Org matches source owner
    Handler->>SourceSvc: GetSourceDownloadURL(id)
    alt Success
      SourceSvc-->>Handler: {Url, ExpiresAt}
      Handler-->>Client: 200 {Url, ExpiresAt}
    else Error
      SourceSvc-->>Handler: Error
      Handler-->>Client: 500 Error
    end
  else Ownership mismatch
    Handler-->>Client: 403 Error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

lgtm, approved

Suggested reviewers

  • amalimov

Poem

🐰 I hopped to guard a secret door,
Owner's name and org must match before.
If a stranger peeks where they don't belong,
A polite 403 says "move along".
Tests tidy the burrow, safe and sound.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a missing organization check to the GetSourceDownloadURL endpoint to fix an authorization vulnerability.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/handlers/v1alpha1/source_test.go`:
- Around line 1118-1120: The test currently only asserts result.Url is
non-empty; also assert the ExpiresAt field on the success response (the
server.GetSourceDownloadURL200JSONResponse value assigned to result) to ensure
it is set and meaningful. Update the test around the result variable to check
result.ExpiresAt is not nil/zero and represents a future timestamp (e.g., after
time.Now()) so callers relying on the expiry get a valid value.
- Around line 1122-1145: Add a test case where the requesting User has the same
Username but a different Organization to ensure the handler checks organization,
not just username: create a source row with Username "sharedUser" and
Organization "victimOrg" (similar to victimSourceID insertion), then call
handlers.NewServiceHandler(...).GetSourceDownloadURL with
auth.NewTokenContext(context.TODO(), attackerUser) where attackerUser.Username
== "sharedUser" but attackerUser.Organization == "attackerOrg", and assert the
response type is server.GetSourceDownloadURL404JSONResponse{}; this verifies
GetSourceDownloadURL (and related auth path) enforces org ownership rather than
only username.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ad7bf7aa-9e03-47de-9798-884044025be0

📥 Commits

Reviewing files that changed from the base of the PR and between fd21a23 and 89765fe.

📒 Files selected for processing (2)
  • internal/handlers/v1alpha1/source.go
  • internal/handlers/v1alpha1/source_test.go

Comment thread internal/handlers/v1alpha1/source_test.go
Comment thread internal/handlers/v1alpha1/source_test.go Outdated
@ronenav ronenav force-pushed the 4722-missing-org-check branch 2 times, most recently from 25d4676 to f3069b0 Compare June 2, 2026 11:23
Comment thread internal/handlers/v1alpha1/source.go
@ronenav ronenav force-pushed the 4722-missing-org-check branch from f3069b0 to b954990 Compare June 2, 2026 13:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/handlers/v1alpha1/source_test.go`:
- Around line 1095-1194: Add a new It test under the GetSourceDownloadURL
Context that covers the 500 path: create a source and its image_infra (same as
other tests) and authenticate as the owning user, but configure the downstream
URL generator to fail (e.g., inject a mock/storage client or URLProvider that
returns an error into service.NewSourceService or service.NewSizerService so URL
generation fails) then call handlers.NewServiceHandler(...).GetSourceDownloadURL
and assert no error and that the response type equals
server.GetSourceDownloadURL500JSONResponse; place cleanup in the existing
AfterEach.

In `@internal/handlers/v1alpha1/source.go`:
- Around line 243-255: The two places returning an empty
server.GetSourceDownloadURL500JSONResponse must return the required Error body
with a message and include context; replace both empty returns with returning
server.GetSourceDownloadURL500JSONResponse{Error: server.Error{Message:
fmt.Sprintf("failed to prefetch source: %v", err)}} or similar for the prefetch
path and server.GetSourceDownloadURL500JSONResponse{Error: server.Error{Message:
fmt.Sprintf("failed to get download URL for source %s: %v", request.Id, err)}}
for the s.sourceSrv.GetSourceDownloadURL error path, and wrap/log the original
error where appropriate (use fmt.Errorf or %w upstream) so the message surfaces
useful context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9a0e6a96-8511-4499-a5ce-f1ffa30f3162

📥 Commits

Reviewing files that changed from the base of the PR and between 89765fe and b954990.

📒 Files selected for processing (6)
  • api/v1alpha1/openapi.yaml
  • api/v1alpha1/spec.gen.go
  • internal/api/client/client.gen.go
  • internal/api/server/server.gen.go
  • internal/handlers/v1alpha1/source.go
  • internal/handlers/v1alpha1/source_test.go

Comment thread internal/handlers/v1alpha1/source_test.go
Comment thread internal/handlers/v1alpha1/source.go Outdated
@ronenav ronenav force-pushed the 4722-missing-org-check branch from b954990 to 4f3bfd0 Compare June 2, 2026 13:31
Comment thread internal/handlers/v1alpha1/source.go Outdated
…nloadURL endpoint

Signed-off-by: Ronen Avraham <ravraham@redhat.com>
@ronenav ronenav force-pushed the 4722-missing-org-check branch from 4f3bfd0 to 00950cc Compare June 4, 2026 11:33
@ronenav

ronenav commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

/hold

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/handlers/v1alpha1/source.go`:
- Around line 237-256: The handler currently calls s.sourceSrv.GetSource(ctx,
request.Id) and then s.sourceSrv.GetSourceDownloadURL(ctx, request.Id), causing
two DB fetches; refactor the service by adding a method that accepts a
pre-loaded *model.Source (e.g., GetSourceDownloadURLFromSource(ctx
context.Context, src *model.Source) (url string, expireAt time.Time, err error)
or change GetSourceDownloadURL to accept *model.Source) and implement the
download-URL logic there, then update the handler to pass the already-loaded
source into that new method (replace the second call to
s.sourceSrv.GetSourceDownloadURL(ctx, request.Id) with
s.sourceSrv.GetSourceDownloadURLFromSource(ctx, source)), keeping error handling
and return types unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 210ab3d2-61f2-466e-ab5e-2ec058654720

📥 Commits

Reviewing files that changed from the base of the PR and between b954990 and 00950cc.

📒 Files selected for processing (6)
  • api/v1alpha1/openapi.yaml
  • api/v1alpha1/spec.gen.go
  • internal/api/client/client.gen.go
  • internal/api/server/server.gen.go
  • internal/handlers/v1alpha1/source.go
  • internal/handlers/v1alpha1/source_test.go

Comment thread internal/handlers/v1alpha1/source.go
@openshift-ci

openshift-ci Bot commented Jun 7, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: AvielSegev, nirarg

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@AvielSegev

AvielSegev commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

/hold cancel

@AvielSegev AvielSegev merged commit ec47a33 into kubev2v:main Jun 7, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants