diff --git a/gems/excel_analyzer/lib/excel_analyzer.rb b/gems/excel_analyzer/lib/excel_analyzer.rb index 9be9d5fdb1..ff6a5b926c 100644 --- a/gems/excel_analyzer/lib/excel_analyzer.rb +++ b/gems/excel_analyzer/lib/excel_analyzer.rb @@ -19,6 +19,18 @@ module ExcelAnalyzer # @return [Proc] the callable to run for spreadsheet attachments mattr_accessor :on_spreadsheet_received, default: ->(blob) {} + # A configurable callable that gets executed when an analyzed spreadsheet + # contains signs of hidden data. This can be useful for raising alerts, + # logging incidents, or taking other custom actions. + # + # @example Set a custom callable to handle hidden metadata detection + # ExcelAnalyzer.on_hidden_metadata = ->(blob, metadata) { alert(blob) } + # + # @!attribute [rw] on_hidden_metadata + # @return [Proc] the callable to run when hidden metadata is detected in a + # spreadsheet + mattr_accessor :on_hidden_metadata, default: ->(blob, metadata) {} + # Provides the list of content types that the ExcelAnalyzer will attempt to # analyze in search of hidden data. It currently includes content types for # .xls and .xlsx files. diff --git a/gems/excel_analyzer/lib/excel_analyzer/xlsx_analyzer.rb b/gems/excel_analyzer/lib/excel_analyzer/xlsx_analyzer.rb index 10bcc82e68..6b938f3927 100644 --- a/gems/excel_analyzer/lib/excel_analyzer/xlsx_analyzer.rb +++ b/gems/excel_analyzer/lib/excel_analyzer/xlsx_analyzer.rb @@ -19,7 +19,15 @@ def self.accept?(blob) end def metadata - { excel: excel_metadata } + data = excel_metadata + + if suspected_problem?(data) + # rubocop:disable Style/RescueModifier + ExcelAnalyzer.on_hidden_metadata.call(blob, data) rescue nil + # rubocop:enable Style/RescueModifier + end + + { excel: data } end private @@ -29,5 +37,9 @@ def excel_metadata rescue StandardError => ex { error: ex.message } end + + def suspected_problem?(data) + data.any? { |k, v| k != :error && k != :named_ranges && v > 1 } + end end end diff --git a/gems/excel_analyzer/spec/excel_analyzer/xlsx_analyzer_spec.rb b/gems/excel_analyzer/spec/excel_analyzer/xlsx_analyzer_spec.rb index bb0fa2a491..0a7689ce1c 100644 --- a/gems/excel_analyzer/spec/excel_analyzer/xlsx_analyzer_spec.rb +++ b/gems/excel_analyzer/spec/excel_analyzer/xlsx_analyzer_spec.rb @@ -23,6 +23,13 @@ end describe "#metadata" do + around do |example| + original_callback = ExcelAnalyzer.on_hidden_metadata + ExcelAnalyzer.on_hidden_metadata = ->(blob) {} + example.call + ExcelAnalyzer.on_hidden_metadata = original_callback + end + let(:metadata) { ExcelAnalyzer::XlsxAnalyzer.new(blob).metadata } context "when the blob is an Excel file with hidden data" do @@ -70,6 +77,11 @@ it "detects pivot cache" do expect(metadata[:excel][:pivot_cache]).to eq 1 end + + it "does not call on_hidden_metadata callback" do + expect(ExcelAnalyzer.on_hidden_metadata).to receive(:call) + metadata + end end context "when the blob is an Excel file without hidden data" do @@ -89,6 +101,11 @@ pivot_cache: 0 ) end + + it "does not call on_hidden_metadata callback" do + expect(ExcelAnalyzer.on_hidden_metadata).to_not receive(:call) + metadata + end end context "when the blob is not an Excel file" do @@ -102,6 +119,11 @@ error: "Zip end of central directory signature not found" ) end + + it "does not call on_hidden_metadata callback" do + expect(ExcelAnalyzer.on_hidden_metadata).to_not receive(:call) + metadata + end end end end