fix(server): apply workspace/scene filters to authenticated requests (SEC-01) - #2348
Conversation
…(SEC-01) UsecaseMiddleware was registered globally, before the auth middleware runs, so its operator was always nil and repos.Filtered was never applied. Any authenticated user could read another workspace's projects and scenes, including private ones, since the resulting repos had no read/write restriction at all. Re-run the same middleware on the private route group, after the operator is attached, so authenticated requests get their usecase container rebuilt with the operator's workspace/scene filters actually applied. Adds a unit test pinning UsecaseMiddleware's filtering behavior and an e2e test that goes through the real HTTP server, confirmed to fail against the unpatched code and pass with the fix.
There was a problem hiding this comment.
Pull request overview
This PR fixes a security issue (SEC-01) where authenticated requests could access projects/scenes in other workspaces because UsecaseMiddleware was executed before the auth middleware, resulting in unscoped (unfiltered) repositories being used for private routes.
Changes:
- Re-register
UsecaseMiddlewareonapiPrivateRouteafter operator attachment so authenticated requests rebuild the usecase container with workspace/scene filters applied. - Add a unit test covering
UsecaseMiddlewarebehavior with and without an operator present on the context. - Add an end-to-end GraphQL regression test ensuring cross-tenant project listing is blocked while owner access still works.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| server/internal/app/app.go | Re-runs usecase construction on private routes post-auth via newUsecaseMiddleware to ensure repo scoping is applied. |
| server/internal/app/usecase_test.go | Unit regression test verifying workspace scoping is applied when an operator is present at middleware execution time. |
| server/e2e/gql_project_workspace_scoping_test.go | E2E regression test validating route wiring prevents cross-tenant project reads for authenticated users. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
airslice
left a comment
There was a problem hiding this comment.
Non-blocking: this fixes the immediate issue, but having UsecaseMiddleware registered globally and then re-run for authenticated routes makes the authorization behavior somewhat dependent on middleware ordering.
It might be worth considering later whether public/private usecase construction can be separated more explicitly, so authenticated routes never receive an unrestricted container in the first place.
Thanks. I will create a task to refactor this. |
Summary
UsecaseMiddleware ran globally, before the auth middleware, so its operator was always nil and the workspace/scene filters never got applied to the repos. Any authenticated user could read another workspace's projects and scenes, private ones included, since the repos ended up unrestricted.
The fix re-runs the same middleware on the private route group, after the operator is attached, so authenticated requests get their usecase container rebuilt with the operator's filters actually applied. Public routes keep the original global registration, unchanged.
Test plan