Skip to content

fix(integrations): Return 400 instead of 500 on partial code mapping PUT#120602

Open
scttcper wants to merge 4 commits into
masterfrom
scttcper/code-mapping-org-scope
Open

fix(integrations): Return 400 instead of 500 on partial code mapping PUT#120602
scttcper wants to merge 4 commits into
masterfrom
scttcper/code-mapping-org-scope

Conversation

@scttcper

@scttcper scttcper commented Jul 24, 2026

Copy link
Copy Markdown
Member

A PUT that left out projectId, e.g. {"sourceRoot": ""}, returned a 500. put() took new_project as a required arg but convert_args only set it when the body had a projectId, so the handler was called without it.

new_project is now optional and only included in the access check when the body actually asks to move the mapping. Missing required fields come back as a normal 400, and a garbage projectId or config_id 404s instead of blowing up on int(). Every other status code is unchanged.

fixes SENTRY-5ENF

The config lookup only filtered on org integration ids, and the new
project came straight out of `request.data` in `convert_args`. Two
problems fell out of that.

A PUT without `projectId` never set the `new_project` kwarg, so the
handler blew up on a missing positional arg and returned a 500 instead
of a validation error. And `get_project` was called with whatever string
was in the payload, so a non-numeric `projectId` hit the db as garbage.

Now the lookup filters on `organization_id` directly, and the new project
is resolved from `serializer.validated_data` after validation runs. The
serializer already marks `projectId` required and camelizes its error
keys, so the missing-field case returns the normal 400 field errors with
no special casing in the handler.

Also catches ValueError on the config lookup since the url pattern for
`config_id` is `[^/]+`, not digits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Jul 24, 2026
scttcper and others added 2 commits July 24, 2026 15:18
Unrelated to the 500 fix, and `organization_integration_id__in` already
scopes the lookup to the current org since those ids come from the org
itself. Keeping the pr focused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ional

Restores the original shape. `new_project` needs to exist so we access
check the project a mapping is being moved to, otherwise you could move
one into a project you can't see. The only thing wrong with it was that
`put()` took it as required while `convert_args` set it conditionally.

Now it defaults to None and the access check only includes it when the
body actually asks to move the mapping. The project lookup moved into
the existing try so a non-numeric `projectId` 404s like a foreign or
unknown one already did, instead of blowing up on int().

Keeps every other status code the same as before, only the three 500s
turn into 4xx.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@scttcper
scttcper marked this pull request as ready for review July 24, 2026 22:31
@scttcper
scttcper requested a review from a team as a code owner July 24, 2026 22:31
@scttcper
scttcper requested a review from a team July 24, 2026 22:31

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 28bcc5b. Configure here.

projects = [config.project_repository.project]
if new_project is not None:
projects.append(new_project)
if not request.access.has_projects_access(projects):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Snake_case bypasses project access check

High Severity

Making new_project optional means a PUT can skip the destination access check when the body uses snake_case project_id instead of projectId. convert_args only reads projectId, so new_project stays unset and has_projects_access only covers the current project, while RepositoryProjectPathConfigSerializer still accepts project_id and can move the mapping. Previously this path 500'd on the required new_project argument, so the bypass was unreachable.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 28bcc5b. Configure here.

… PUT

Bugbot caught a real hole in the previous commit. Making `new_project`
optional meant a body using `project_id` instead of `projectId` skipped
the destination access check entirely, since `convert_args` looked for
the literal camelCase key while the serializer normalizes both spellings
and happily moves the mapping. A member could move a code mapping onto a
project they can't see. It was a 500 before, so it wasn't reachable.

Normalizes the body with the same helpers the serializer uses instead of
matching one spelling, so `projectId`, `project_id` and `ProjectId` all
resolve to the same lookup and get access checked.

Test fails with 200 on the old code and 403 on this one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@scttcper

Copy link
Copy Markdown
Member Author

Good catch on the snake_case bypass, confirmed it was real. A member with access to the mapping's current project could PUT {"project_id": <hidden project>} and get a 200 with the mapping moved, while the camelCase spelling correctly 403'd. Reachable only because the missing-arg path used to 500.

Fixed in a63d3e3 by normalizing the body with the same convert_dict_key_case/camel_to_snake_case helpers the serializer uses, rather than matching a single spelling, so projectId, project_id and ProjectId all hit the same lookup and access check. Added a regression test that returns 200 without the fix and 403 with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants