Skip to content

Vulcan v2.3.5

Released: 2026-04-11

Highlights

  • Information disclosure fix — full user directory was previously pre-loaded into the project/component page payloads, allowing any project admin to enumerate every registered Vulcan user. Replaced with a server-side /api/users/search endpoint with admin-only authorization and a scope=members mode for PoC selection.
  • Editor refresh shape drift fixrefreshComponent() was silently degrading the in-memory component shape after edits (memberships lost their name/email decoration, breaking MembersModal until full page reload). The editor JSON path now uses ComponentBlueprint :editor directly, eliminating the parallel jbuilder code path.
  • CI/release workflow split — Docker release moved to its own release.yml so the test suite no longer reruns on release publish. All GitHub Actions pinned to commit SHAs.

Added

  • GET /api/users/search — server-side user search endpoint
    • q (min 2 chars), membership_type (Project or Component), membership_id, limit (default 10, max 25)
    • scope=members (optional) — search within existing members instead of non-members; accessible to any member, not just admins
    • Returns only id, name, email
  • Project#search_available_members / Project#search_members
  • Component#search_available_members / Component#search_members (matches all_users semantics for direct + inherited members)
  • Async server-side search via vue-multiselect in NewMembership.vue, MembersModal.vue, and UpdateComponentDetailsModal.vue (debounced 300ms)
  • Contract tests asserting /components/:id.json editor refresh response shape exactly matches ComponentBlueprint :editor
  • Regression guards in ComponentBlueprint and rules_spec asserting available_members and all_users are absent from serialized payloads
  • 6 Component-target test cases in user_search_spec covering default search, scope=members, project-admin exclusion, and inherited member resolution
  • Dedicated release.yml workflow triggered only on release published events

Changed

  • available_members and all_users removed from ComponentBlueprint and ProjectBlueprint editor payloads
  • MembershipsTable derives pending access request user info from access_requests directly
  • ComponentsController#show editor JSON renders ComponentBlueprint :editor directly (no more jbuilder editor branch)
  • show.json.jbuilder simplified to non-member only (BenchmarkViewer's lightweight rule shape)
  • All GitHub Actions pinned to full commit SHAs

Fixed

  • Information disclosure: project admins could enumerate the full user directory via the page payload
  • Editor refresh shape drift: refreshComponent() was replacing component.memberships with raw ActiveRecord JSON (no name/email from the user join), silently breaking MembersModal
  • MembershipsTable.getAccessRequestId was reading stale request.user_id after the payload moved to nested request.user.{id,name,email} — Accept/Reject would crash
  • Component#search_available_members / #search_members were missing — controller dispatched to @target.search_* but only Project had them, causing NoMethodError for any membership_type=Component request
  • first_user_admin after_create callback was silently promoting test users to site admin in new request specs
  • SBOM tag mismatch (v2.3.4 vs 2.3.4) in release workflow

Security

The /api/users/search endpoint enforces:

  • Default search (non-members): admin-only — only project/component admins can search for candidates to add
  • scope=members search: any member of the project/component (used for PoC selection where the user list is already known to the searcher)
  • Site admins (current_user.admin) can search any project or component
  • Min 2-character query, max 25 results — limits enumeration even with admin access
  • ILIKE input sanitized via ActiveRecord::Base.sanitize_sql_like

Upgrade Notes

No database migrations required. This is a drop-in upgrade from v2.3.4.

Frontend behavior change:

  • The user dropdown in "Add Member" modals (project, component, and PoC selection) now shows an empty list until the user types at least 2 characters. This is intentional — the full user directory is no longer sent to the client.

No new environment variables.

No gem changes.

Version: v2.3.5

Part of the MITRE Security Automation Framework (SAF)