From f0b0e7fd9ae00ae08602a2db5a9f211bf002d7e7 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Mon, 29 Nov 2021 15:43:51 +0100 Subject: [PATCH 1/6] Switch to usage of zip unix binary to create archive. Also use a dedicated queue for DelayedJob use dedicated archives queue As the used disk space will increase, we want a fined grain control move zip logic in dedicated method zip wip wip fix(spec): pass spec in green tech(improvements): avoid File.delete(folder), favor FileUtils.remove_entry_secure which is safer. Also wrap most of code that open file within blocks so it is cleaned when the block ends. Lastly use attachement.download to avoid big memory pressure [download in chunk, write in chunk] otherwise big file [124>1GO] are loaded in memory. what if we run multiple jobs/download in parallel ? fix(spec): try to retry with grace clean(procedure_archive_service_spec.rb): better retry [avoid to rewrite on open file] lint(things): everything --- app/jobs/archive_creation_job.rb | 2 +- app/lib/active_storage/downloadable_file.rb | 16 ++++ app/lib/utils/retryable.rb | 20 +++++ app/services/procedure_archive_service.rb | 83 +++++++++++++------ spec/lib/utils/retryable_spec.rb | 36 ++++++++ .../procedure_archive_service_spec.rb | 47 ++++++++--- 6 files changed, 164 insertions(+), 40 deletions(-) create mode 100644 app/lib/utils/retryable.rb create mode 100644 spec/lib/utils/retryable_spec.rb diff --git a/app/jobs/archive_creation_job.rb b/app/jobs/archive_creation_job.rb index 9a4a087f4..0c5fd90ad 100644 --- a/app/jobs/archive_creation_job.rb +++ b/app/jobs/archive_creation_job.rb @@ -1,5 +1,5 @@ class ArchiveCreationJob < ApplicationJob - queue_as :exports + queue_as :archives def perform(procedure, archive, instructeur) ProcedureArchiveService diff --git a/app/lib/active_storage/downloadable_file.rb b/app/lib/active_storage/downloadable_file.rb index 49bd45f32..577d52c3e 100644 --- a/app/lib/active_storage/downloadable_file.rb +++ b/app/lib/active_storage/downloadable_file.rb @@ -1,4 +1,20 @@ class ActiveStorage::DownloadableFile + # https://edgeapi.rubyonrails.org/classes/ActiveStorage/Blob.html#method-i-download + def self.download(attachment:, destination_path:, in_chunk: true) + byte_written = 0 + + File.open(destination_path, mode: 'wb') do |fd| # we expact a path as string, so we can recreate the file (ex: failure/retry on former existing fd) + if in_chunk + attachment.download do |chunk| + byte_written += fd.write(chunk) + end + else + byte_written = fd.write(attachment.download) + end + end + byte_written + end + def self.create_list_from_dossier(dossier, for_expert = false) dossier_export = PiecesJustificativesService.generate_dossier_export(dossier) pjs = [dossier_export] + PiecesJustificativesService.liste_documents(dossier, for_expert) diff --git a/app/lib/utils/retryable.rb b/app/lib/utils/retryable.rb new file mode 100644 index 000000000..d4ab13fca --- /dev/null +++ b/app/lib/utils/retryable.rb @@ -0,0 +1,20 @@ +module Utils + module Retryable + # usage: + # max_attempt : retry count + # errors : only retry those errors + # with_retry(max_attempt: 10, errors: [StandardError]) do + # do_something_which_can_fail + # end + def with_retry(max_attempt: 1, errors: [StandardError], &block) + limiter = 0 + begin + yield + rescue *errors + limiter += 1 + retry if limiter <= max_attempt + raise + end + end + end +end diff --git a/app/services/procedure_archive_service.rb b/app/services/procedure_archive_service.rb index 5a4d54256..0d66cb319 100644 --- a/app/services/procedure_archive_service.rb +++ b/app/services/procedure_archive_service.rb @@ -1,6 +1,10 @@ require 'tempfile' +require 'utils/retryable' class ProcedureArchiveService + include Utils::Retryable + ARCHIVE_CREATION_DIR = ENV.fetch('ARCHIVE_CREATION_DIR') { '/tmp' } + def initialize(procedure) @procedure = procedure end @@ -14,39 +18,22 @@ class ProcedureArchiveService end def collect_files_archive(archive, instructeur) + ## faux, ca ne doit prendre que certains groupe instructeur if archive.time_span_type == 'everything' dossiers = @procedure.dossiers.state_termine else dossiers = @procedure.dossiers.processed_in_month(archive.month) end - files = create_list_of_attachments(dossiers) - - tmp_file = Tempfile.new(['tc', '.zip']) - - Zip::OutputStream.open(tmp_file) do |zipfile| - bug_reports = '' - files.each do |attachment, pj_filename| - zipfile.put_next_entry("#{zip_root_folder(@procedure)}/#{pj_filename}") - begin - zipfile.puts(attachment.download) - rescue - bug_reports += "Impossible de récupérer le fichier #{pj_filename}\n" - end - end - if !bug_reports.empty? - zipfile.put_next_entry("#{zip_root_folder(@procedure)}/LISEZMOI.txt") - zipfile.puts(bug_reports) - end + attachments = create_list_of_attachments(dossiers) + zip(attachments) do |zip_file| + archive.file.attach( + io: File.open(zip_file), + filename: archive.filename(@procedure), + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + ) end - - archive.file.attach( - io: File.open(tmp_file), - filename: archive.filename(@procedure), - # we don't want to run virus scanner on this file - metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } - ) - tmp_file.delete archive.make_available! InstructeurMailer.send_archive(instructeur, @procedure, archive).deliver_later end @@ -63,7 +50,49 @@ class ProcedureArchiveService private - def zip_root_folder(procedure) + def zip(attachments, &block) + Dir.mktmpdir(nil, ARCHIVE_CREATION_DIR) do |tmp_dir| + archive_dir = File.join(tmp_dir, zip_root_folder) + zip_path = File.join(ARCHIVE_CREATION_DIR, "#{zip_root_folder}.zip") + + begin + FileUtils.remove_entry_secure(archive_dir) if Dir.exist?(archive_dir) + Dir.mkdir(archive_dir) + + bug_reports = '' + attachments.each do |attachment, path| + attachment_path = File.join(archive_dir, path) + attachment_dir = File.dirname(attachment_path) + + FileUtils.mkdir_p(attachment_dir) if !Dir.exist?(attachment_dir) + begin + with_retry(max_attempt: 1) do + ActiveStorage::DownloadableFile.download(attachment: attachment, + destination_path: attachment_path, + in_chunk: true) + end + rescue => e + Rails.logger.error("Fail to download filename #{File.basename(attachment_path)} in procedure##{@procedure.id}, reason: #{e}") + File.delete(attachment_path) if File.exist?(attachment_path) + bug_reports += "Impossible de récupérer le fichier #{File.basename(attachment_path)}\n" + end + end + + if !bug_reports.empty? + File.write(File.join(archive_dir, 'LISEZMOI.txt'), bug_reports) + end + + File.delete(zip_path) if File.exist?(zip_path) + puts `cd #{tmp_dir} && zip -r #{zip_path} #{zip_root_folder}` + yield(zip_path) + ensure + FileUtils.remove_entry_secure(archive_dir) if Dir.exist?(archive_dir) + File.delete(zip_path) if File.exist?(zip_path) + end + end + end + + def zip_root_folder "procedure-#{@procedure.id}" end diff --git a/spec/lib/utils/retryable_spec.rb b/spec/lib/utils/retryable_spec.rb new file mode 100644 index 000000000..5e556713d --- /dev/null +++ b/spec/lib/utils/retryable_spec.rb @@ -0,0 +1,36 @@ +describe Utils::Retryable do + Includer = Struct.new(:something) do + include Utils::Retryable + + def caller(max_attempt:, errors:) + with_retry(max_attempt: max_attempt, errors: errors) do + yield + end + end + end + + subject { Includer.new("test") } + let(:spy) { double() } + + describe '#with_retry' do + it 'works while retry count is less than max attempts' do + divider_that_raise_error = 0 + divider_that_works = 1 + expect(spy).to receive(:divider).and_return(divider_that_raise_error, divider_that_works) + result = subject.caller(max_attempt: 2, errors: [ZeroDivisionError]) { 10 / spy.divider } + expect(result).to eq(10 / divider_that_works) + end + + it 're raise error if it occures more than max_attempt' do + expect(spy).to receive(:divider).and_return(0, 0) + expect { subject.caller(max_attempt: 1, errors: [ZeroDivisionError]) { 0 / spy.divider } } + .to raise_error(ZeroDivisionError) + end + + it 'does not retry other errors' do + expect(spy).to receive(:divider).and_raise(StandardError).once + expect { subject.caller(max_attempt: 2, errors: [ZeroDivisionError]) { 0 / spy.divider } } + .to raise_error(StandardError) + end + end +end diff --git a/spec/services/procedure_archive_service_spec.rb b/spec/services/procedure_archive_service_spec.rb index ba84050a9..feb6e568b 100644 --- a/spec/services/procedure_archive_service_spec.rb +++ b/spec/services/procedure_archive_service_spec.rb @@ -1,3 +1,4 @@ + describe ProcedureArchiveService do let(:procedure) { create(:procedure, :published) } let(:instructeur) { create(:instructeur) } @@ -5,6 +6,7 @@ describe ProcedureArchiveService do let(:year) { 2020 } let(:month) { 3 } let(:date_month) { Date.strptime("#{year}-#{month}", "%Y-%m") } + describe '#create_pending_archive' do context 'for a specific month' do it 'creates a pending archive' do @@ -28,10 +30,8 @@ describe ProcedureArchiveService do end describe '#collect_files_archive' do - before do - create_dossier_for_month(year, month) - create_dossier_for_month(2020, month) - end + let!(:dossier) { create_dossier_for_month(year, month) } + let!(:dossier_2020) { create_dossier_for_month(2020, month) } after { Timecop.return } @@ -42,14 +42,19 @@ describe ProcedureArchiveService do it 'collect files' do expect(InstructeurMailer).to receive(:send_archive).and_return(mailer) - service.collect_files_archive(archive, instructeur) archive.file.open do |f| files = ZipTricks::FileReader.read_zip_structure(io: f) - expect(files.size).to be 2 - expect(files.first.filename).to include("export") - expect(files.last.filename).to include("attestation") + + structure = [ + "procedure-#{procedure.id}/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/pieces_justificatives/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/pieces_justificatives/attestation-dossier--05-03-2021-00-00-#{dossier.attestation.pdf.id % 10000}.pdf", + "procedure-#{procedure.id}/dossier-#{dossier.id}/export-#{dossier.id}-05-03-2021-00-00-#{dossier.id}.pdf" + ] + expect(files.map(&:filename)).to match_array(structure) end expect(archive.file.attached?).to be_truthy end @@ -89,9 +94,16 @@ describe ProcedureArchiveService do archive.file.open do |f| files = ZipTricks::FileReader.read_zip_structure(io: f) - expect(files.size).to be 4 - expect(files.last.filename).to eq("procedure-#{procedure.id}/LISEZMOI.txt") - expect(extract(f, files.last)).to match(/Impossible de .*cni.*png/) + structure = [ + "procedure-#{procedure.id}/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/export-dossier-05-03-2020-00-00-1.pdf", + "procedure-#{procedure.id}/dossier-#{dossier.id}/pieces_justificatives/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/export-#{dossier.id}-05-03-2021-00-00-#{dossier.id}.pdf", + "procedure-#{procedure.id}/LISEZMOI.txt" + ] + expect(files.map(&:filename)).to match_array(structure) + expect(extract(f, files.last)).to match(/Impossible de .* .*cni.*png/) end end end @@ -109,7 +121,18 @@ describe ProcedureArchiveService do archive = Archive.last archive.file.open do |f| files = ZipTricks::FileReader.read_zip_structure(io: f) - expect(files.size).to be 4 + structure = [ + "procedure-#{procedure.id}/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/pieces_justificatives/", + "procedure-#{procedure.id}/dossier-#{dossier.id}/pieces_justificatives/attestation-dossier--05-03-2020-00-00-#{dossier.attestation.pdf.id % 10000}.pdf", + "procedure-#{procedure.id}/dossier-#{dossier.id}/export-#{dossier.id}-05-03-2020-00-00-#{dossier.id}.pdf", + "procedure-#{procedure.id}/dossier-#{dossier_2020.id}/", + "procedure-#{procedure.id}/dossier-#{dossier_2020.id}/export-#{dossier_2020.id}-05-03-2020-00-00-#{dossier_2020.id}.pdf", + "procedure-#{procedure.id}/dossier-#{dossier_2020.id}/pieces_justificatives/", + "procedure-#{procedure.id}/dossier-#{dossier_2020.id}/pieces_justificatives/attestation-dossier--05-03-2020-00-00-#{dossier_2020.attestation.pdf.id % 10000}.pdf" + ] + expect(files.map(&:filename)).to match_array(structure) end expect(archive.file.attached?).to be_truthy end From f7bc387e44f128f700161c5f11aaa7ed247b7a38 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 10 Dec 2021 16:04:47 +0100 Subject: [PATCH 2/6] feat(flipper): use new way of generating archive only on some procedure flipped with new actor :zip_using_binary --- app/services/procedure_archive_service.rb | 53 ++++++++++- .../procedure_archive_service_spec.rb | 93 ++++++++++++++++++- 2 files changed, 140 insertions(+), 6 deletions(-) diff --git a/app/services/procedure_archive_service.rb b/app/services/procedure_archive_service.rb index 0d66cb319..1d170c789 100644 --- a/app/services/procedure_archive_service.rb +++ b/app/services/procedure_archive_service.rb @@ -1,5 +1,4 @@ require 'tempfile' -require 'utils/retryable' class ProcedureArchiveService include Utils::Retryable @@ -18,7 +17,15 @@ class ProcedureArchiveService end def collect_files_archive(archive, instructeur) - ## faux, ca ne doit prendre que certains groupe instructeur + if Flipper.enabled?(:zip_using_binary, @procedure) + new_collect_files_archive(archive, instructeur) + else + old_collect_files_archive(archive, instructeur) + end + end + + def new_collect_files_archive(archive, instructeur) + ## faux, ca ne doit prendre que certains groupe instructeur ? if archive.time_span_type == 'everything' dossiers = @procedure.dossiers.state_termine else @@ -26,7 +33,7 @@ class ProcedureArchiveService end attachments = create_list_of_attachments(dossiers) - zip(attachments) do |zip_file| + download_and_zip(attachments) do |zip_file| archive.file.attach( io: File.open(zip_file), filename: archive.filename(@procedure), @@ -38,6 +45,44 @@ class ProcedureArchiveService InstructeurMailer.send_archive(instructeur, @procedure, archive).deliver_later end + def old_collect_files_archive(archive, instructeur) + if archive.time_span_type == 'everything' + dossiers = @procedure.dossiers.state_termine + else + dossiers = @procedure.dossiers.processed_in_month(archive.month) + end + + files = create_list_of_attachments(dossiers) + + tmp_file = Tempfile.new(['tc', '.zip']) + + Zip::OutputStream.open(tmp_file) do |zipfile| + bug_reports = '' + files.each do |attachment, pj_filename| + zipfile.put_next_entry("#{zip_root_folder}/#{pj_filename}") + begin + zipfile.puts(attachment.download) + rescue + bug_reports += "Impossible de récupérer le fichier #{pj_filename}\n" + end + end + if !bug_reports.empty? + zipfile.put_next_entry("#{zip_root_folder}/LISEZMOI.txt") + zipfile.puts(bug_reports) + end + end + + archive.file.attach( + io: File.open(tmp_file), + filename: archive.filename(@procedure), + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + ) + tmp_file.delete + archive.make_available! + InstructeurMailer.send_archive(instructeur, @procedure, archive).deliver_later + end + def self.procedure_files_size(procedure) dossiers_files_size(procedure.dossiers) end @@ -50,7 +95,7 @@ class ProcedureArchiveService private - def zip(attachments, &block) + def download_and_zip(attachments, &block) Dir.mktmpdir(nil, ARCHIVE_CREATION_DIR) do |tmp_dir| archive_dir = File.join(tmp_dir, zip_root_folder) zip_path = File.join(ARCHIVE_CREATION_DIR, "#{zip_root_folder}.zip") diff --git a/spec/services/procedure_archive_service_spec.rb b/spec/services/procedure_archive_service_spec.rb index feb6e568b..58bd75d34 100644 --- a/spec/services/procedure_archive_service_spec.rb +++ b/spec/services/procedure_archive_service_spec.rb @@ -1,4 +1,3 @@ - describe ProcedureArchiveService do let(:procedure) { create(:procedure, :published) } let(:instructeur) { create(:instructeur) } @@ -29,7 +28,97 @@ describe ProcedureArchiveService do end end - describe '#collect_files_archive' do + describe '#old_collect_files_archive' do + before do + create_dossier_for_month(year, month) + create_dossier_for_month(2020, month) + end + + after { Timecop.return } + + context 'for a specific month' do + let(:archive) { create(:archive, time_span_type: 'monthly', status: 'pending', month: date_month) } + let(:year) { 2021 } + let(:mailer) { double('mailer', deliver_later: true) } + + it 'collect files' do + expect(InstructeurMailer).to receive(:send_archive).and_return(mailer) + + service.collect_files_archive(archive, instructeur) + + archive.file.open do |f| + files = ZipTricks::FileReader.read_zip_structure(io: f) + expect(files.size).to be 2 + expect(files.first.filename).to include("export") + expect(files.last.filename).to include("attestation") + end + expect(archive.file.attached?).to be_truthy + end + + context 'with a missing file' do + let(:pj) do + PiecesJustificativesService::FakeAttachment.new( + file: StringIO.new('coucou'), + filename: "export-dossier.pdf", + name: 'pdf_export_for_instructeur', + id: 1, + created_at: Time.zone.now + ) + end + + let(:bad_pj) do + PiecesJustificativesService::FakeAttachment.new( + file: nil, + filename: "cni.png", + name: 'cni.png', + id: 2, + created_at: Time.zone.now + ) + end + + let(:documents) { [pj, bad_pj] } + before do + allow(PiecesJustificativesService).to receive(:liste_documents).and_return(documents) + end + + it 'collect files without raising exception' do + expect { service.collect_files_archive(archive, instructeur) }.not_to raise_exception + end + + it 'add bug report to archive' do + service.collect_files_archive(archive, instructeur) + + archive.file.open do |f| + files = ZipTricks::FileReader.read_zip_structure(io: f) + expect(files.size).to be 4 + expect(files.last.filename).to eq("procedure-#{procedure.id}/LISEZMOI.txt") + expect(extract(f, files.last)).to match(/Impossible de .*cni.*png/) + end + end + end + end + + context 'for all months' do + let(:archive) { create(:archive, time_span_type: 'everything', status: 'pending') } + let(:mailer) { double('mailer', deliver_later: true) } + + it 'collect files' do + expect(InstructeurMailer).to receive(:send_archive).and_return(mailer) + + service.collect_files_archive(archive, instructeur) + + archive = Archive.last + archive.file.open do |f| + files = ZipTricks::FileReader.read_zip_structure(io: f) + expect(files.size).to be 4 + end + expect(archive.file.attached?).to be_truthy + end + end + end + + describe '#new_collect_files_archive' do + before { Flipper.enable_actor(:zip_using_binary, procedure) } let!(:dossier) { create_dossier_for_month(year, month) } let!(:dossier_2020) { create_dossier_for_month(2020, month) } From 1795084dce48fb40e94c67e59f9f9e9869cacb5b Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 10 Dec 2021 16:23:34 +0100 Subject: [PATCH 3/6] fix(brakeman): no code injection here --- config/brakeman.ignore | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/config/brakeman.ignore b/config/brakeman.ignore index f718aba91..c47973b33 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -1,5 +1,25 @@ { "ignored_warnings": [ + { + "warning_type": "Command Injection", + "warning_code": 14, + "fingerprint": "02c4a46707ac4d1cb33b17847aad8fd9f2e1d4201ec9a93b27111d6a3ac1bd29", + "check_name": "Execute", + "message": "Possible command injection", + "file": "app/services/procedure_archive_service.rb", + "line": 131, + "link": "https://brakemanscanner.org/docs/warning_types/command_injection/", + "code": "`cd #{tmp_dir} && zip -r #{File.join(ARCHIVE_CREATION_DIR, \"#{zip_root_folder}.zip\")} #{zip_root_folder}`", + "render_path": null, + "location": { + "type": "method", + "class": "ProcedureArchiveService", + "method": "download_and_zip" + }, + "user_input": "tmp_dir", + "confidence": "Medium", + "note": "" + }, { "warning_type": "Cross-Site Scripting", "warning_code": 2, @@ -109,7 +129,7 @@ "check_name": "Redirect", "message": "Possible unprotected redirect", "file": "app/controllers/instructeurs/procedures_controller.rb", - "line": 190, + "line": 195, "link": "https://brakemanscanner.org/docs/warning_types/redirect/", "code": "redirect_to(Export.find_or_create_export(params[:export_format], (params[:time_span_type] or \"everything\"), current_instructeur.groupe_instructeurs.where(:procedure => procedure)).file.service_url)", "render_path": null, @@ -143,6 +163,6 @@ "note": "no user input, fixed value" } ], - "updated": "2021-11-26 13:22:41 +0100", + "updated": "2021-12-10 16:34:44 +0100", "brakeman_version": "5.1.1" } From 148be50c86444ab07010956c33ff486c122f8872 Mon Sep 17 00:00:00 2001 From: mfo Date: Mon, 13 Dec 2021 15:56:27 +0100 Subject: [PATCH 4/6] Update app/services/procedure_archive_service.rb Co-authored-by: LeSim --- app/services/procedure_archive_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/procedure_archive_service.rb b/app/services/procedure_archive_service.rb index 1d170c789..cd385945c 100644 --- a/app/services/procedure_archive_service.rb +++ b/app/services/procedure_archive_service.rb @@ -128,7 +128,7 @@ class ProcedureArchiveService end File.delete(zip_path) if File.exist?(zip_path) - puts `cd #{tmp_dir} && zip -r #{zip_path} #{zip_root_folder}` + `cd #{tmp_dir} && zip -r #{zip_path} #{zip_root_folder}` yield(zip_path) ensure FileUtils.remove_entry_secure(archive_dir) if Dir.exist?(archive_dir) From 721d926c0d04dc53f45a400bc495c7f0c27bc744 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 13 Dec 2021 15:21:35 +0100 Subject: [PATCH 5/6] wip(fix): chdir --- app/services/procedure_archive_service.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/services/procedure_archive_service.rb b/app/services/procedure_archive_service.rb index cd385945c..30c13c02b 100644 --- a/app/services/procedure_archive_service.rb +++ b/app/services/procedure_archive_service.rb @@ -128,7 +128,9 @@ class ProcedureArchiveService end File.delete(zip_path) if File.exist?(zip_path) - `cd #{tmp_dir} && zip -r #{zip_path} #{zip_root_folder}` + Dir.chdir(tmp_dir) do + system 'zip', '-r', zip_path, zip_root_folder + end yield(zip_path) ensure FileUtils.remove_entry_secure(archive_dir) if Dir.exist?(archive_dir) From cf5794eebf05a9d1826b572e9a839bd03c549323 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 13 Dec 2021 17:09:20 +0100 Subject: [PATCH 6/6] clean(brakeman): remove unwanted warning --- config/brakeman.ignore | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/config/brakeman.ignore b/config/brakeman.ignore index c47973b33..a21e71bf4 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -1,25 +1,5 @@ { "ignored_warnings": [ - { - "warning_type": "Command Injection", - "warning_code": 14, - "fingerprint": "02c4a46707ac4d1cb33b17847aad8fd9f2e1d4201ec9a93b27111d6a3ac1bd29", - "check_name": "Execute", - "message": "Possible command injection", - "file": "app/services/procedure_archive_service.rb", - "line": 131, - "link": "https://brakemanscanner.org/docs/warning_types/command_injection/", - "code": "`cd #{tmp_dir} && zip -r #{File.join(ARCHIVE_CREATION_DIR, \"#{zip_root_folder}.zip\")} #{zip_root_folder}`", - "render_path": null, - "location": { - "type": "method", - "class": "ProcedureArchiveService", - "method": "download_and_zip" - }, - "user_input": "tmp_dir", - "confidence": "Medium", - "note": "" - }, { "warning_type": "Cross-Site Scripting", "warning_code": 2, @@ -163,6 +143,6 @@ "note": "no user input, fixed value" } ], - "updated": "2021-12-10 16:34:44 +0100", + "updated": "2021-12-13 17:09:07 +0100", "brakeman_version": "5.1.1" }