Gate Duo CLI via request header instead of client-trusted param
What does this MR do and why?
The Duo CLI admin gate previously fired only when the client sent requires_duo_cli_enabled: true in the create-workflow request body — so any client that omitted it (older CLIs, or a modified client) walked straight past an admin's "Duo CLI off" setting.
This MR makes the server the source of truth. It removes the client-trusted param and identifies a Duo CLI session from the X-Gitlab-Client-Name: Duo CLI header the CLI already sends on every request. A Duo CLI session — interactive or headless alike — is blocked whenever the duo_cli_enabled_setting FF is on and the duo_cli_enabled admin setting is off, regardless of request body. A duo_cli_session_blocked audit event is emitted on refusal.
GA-readiness change for Duo CLI gating. Companion client MR: gitlab-org/editor-extensions/gitlab-lsp!3596 (merged). (FF default-flip for self-managed is a separate MR.)
Honest-actor model (intentional): the client-name header is client-set and forgeable — governance for honest admins, not a security boundary.
Where to look
ee/lib/api/ai/duo_workflows/workflows.rb— the gate in thepostblock readsif duo_cli_request? && duo_cli_disabled_by_admin?(the CLI-request check is the explicit outer guard;duo_cli_disabled_by_admin?is just the FF + admin-setting check).:requires_duo_cli_enabledremoved.config/audit_events/types/duo_cli_session_blocked.yml— new audit event type.doc/user/compliance/audit_event_types.md— regenerated to match (do not hand-edit).ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb— header-based gating cases; interactive and headless are gated identically.
References
- Admin setting / FF: #597833 (closed) · rollout #603129
- Audit/telemetry: gitlab-org/editor-extensions/gitlab-lsp#2318 (closed)
- Client MR: gitlab-org/editor-extensions/gitlab-lsp!3596 (merged)
Screenshots or screen recordings
N/A — no UI changes (API gating only).
How to set up and validate locally
- Enable the FF and disable the CLI in the Rails console:
Feature.enable(:duo_cli_enabled_setting) Ai::Setting.instance.update!(duo_cli_enabled: false) - CLI blocked —
POST /api/v4/ai/duo_workflows/workflowswith headerX-Gitlab-Client-Name: Duo CLI⇒403 "Duo CLI has been disabled by your administrator"and aduo_cli_session_blockedaudit event. A headlessrunis blocked the same way (no exemption). - Non-CLI unaffected — same request without the
X-Gitlab-Client-Name: Duo CLIheader ⇒ succeeds. - Re-enable (
duo_cli_enabled: true) ⇒ the CLI request succeeds again.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist.