Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/controllers/manual_verifications_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ def start_capture

# direct upload fallback for either document class
def submit_documents
# the scan button shares this form (per-form csrf tokens rule out a
# formaction to another url) and marks itself with intent=scan
return prepare_capture if params[:intent] == "scan"

unless @case.link_sent? && @case.document_class.present?
redirect_to manual_verification_path and return
end
Expand Down
5 changes: 4 additions & 1 deletion app/views/manual_verifications/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,10 @@
</label>

<% if @case.persona_capture_available? %>
<button type="submit" formaction="<%= manual_verification_prepare_capture_path %>" formmethod="post" class="chooser-cta">Scan with your camera</button>
<%# same action as the form on purpose: rails per-form csrf tokens are
bound to the form url, so a formaction elsewhere fails forgery
protection. submit_documents dispatches on intent instead. %>
<button type="submit" name="intent" value="scan" class="chooser-cta">Scan with your camera</button>
<p><small>Camera not working? Upload directly below.</small></p>
<% end %>

Expand Down
24 changes: 22 additions & 2 deletions spec/requests/manual_verifications_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,9 @@
it "offers the scan button inside the same form as the details and consent" do
get manual_verification_path
expect(response.body).to include("Scan with your camera")
expect(response.body).to include(manual_verification_prepare_capture_path)
# must post to the shared form's own url: per-form csrf tokens reject a formaction elsewhere
expect(response.body).to include('name="intent" value="scan"')
expect(response.body).not_to include("formaction")
expect(response.body).to include('name="biometric_consent"')
end

Expand Down Expand Up @@ -305,8 +307,26 @@
expect(kase.reload.submitted_fields).to be_empty
end

it "passes real forgery protection when the scan button submits the shared form" do
# the test env disables csrf checks, which is how the formaction bug
# shipped: per-form tokens are bound to the form url, so the scan
# button must post to the same url as the upload form.
ActionController::Base.allow_forgery_protection = true

get manual_verification_path
form = Nokogiri::HTML(response.body).at_css("form[action='#{manual_verification_documents_path}']")
token = form.at_css("input[name='authenticity_token']")["value"]

post manual_verification_documents_path,
params: details.merge(attested: "1", biometric_consent: "1", intent: "scan", authenticity_token: token)
expect(response).to redirect_to(manual_verification_capture_path)
expect(kase.reload.attested).to be(true)
ensure
ActionController::Base.allow_forgery_protection = false
end

it "records the prerequisites on the case, then lets the capture start" do
post manual_verification_prepare_capture_path, params: details.merge(attested: "1", biometric_consent: "1")
post manual_verification_documents_path, params: details.merge(attested: "1", biometric_consent: "1", intent: "scan")
expect(response).to redirect_to(manual_verification_capture_path)

kase.reload
Expand Down
Loading