From b8f88ece5c962edf21058b60b8d9cad45d343606 Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Tue, 18 Sep 2018 15:53:11 +0200 Subject: [PATCH] [#2579] Fix injection SQL dans le filtrage instructeur --- app/services/dossier_field_service.rb | 23 ++++++++---- spec/services/dossier_field_service_spec.rb | 41 ++++++++++++++++++++- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/app/services/dossier_field_service.rb b/app/services/dossier_field_service.rb index 08ce0d58b..2420ba072 100644 --- a/app/services/dossier_field_service.rb +++ b/app/services/dossier_field_service.rb @@ -62,7 +62,9 @@ class DossierFieldService def filtered_ids(dossiers, filters) filters.map do |filter| - case filter['table'] + table = filter['table'] + column = sanitized_column(filter) + case table when 'self' dossiers.where("? ILIKE ?", filter['column'], "%#{filter['value']}%") @@ -72,27 +74,26 @@ class DossierFieldService .where("? ILIKE ?", "france_connect_informations.#{filter['column']}", "%#{filter['value']}%") when 'type_de_champ', 'type_de_champ_private' - relation = filter['table'] == 'type_de_champ' ? :champs : :champs_private + relation = table == 'type_de_champ' ? :champs : :champs_private dossiers .includes(relation) .where("champs.type_de_champ_id = ?", filter['column'].to_i) .where("champs.value ILIKE ?", "%#{filter['value']}%") when 'etablissement' - table = filter['table'] if filter['column'] == 'entreprise_date_creation' date = filter['value'].to_date rescue nil dossiers .includes(table) - .where("#{table.pluralize}.#{filter['column']} = ?", date) + .where("#{column} = ?", date) else dossiers .includes(table) - .where("#{table.pluralize}.#{filter['column']} ILIKE ?", "%#{filter['value']}%") + .where("#{column} ILIKE ?", "%#{filter['value']}%") end when 'user' dossiers - .includes(filter['table']) - .where("#{filter['table'].pluralize}.#{filter['column']} ILIKE ?", "%#{filter['value']}%") + .includes(table) + .where("#{column} ILIKE ?", "%#{filter['value']}%") end.pluck(:id) end.reduce(:&) end @@ -138,6 +139,14 @@ class DossierFieldService private + def sanitized_column(field) + table = field['table'] + table = ActiveRecord::Base.connection.quote_column_name((table == 'self' ? 'dossier' : table).pluralize) + column = ActiveRecord::Base.connection.quote_column_name(field['column']) + + table + '.' + column + end + def field_hash(label, table, column) { 'label' => label, diff --git a/spec/services/dossier_field_service_spec.rb b/spec/services/dossier_field_service_spec.rb index cc0c0e975..73a061b31 100644 --- a/spec/services/dossier_field_service_spec.rb +++ b/spec/services/dossier_field_service_spec.rb @@ -2,7 +2,37 @@ require 'spec_helper' describe DossierFieldService do describe '#filtered_ids' do - let(:procedure) { create(:procedure) } + let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) } + + context 'for type_de_champ table' do + let(:kept_dossier) { create(:dossier, procedure: procedure) } + let(:discarded_dossier) { create(:dossier, procedure: procedure) } + let(:type_de_champ) { procedure.types_de_champ.first } + + before do + type_de_champ.champ.create(dossier: kept_dossier, value: 'keep me') + type_de_champ.champ.create(dossier: discarded_dossier, value: 'discard me') + end + + subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'type_de_champ', 'column' => type_de_champ.id, 'value' => 'keep' }]) } + + it { is_expected.to contain_exactly(kept_dossier.id) } + end + + context 'for type_de_champ_private table' do + let(:kept_dossier) { create(:dossier, procedure: procedure) } + let(:discarded_dossier) { create(:dossier, procedure: procedure) } + let(:type_de_champ_private) { procedure.types_de_champ_private.first } + + before do + type_de_champ_private.champ.create(dossier: kept_dossier, value: 'keep me') + type_de_champ_private.champ.create(dossier: discarded_dossier, value: 'discard me') + end + + subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.id, 'value' => 'keep' }]) } + + it { is_expected.to contain_exactly(kept_dossier.id) } + end context 'for etablissement table' do context 'for entreprise_date_creation column' do @@ -25,5 +55,14 @@ describe DossierFieldService do it { is_expected.to contain_exactly(kept_dossier.id) } end end + + context 'for user table' do + let!(:kept_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@keepmail.com')) } + let!(:discarded_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@discard.com')) } + + subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'user', 'column' => 'email', 'value' => 'keepmail' }]) } + + it { is_expected.to contain_exactly(kept_dossier.id) } + end end end