Skip to content

Enforce ORDER BY allowlist on activity list endpoints - #49624

Closed
lukeheath wants to merge 2 commits into
mainfrom
lukeheath/ghsa-rxhg-activity-order-allowlist
Closed

Enforce ORDER BY allowlist on activity list endpoints#49624
lukeheath wants to merge 2 commits into
mainfrom
lukeheath/ghsa-rxhg-activity-order-allowlist

Conversation

@lukeheath

@lukeheath lukeheath commented Jul 20, 2026

Copy link
Copy Markdown
Member

Related issue: Addresses GHSA-rxhg-vcww-2mpw (residual call sites left by #44385)

Checklist for submitter

  • Changes file added for user-visible changes in changes/.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements).

What & why

#44385 migrated the deprecated appendListOptionsWithCursorToSQL (which sanitizes but does not allowlist the order_key) to the allowlist-based secure variant across the codebase. Two call sites were left behind in the activity bounded context after it was refactored to use platform_mysql.AppendListOptionsWithParams:

  • ListActivities — powers GET /api/v1/fleet/activities
  • ListHostPastActivities — powers GET /api/v1/fleet/hosts/{id}/activities

Both allowed sorting by any column name (the value was backtick-sanitized, so it was not SQL-injectable, but there was no allowlist). This migrates them to AppendListOptionsWithParamsSecure with an explicit OrderKeyAllowlist:

  • List activities: id, created_at, activity_type, user_id, user_name, user_email, streamed, fleet_initiated (all columns already returned by the query).
  • List host past activities: id, created_at, activity_type (matches the documented allowed fields).

Columns not returned by the query (details, host_only) are intentionally excluded so order_key cannot be used as an inference oracle. An unlisted key now returns 422 Unprocessable Entity instead of being sorted on.

Testing

  • Added/updated automated tests

    • Datastore unit tests assert unlisted keys (details, host_only, user_email for host activities, etc.) return InvalidOrderKeyError, and documented keys still succeed.
    • Integration tests assert the HTTP endpoints return 422 for an unlisted order_key.
  • QA'd via automated tests (MYSQL_TEST=1 REDIS_TEST=1 go test ./server/activity/...).

Database migrations

N/A — no schema changes.

New Fleet configuration settings

N/A.

fleetd/orbit/Fleet Desktop

N/A.

Summary by CodeRabbit

  • Bug Fixes
    • Restricted activity sorting to approved fields; unsupported order_key values now return a client validation error.
    • Corrected the default ordering for host activity history.
    • Prevented endpoint-restricted API-only access tokens from accessing debug and profiling endpoints.

A global-admin API-only token scoped to an api_endpoints allowlist could
still reach every /debug/* route, because the debug handler's middleware
only checked for the global-admin role and never consulted the token's
endpoint restrictions like the main API path does.

Debug routes are not in the public API catalog, so they can never appear
in an allowlist. Deny restricted API-only tokens (api_only with a
non-empty api_endpoints list) at the debug middleware, matching the
least-privilege scoping APIOnlyEndpointCheck enforces elsewhere.
…-2mpw)

Migrate ListActivities and ListHostPastActivities in the activity bounded
context from the deprecated AppendListOptionsWithParams to the allowlist-based
AppendListOptionsWithParamsSecure. These were the two residual call sites left
by #44385. Unlisted order keys (including columns not returned by the query,
such as details) now return a client error instead of being sorted on.
@lukeheath
lukeheath requested review from a team and rachaelshaw as code owners July 20, 2026 23:35
Copilot AI review requested due to automatic review settings July 20, 2026 23:35
@lukeheath
lukeheath marked this pull request as draft July 20, 2026 23:36
@coderabbitai

coderabbitai Bot commented Jul 20, 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 27a2cf4a-a7e6-4cd7-9c36-c0b83f4c0705

📥 Commits

Reviewing files that changed from the base of the PR and between 5e19bfc and 71050a2.

⛔ Files ignored due to path filters (1)
  • docs/REST API/rest-api.md is excluded by !**/*.md
📒 Files selected for processing (8)
  • changes/15935-activity-order-key-allowlist
  • changes/16879-debug-api-only-restriction
  • server/activity/internal/mysql/activity.go
  • server/activity/internal/mysql/activity_test.go
  • server/activity/internal/service/service.go
  • server/activity/internal/tests/integration_test.go
  • server/service/debug_handler.go
  • server/service/debug_handler_test.go

Walkthrough

Activity listing endpoints now restrict order_key values through endpoint-specific MySQL allowlists and secure pagination helpers. The host past activities service uses created_at as its default order key, with unit and integration tests covering valid and invalid values. Debug authentication now denies API-only users with non-empty endpoint allowlists, while tests verify both denied and permitted configurations.

Possibly related PRs

  • fleetdm/fleet#49607: Updates the same debug middleware and tests to block restricted API-only users from debug routes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 It clearly summarizes the main change: enforcing ORDER BY allowlists on activity list endpoints.
Description check ✅ Passed The description follows the template closely and includes the issue link, impact, testing, and no-op sections.
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.
✨ 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 lukeheath/ghsa-rxhg-activity-order-allowlist

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.

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 67.03%. Comparing base (fb0e817) to head (71050a2).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
server/activity/internal/mysql/activity.go 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49624      +/-   ##
==========================================
- Coverage   67.81%   67.03%   -0.79%     
==========================================
  Files        3890     3890              
  Lines      247635   247641       +6     
  Branches    12982    12982              
==========================================
- Hits       167943   166004    -1939     
- Misses      64529    66430    +1901     
- Partials    15163    15207      +44     
Flag Coverage Δ
backend 68.29% <90.90%> (-0.94%) ⬇️
backend-activity 86.37% <87.50%> (+0.72%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

This PR tightens sorting validation on the activity list endpoints by enforcing an ORDER BY allowlist (returning 422 for unapproved order_key values), and also prevents restricted API-only tokens (those with a non-empty endpoint allowlist) from accessing /debug/* routes.

Changes:

  • Migrate activity datastore list queries to platform_mysql.AppendListOptionsWithParamsSecure with explicit OrderKeyAllowlist mappings.
  • Add datastore + integration tests asserting invalid order_key values are rejected with 422 Unprocessable Entity.
  • Deny restricted API-only users from debug endpoints and add unit tests for the new auth behavior.

Reviewed changes

Copilot reviewed 6 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/service/debug_handler.go Denies debug route access for restricted API-only users.
server/service/debug_handler_test.go Adds coverage for restricted vs unrestricted API-only behavior on debug routes.
server/activity/internal/tests/integration_test.go Adds endpoint-level assertions for 422 on invalid order_key.
server/activity/internal/service/service.go Adjusts default order key to match new allowlist keys.
server/activity/internal/mysql/activity.go Introduces and applies order-key allowlists for activity list queries.
server/activity/internal/mysql/activity_test.go Adds datastore-level tests for allowed/rejected order keys.
docs/REST API/rest-api.md (Content excluded) REST API documentation update.
changes/16879-debug-api-only-restriction (Content excluded) Release-note/change entry for debug restriction.
changes/15935-activity-order-key-allowlist (Content excluded) Release-note/change entry for activity order-key allowlist.
Files excluded by content exclusion policy (3)
  • changes/15935-activity-order-key-allowlist
  • changes/16879-debug-api-only-restriction
  • docs/REST API/rest-api.md

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +53
if v.User.APIOnly && len(v.User.APIEndpoints) > 0 {
http.Error(w, "Unauthorized", http.StatusForbidden)
return
Comment on lines +115 to +119
).Return(&fleet.User{
GlobalRole: ptr.String(fleet.RoleAdmin),
APIOnly: true,
APIEndpoints: []fleet.APIEndpointRef{{Method: "GET", Path: "/api/v1/fleet/hosts"}},
}, nil)
Comment on lines +135 to +137
"admin session": {GlobalRole: ptr.String(fleet.RoleAdmin)},
"unrestricted api-only": {GlobalRole: ptr.String(fleet.RoleAdmin), APIOnly: true},
"api-only empty allowlist": {GlobalRole: ptr.String(fleet.RoleAdmin), APIOnly: true, APIEndpoints: []fleet.APIEndpointRef{}},
@lukeheath lukeheath closed this Jul 30, 2026
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