WAF blocks requests of users that try to create new merge requests via the GitLab UI
Summary
When using a web application firewall with GitLab, users creating new merge requests via the GitLab UI are blocked.
When there is an MR create action via the GitLab UI with the "Delete source branch when merge request is accepted." option selected, a duplicate parameter/form data is submitted. This duplicate parameter/form data triggers a blocking action from the WAF due to a suspected evasion attack.
This duplicate key or parameter with different values (0 and 1) will cause the WAF policy to block the request.
Steps to reproduce
1. Put Gitlab behind a WAF
2. Create a new Merge Request via the GitLab UI
3. On the "New merge request" screen, select under "Merge options" - "Delete source branch when merge request is accepted"
4. Click "Create merge request"
The resulting POST request sent by the GitLab contains the following payload:
merge_request%5Bforce_remove_source_branch%5D=0&merge_request%5Bforce_remove_source_branch%5D=1
which decodes to:
merge_request[force_remove_source_branch]: 0
merge_request[force_remove_source_branch]: 1
What is the current bug behavior?
Per /app/views/shared/issuable/form/_merge_params.html.haml#L12-25:
haml
- if issuable.can_remove_source_branch?(current_user)
.form-check.gl-mb-3
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?, class: 'form-check-input'
= label_tag 'merge_request[force_remove_source_branch]', class: 'form-check-label' do
Delete source branch when merge request is accepted.
- if !project.squash_never?
.form-check
- if project.squash_always?
= hidden_field_tag 'merge_request[squash]', '1', id: nil
= check_box_tag 'merge_request[squash]', '1', project.squash_enabled_by_default?, class: 'form-check-input', disabled: 'true'
- else
= hidden_field_tag 'merge_request[squash]', '0', id: nil
= check_box_tag 'merge_request[squash]', '1', issuable_squash_option?(issuable, project), class: 'form-check-input'
The create MR form is still using the HAML methods, and we appear to inject the same form field twice if its default value is on.
Here's an example form data:
merge_request[title]=Update+README.md&merge_request_diff_head_sha=8daaa1cb9805521c94a08fa7d599a75cbddbff97&merge_request[description]=A+goal&merge_request[assignee_ids][]=1&merge_request[reviewer_ids][]=0&merge_request[approval_rules_attributes][][id]=&merge_request[approval_rules_attributes][][approvals_required]=0&merge_request[approval_rules_attributes][][name]=&merge_request[approval_rules_attributes][][user_ids][]=&merge_request[approval_rules_attributes][][group_ids][]=&merge_request[label_ids][]=&merge_request[update_blocking_merge_request_refs]=false&merge_request[force_remove_source_branch]=0&merge_request[force_remove_source_branch]=1&merge_request[squash]=0&merge_request[lock_version]=0&merge_request[source_project_id]=16&merge_request[source_branch]=test&merge_request[target_project_id]=16&merge_request[target_branch]=main
Relevant bit in the above is:
merge_request[force_remove_source_branch]=0&merge_request[force_remove_source_branch]=1
Going by the implementation link above, the same issue occurs for the squash option on the form too.
What is the expected correct behavior?
When there is an MR create action via the GitLab UI with the "Delete source branch when merge request is accepted." option selected, there should not be a duplicate parameter/form data sent.
This would prevent the WAF from suspecting an evasion attack (and blocking the request), as there would only be one parameter passed instead of a duplicate as well.
Possible fixes
1. The hidden field lines above can probably be removed away to address the issue.
2. A Vue.js form rewrite would also address this unintentionally.