Skip to content

fix(backend): stop users reading and overwriting each other's data - #164

Merged
CMaintz merged 5 commits into
mainfrom
fix/ownership-checks
Oct 1, 2026
Merged

CMaintz merged 5 commits into
mainfrom
fix/ownership-checks

Conversation

@CMaintz

@CMaintz CMaintz commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Went through every endpoint that takes an id and checked it's actually scoped to the logged-in user. A few weren't:

  • GET /jobs/{id}/documents returned everyone's generated CVs and cover letters for that job, since jobs are shared. Now filtered by user in the query.
  • PUT on experience, projects, education, socials, strengths, languages and profile skills saved the path id with the caller's user id stamped on it, so knowing someone else's id let you overwrite their row and take it over. They now load by id + owner first and 404 if it isn't yours (same 404 for "doesn't exist", so ids can't be probed).
  • PDF templates: GET /{id}, export and update only work on system templates or your own.
  • Prompt templates: can't duplicate, favourite or generate with someone else's private template anymore. CV analysis also ignores CV versions you don't own.
  • Error handler: malformed JSON, bad UUIDs, 405, unknown routes, AccessDeniedException and ResponseStatusException were all turning into 500s. They're 4xx now, and real 500s get logged.

Deletes were already scoped, and I added tests to keep them that way.

Tests: service tests with two users for each fix, plus @WebMvcTests for the error mapping and the cross-user cases. ./gradlew :backend:test passes locally (576 tests), and so do spotlessCheck and spotbugs.

Not fixed here: company research notes live on the shared companies row, so all users read and overwrite the same notes. Making them per user needs a migration, so it'll be its own PR.

Merge order: no conflicts with #155, #157, #158, #160, #161 or #162. If #155 lands first, CrossUserAccessWebMvcTest needs a @MockitoBean ManageCustomSectionsUseCase, same as #162's ProfileSectionControllerTest. A Testcontainers test for the new documents query can go in once #158 is merged.

The red dependency-audit check is the jackson-databind CVEs already on main, fixed in #157.

Malformed JSON, bad path ids, 405, unknown routes, AccessDeniedException and
ResponseStatusException all fell through the catch-all and came back as 500.
Adds NotFoundException -> 404 and logs the real 500s.
- GET /jobs/{id}/documents returned every user's generated docs for a job
- PUT on experience, projects, education, socials, strengths, languages and
  profile skills saved the path id with the caller's user id, so anyone who
  knew an id could overwrite (and take over) another user's row. Now loads by
  id + owner first, 404 otherwise
- PDF templates: get/export/update only system or own templates
- prompt templates: no duplicating, favouriting or generating with someone
  else's private template
- CV analysis ignores cv versions the caller doesn't own
@CMaintz
CMaintz merged commit 2af10c4 into main Oct 1, 2026
23 checks passed
@CMaintz
CMaintz deleted the fix/ownership-checks branch October 1, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant