Skip to content

[WEB-7813] fix: prevent ORM order_by injection in issue and other endpoints#9292

Open
mguptahub wants to merge 2 commits into
previewfrom
web-7806/fix-orm-order-by-injection
Open

[WEB-7813] fix: prevent ORM order_by injection in issue and other endpoints#9292
mguptahub wants to merge 2 commits into
previewfrom
web-7806/fix-orm-order-by-injection

Conversation

@mguptahub

@mguptahub mguptahub commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Advisories: GHSA-2r95-c453-vxmr · GHSA-w45q-6m65-9498
  • The order_by query param was passed directly to .order_by() / paginator without validation, allowing field-name injection (e.g. traversals like workspace__secret_key for timing-based data inference)
  • GHSA-w45q specifically targets the unauthenticated ProjectIssuesPublicEndpoint in Spaces

Approach

All allowlists are centralised in plane/utils/order_queryset.py as named frozenset constants. Each call site imports the constant by name — no inline sets in view files.

sanitize_order_by(value, allowed_fields, default) strips the leading - before looking up the bare field name; unrecognised values fall back to the safe default silently.

Files changed

File What
utils/order_queryset.py 6 named allowlists + sanitize_order_by() + guard at top of order_issue_queryset()
api/views/cycle.py Cycle issue list
api/views/module.py Module issue list (2 endpoints)
api/views/issue.py IssueActivity list + detail
app/views/intake/base.py IntakeIssue list
app/views/view/base.py Saved-view list
app/views/notification/base.py Notification paginator
app/views/project/base.py Project list paginator
app/views/user/base.py User activity paginator
app/views/workspace/user.py Workspace member activity paginator

Test plan

  • Issue list — valid order_by=sequence_id sorts correctly
  • Issue list — invalid order_by=workspace__secret_key silently falls back to -created_at
  • Public Spaces endpoint — unauthenticated request with injected order_by returns -created_at ordering
  • Cycle/module issue lists — valid fields work; unknown fields fall back
  • Notification list — paginator receives sanitised value

Co-authored-by: Plane AI noreply@plane.so

Summary by CodeRabbit

  • Bug Fixes
    • Applied consistent sorting restrictions across all relevant list endpoints to enforce only predefined, safe order_by fields and directions, improving stability and preventing unsafe ordering inputs.

…HSA-2r95, GHSA-w45q)

Add field-name allowlists and a sanitize_order_by() utility in order_queryset.py.
All allowlists are centralised there; each call site imports the named constant
so there are no inline sets scattered across view files.

- order_queryset.py: ISSUE_ORDER_BY_ALLOWLIST, INTAKE_ISSUE_ORDER_BY_ALLOWLIST,
  ACTIVITY_ORDER_BY_ALLOWLIST, PROJECT_ORDER_BY_ALLOWLIST, VIEW_ORDER_BY_ALLOWLIST,
  NOTIFICATION_ORDER_BY_ALLOWLIST + sanitize_order_by() utility; validation added
  at the top of order_issue_queryset() — fixes all callers including the
  unauthenticated ProjectIssuesPublicEndpoint (GHSA-w45q)
- api/views/cycle.py, api/views/module.py: cycle/module issue list endpoints
- api/views/issue.py: IssueActivity list and detail endpoints
- app/views/intake/base.py: IntakeIssue list
- app/views/view/base.py: saved-view list
- app/views/notification/base.py: notification paginator
- app/views/project/base.py: project list paginator
- app/views/user/base.py, app/views/workspace/user.py: activity paginators

Closes WEB-7813

Co-authored-by: Plane AI <noreply@plane.so>
Copilot AI review requested due to automatic review settings June 22, 2026 12:46

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b92dffaa-d0cc-4665-a2b6-841a1eec85d8

📥 Commits

Reviewing files that changed from the base of the PR and between 3161f75 and c0aa685.

📒 Files selected for processing (1)
  • apps/api/plane/utils/order_queryset.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/utils/order_queryset.py

📝 Walkthrough

Walkthrough

Adds sanitize_order_by helper and per-model frozenset allowlists to plane/utils/order_queryset.py. Applies the helper across nine view files (cycle, issue, module, intake, notification, project, user, view, workspace/user) to replace raw request.GET order_by accesses, constraining ORM ordering to known-safe fields.

Changes

ORM order_by Injection Prevention

Layer / File(s) Summary
Allowlists and sanitize_order_by helper
apps/api/plane/utils/order_queryset.py
Defines frozenset allowlists for issue, intake, activity, project, view, and notification querysets. Adds sanitize_order_by(value, allowed_fields, default) which strips a leading -, checks the bare field against the allowlist, and returns the default when not found. Updates order_issue_queryset to sanitize its order_by_param upfront.
Endpoint sanitization across all views
apps/api/plane/api/views/cycle.py, apps/api/plane/api/views/issue.py, apps/api/plane/api/views/module.py, apps/api/plane/app/views/intake/base.py, apps/api/plane/app/views/notification/base.py, apps/api/plane/app/views/project/base.py, apps/api/plane/app/views/user/base.py, apps/api/plane/app/views/view/base.py, apps/api/plane/app/views/workspace/user.py
Imports sanitize_order_by and relevant allowlist constants, then replaces raw request.GET order_by usage in cycle work item, issue activity list/detail, module issue list/detail, intake, notification, project, user activity, workspace view, and workspace user activity endpoints.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • dheeru0198
  • pablohashescobar
  • sangeethailango

Poem

🐇 Hoppity-hop, no sneaky sorts allowed,
The order_by fields must pass through my crowd.
A frozenset fence keeps bad fields away,
-created_at stands guard come what may.
No injection shall darken this burrow today! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main security fix: preventing ORM order_by injection in endpoints.
Description check ✅ Passed The description includes summary, approach, files changed table, and test plan checklist, covering all template sections appropriately.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch web-7806/fix-orm-order-by-injection

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.

@makeplane

makeplane Bot commented Jun 22, 2026

Copy link
Copy Markdown

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

@coderabbitai coderabbitai 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.

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 `@apps/api/plane/api/views/cycle.py`:
- Line 858: The sanitize_order_by function in
apps/api/plane/utils/order_queryset.py uses lstrip("-") which removes all
leading dashes, allowing malformed inputs like "--created_at" to pass allowlist
validation. Fix this by updating the normalization logic to strip only a single
leading dash (for descending order) before validating against
ISSUE_ORDER_BY_ALLOWLIST, then append it back if present in the original input.
This ensures only properly formatted sort tokens with at most one leading dash
are accepted.

In `@apps/api/plane/api/views/module.py`:
- Line 601: The sanitize_order_by function uses lstrip("-") which strips all
leading dashes, allowing malformed inputs like "--created_at" to pass through
unchanged when the bare field is in the allowlist. These invalid tokens then
cause FieldError exceptions at runtime instead of safely falling back to the
default value. Update the sanitize_order_by function in
apps/api/plane/utils/order_queryset.py to properly validate the input so that it
rejects fields with multiple leading dashes or other malformed patterns,
returning the default value for any input that doesn't match the exact expected
format (a single optional dash followed by a valid field name from the
allowlist).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e3c8bcee-66d0-452a-aa04-c22ab8b9c9f8

📥 Commits

Reviewing files that changed from the base of the PR and between 4a0746b and 3161f75.

📒 Files selected for processing (10)
  • apps/api/plane/api/views/cycle.py
  • apps/api/plane/api/views/issue.py
  • apps/api/plane/api/views/module.py
  • apps/api/plane/app/views/intake/base.py
  • apps/api/plane/app/views/notification/base.py
  • apps/api/plane/app/views/project/base.py
  • apps/api/plane/app/views/user/base.py
  • apps/api/plane/app/views/view/base.py
  • apps/api/plane/app/views/workspace/user.py
  • apps/api/plane/utils/order_queryset.py

Comment thread apps/api/plane/api/views/cycle.py
Comment thread apps/api/plane/api/views/module.py
lstrip("-") stripped all leading dashes, allowing "--created_at" to
pass the allowlist check unchanged and reach .order_by() as a malformed
token (causing FieldError). Now strips only one leading dash; any
remaining dash prefix is rejected to the safe default.

Co-authored-by: Plane AI <noreply@plane.so>
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.

2 participants