Merge pull request #7140 from betagouv/only_safe_pj_in_zip

fix(archive): exporte uniquement les fichiers sans virus
This commit is contained in:
LeSim 2022-04-12 12:10:14 +02:00 committed by GitHub
commit 9b1f9f9a21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 13 deletions

View file

@ -165,6 +165,7 @@ class PiecesJustificativesService
ActiveStorage::Attachment
.includes(:blob)
.where(record_type: "Champ", record_id: champ_id_dossier_id.keys)
.filter { |a| safe_attachment(a) }
.map do |a|
dossier_id = champ_id_dossier_id[a.record_id]
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
@ -181,6 +182,7 @@ class PiecesJustificativesService
ActiveStorage::Attachment
.includes(:blob)
.where(record_type: "Commentaire", record_id: commentaire_id_dossier_id.keys)
.filter { |a| safe_attachment(a) }
.map do |a|
dossier_id = commentaire_id_dossier_id[a.record_id]
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
@ -212,6 +214,7 @@ class PiecesJustificativesService
ActiveStorage::Attachment
.includes(:blob)
.where(record_type: "Dossier", name: "justificatif_motivation", record_id: dossiers)
.filter { |a| safe_attachment(a) }
.map do |a|
dossier_id = a.record_id
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
@ -262,4 +265,10 @@ class PiecesJustificativesService
.where(record_type: "BillSignature", record_id: bill_ids)
.map { |bill| ActiveStorage::DownloadableFile.bill_and_path(bill) }
end
def self.safe_attachment(attachment)
attachment
.blob
.metadata[:virus_scan_result] == ActiveStorage::VirusScanner::SAFE
end
end

View file

@ -23,6 +23,16 @@ describe PiecesJustificativesService do
it { expect(subject).to match_array([pj_champ.call(dossier).piece_justificative_file.attachment]) }
end
context 'with a pj not safe on a champ' do
let(:procedure) { create(:procedure, :with_piece_justificative) }
let(:dossier) { create(:dossier, procedure: procedure) }
let(:pj_champ) { -> (d) { d.champs.find { |c| c.type == 'Champs::PieceJustificativeChamp' } } }
before { attach_file_to_champ(pj_champ.call(dossier), safe = false) }
it { expect(subject).to be_empty }
end
context 'with a private pj champ' do
let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, procedure: procedure) }
@ -64,12 +74,26 @@ describe PiecesJustificativesService do
let(:dossier) { create(:dossier) }
let(:witness) { create(:dossier) }
let!(:commentaire) { create(:commentaire, :with_file, dossier: dossier) }
let!(:witness_commentaire) { create(:commentaire, :with_file, dossier: witness) }
let!(:commentaire) { create(:commentaire, dossier: dossier) }
let!(:witness_commentaire) { create(:commentaire, dossier: witness) }
before do
attach_file(commentaire.piece_jointe)
attach_file(witness_commentaire.piece_jointe)
end
it { expect(subject).to match_array(dossier.commentaires.first.piece_jointe.attachment) }
end
context 'with a pj not safe on a commentaire' do
let(:dossier) { create(:dossier) }
let!(:commentaire) { create(:commentaire, dossier: dossier) }
before { attach_file(commentaire.piece_jointe, safe = false) }
it { expect(subject).to be_empty }
end
context 'with a motivation' do
let(:dossier) { create(:dossier, :with_justificatif) }
let!(:witness) { create(:dossier, :with_justificatif) }
@ -77,6 +101,14 @@ describe PiecesJustificativesService do
it { expect(subject).to match_array(dossier.justificatif_motivation.attachment) }
end
context 'with a motivation not safe' do
let(:dossier) { create(:dossier) }
before { attach_file(dossier.justificatif_motivation, safe = false) }
it { expect(subject).to be_empty }
end
context 'with an attestation' do
let(:dossier) { create(:dossier, :with_attestation) }
let!(:witness) { create(:dossier, :with_attestation) }
@ -167,12 +199,20 @@ describe PiecesJustificativesService do
end
end
def attach_file_to_champ(champ)
attach_file(champ.piece_justificative_file)
def attach_file_to_champ(champ, safe = true)
attach_file(champ.piece_justificative_file, safe)
end
def attach_file(attachable)
attachable
.attach(io: StringIO.new("toto"), filename: "toto.png", content_type: "image/png")
def attach_file(attachable, safe = true)
to_be_attached = {
io: StringIO.new("toto"),
filename: "toto.png", content_type: "image/png"
}
if safe
to_be_attached[:metadata] = { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
end
attachable.attach(to_be_attached)
end
end

View file

@ -90,8 +90,22 @@ describe 'Inviting an expert:' do
let(:commentaire) { create(:commentaire, instructeur: instructeur, dossier: dossier) }
before do
champ.piece_justificative_file.attach(io: File.open(path), filename: "piece_justificative_0.pdf", content_type: "application/pdf")
dossier.champs_private << create(:champ_piece_justificative, :with_piece_justificative_file, private: true, dossier: dossier)
champ
.piece_justificative_file
.attach(io: File.open(path),
filename: "piece_justificative_0.pdf",
content_type: "application/pdf",
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE })
dossier.champs_private << create(:champ_piece_justificative, private: true, dossier: dossier)
dossier.champs_private
.first
.piece_justificative_file
.attach(io: File.open(path),
filename: "piece_justificative_0.pdf",
content_type: "application/pdf",
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE })
end
scenario 'An Expert can download an archive containing attachments without any private champ, bill signature and operations logs' do
@ -102,7 +116,7 @@ describe 'Inviting an expert:' do
click_on '1 avis à donner'
click_on avis.dossier.user.email
find(:css, '.attached').click
find(:css, '[aria-controls=print-pj-menu]').click
click_on 'Télécharger le dossier et toutes ses pièces jointes'
# For some reason, clicking the download link does not trigger the download in the headless browser ;
# So we need to go to the download link directly

View file

@ -194,14 +194,18 @@ describe 'Instructing a dossier:', js: true do
before do
dossier.passer_en_instruction!(instructeur: instructeur)
champ.piece_justificative_file.attach(io: File.open(path), filename: "piece_justificative_0.pdf", content_type: "application/pdf")
champ.piece_justificative_file
.attach(io: File.open(path),
filename: "piece_justificative_0.pdf",
content_type: "application/pdf",
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE })
log_in(instructeur.email, password)
visit instructeur_dossier_path(procedure, dossier)
end
scenario 'A instructeur can download an archive containing a single attachment' do
find(:css, '.attached').click
find(:css, '[aria-controls=print-pj-menu]').click
click_on 'Télécharger le dossier et toutes ses pièces jointes'
# For some reason, clicking the download link does not trigger the download in the headless browser ;
# So we need to go to the download link directly
@ -219,7 +223,12 @@ describe 'Instructing a dossier:', js: true do
end
scenario 'A instructeur can download an archive containing several identical attachments' do
commentaire.piece_jointe.attach(io: File.open(path), filename: "piece_justificative_0.pdf", content_type: "application/pdf")
commentaire
.piece_jointe
.attach(io: File.open(path),
filename: "piece_justificative_0.pdf",
content_type: "application/pdf",
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE })
visit telecharger_pjs_instructeur_dossier_path(procedure, dossier)
DownloadHelpers.wait_for_download