Skip to content

fix(security): restrict driver listing, driver readme, zone triggers, and systems module_id lookups to support users#435

Merged
stakach merged 2 commits into
masterfrom
security/h1-access-controls
May 18, 2026
Merged

fix(security): restrict driver listing, driver readme, zone triggers, and systems module_id lookups to support users#435
stakach merged 2 commits into
masterfrom
security/h1-access-controls

Conversation

@camreeves
Copy link
Copy Markdown
Contributor

Summary

Adds support-level access checks to four engine endpoints flagged in the DataArt pentest of McKinsey Converge (2026-05) as H1 — Missing function-level access controls. All four are authenticated, but the previous gate was just `can_read` (or no role check at all on the module_id branch), so any standard user could enumerate internal infrastructure detail.

Endpoint Pentest finding Change
`GET /api/engine/v2/drivers` H1 #6, #9 `before_action :check_support, only: [:index]`
`GET /api/engine/v2/drivers/:id/readme` H1 #10 same filter (readme contents can include configuration notes / credential examples)
`GET /api/engine/v2/zones/:id/triggers` H1 #7 added `:trigger_instances` to the existing `:check_support` list (and to `:can_read`)
`GET /api/engine/v2/systems?module_id=…` H1 #11 inline guard at the top of `Systems#index` — only the `module_id` branch is gated, the rest of the index query is unaffected

Why `Systems#index` is gated inline rather than via `before_action`

`/systems` has many legitimate non-support use cases (end-user system search by name, by zone, by email). The pentest flagged specifically the `module_id` query parameter, which is an operator-style "which system contains this module?" lookup. Gating the whole index would break normal users. The inline check only raises Forbidden when `module_id` is present and the caller is not admin/support.

Sibling endpoints intentionally NOT changed

Per the Converge team review, three sibling endpoints flagged in the same H1 batch stay as-is:

Test plan

  • Added three new test groups using the existing 403 patterns in the same spec files (`zones_spec.cr:284` and `systems_spec.cr:192` for the `group_id` 403 already gated with the same helper):
    • `drivers_spec.cr` — 403 for non-support `GET /drivers`, 403 for non-support `GET /drivers/:id/readme`, positive case for support-only
    • `zones_spec.cr` — 403 for non-support `GET /zones/:id/triggers`, positive case for support-only
    • `systems_spec.cr` — 403 for non-support `GET /systems?module_id=…` (the existing positive test for admin remains)
  • `crystal tool format` on all six touched files
  • `./bin/ameba` on all six touched files (one pre-existing warning on `zones.cr:242` from a recent unrelated commit, not introduced by this PR)
  • CI to run the full spec suite — I was unable to run the rest-api docker test stack locally (Docker Desktop VM OOM-killed the Crystal compiler), so CI verification is the source of truth for these changes

🤖 Generated with Claude Code

…and module_id lookups to support users

Reported by DataArt pentest 2026-05 (McKinsey Converge) as H1 finding —
"Missing function-level access controls". Four engine endpoints were
authenticated but reachable by any standard user, exposing internal
infrastructure detail that should be scoped to operators:

- GET /api/engine/v2/drivers           (H1 #6, #9)
- GET /api/engine/v2/drivers/:id/readme (H1 #10) — README contents can
  include configuration notes / credential examples
- GET /api/engine/v2/zones/:id/triggers (H1 #7) — internal automation
  state
- GET /api/engine/v2/systems?module_id=… (H1 #11) — system-by-module
  lookup; only this branch of /systems is gated, the rest of the index
  query is unchanged

Drivers and zone-triggers are gated with an additional
`before_action :check_support`. Systems gates only the module_id path
inline so the rest of the index (the standard system search used by
end-users) is unaffected.

Three sibling endpoints flagged in the same pentest are deliberately
NOT changed here per Converge team review:
 - /metadata/:id (room/zone metadata) stays can_read_guest
 - /systems?q=…&fields=… stays can_read (search-by-name is a normal user flow)
 - /users?q=… and /users/:id stay as-is (controller already strips PII
   via to_public_struct for non-admin callers and filters to the
   caller's authority)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the type: bug something isn't working label May 18, 2026
# endpoints expose internal infrastructure detail (driver inventory, driver
# README files that may include configuration notes or credential examples)
# that should not be available to standard authenticated users.
# Reported by DataArt pentest 2026-05 (McKinsey Converge) as H1 finding —
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

remove client and pen test information from comments

# "Missing function-level access controls".
if module_id && !user_support?
raise Error::Forbidden.new("module_id lookups require admin or support")
end
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

let's undo this change. It's not a big issue and complicates the controller.

- revert the Systems#index module_id check_support guard — the leak is
  low-impact and the inline branch complicates index. Drops the matching
  spec too.
- shorten the security comments on drivers#index/readme and the zones
  :trigger_instances filter; remove the external project/report attribution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added type: bug something isn't working and removed type: bug something isn't working labels May 18, 2026
Copy link
Copy Markdown
Member

@stakach stakach left a comment

Choose a reason for hiding this comment

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

LGTM

@stakach stakach merged commit 8f1c69d into master May 18, 2026
8 of 11 checks passed
@stakach stakach deleted the security/h1-access-controls branch May 18, 2026 04:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: bug something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants