Skip to content
代码片段 群组 项目
未验证 提交 3aa4ed36 编辑于 作者: 路志远's avatar 路志远 提交者: GitLab
浏览文件

Support parsing XML with huge nodes

上级 df0231b4
No related branches found
No related tags found
无相关合并请求
---
name: allow_nokogiri_parse_huge_xml
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148779
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/454797
milestone: '17.0'
group: group::pipeline execution
type: gitlab_com_derisk
default_enabled: false
......@@ -10,8 +10,12 @@ class Junit
def parse!(xml_data, test_report, job:)
test_suite = test_report.get_suite(job.test_suite_name)
root = if Feature.enabled?(:allow_nokogiri_parse_huge_xml, job.project)
XmlConverter.new(xml_data).to_h
else
Hash.from_xml(xml_data)
end
root = Hash.from_xml(xml_data)
total_parsed = 0
max_test_cases = job.max_test_cases_per_report
......
# frozen_string_literal: true
# rubocop:disable Gitlab/NamespacedClass -- Base method live in the global namespace
module Gitlab
MAX_XML_SIZE = 30.megabytes
class XmlConverter < ActiveSupport::XMLConverter
# Override the default Nokogiri parser in to allow parsing huge XML files
def initialize(xml, disallowed_types = nil)
if xml.size > MAX_XML_SIZE
raise ArgumentError, format(_("The XML file must be less than %{max_size} MB."),
max_size: MAX_XML_SIZE / 1.megabyte)
end
doc = Nokogiri::XML(xml, &:huge)
raise doc.errors.first unless doc.errors.empty?
# These two variables are internally required by `ActiveSupport::XMLConverter`
@xml = normalize_keys(doc.to_hash)
@disallowed_types = disallowed_types || DISALLOWED_TYPES
end
end
end
# rubocop:enable Gitlab/NamespacedClass
......@@ -51402,6 +51402,9 @@ msgstr ""
msgid "The URLs for connecting to Elasticsearch. For clustering, add the URLs separated by commas."
msgstr ""
 
msgid "The XML file must be less than %{max_size} MB."
msgstr ""
msgid "The `/merge` quick action requires the SHA of the head of the branch."
msgstr ""
 
# frozen_string_literal: true
require 'fast_spec_helper'
require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
describe '#parse!' do
subject { described_class.new.parse!(junit, test_report, job: job) }
let(:job) { double(test_suite_name: 'rspec', max_test_cases_per_report: max_test_cases) }
let(:project) { build(:project) }
let(:job) { double(test_suite_name: 'rspec', max_test_cases_per_report: max_test_cases, project: project) }
let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
let(:test_suite) { test_report.get_suite(job.test_suite_name) }
let(:test_cases) { flattened_test_cases(test_suite) }
let(:max_test_cases) { 0 }
let(:junit) { '<testsuites></testsuites>' }
context 'when data is JUnit style XML' do
context 'when there are no <testcases> in <testsuite>' do
......@@ -379,7 +381,7 @@
end
context 'when data is not XML' do
let(:junit) { double(:random_trash) }
let(:junit) { double(:random_trash, size: 1) }
it 'attaches an error to the TestSuite object' do
expect { subject }.not_to raise_error
......@@ -521,6 +523,26 @@
end
end
it 'parses XML using XmlConverter instead of Hash.from_xml' do
expect(Gitlab::XmlConverter).to receive(:new).with(junit).once.and_call_original
expect(Hash).not_to receive(:from_xml)
subject
end
context 'when feature flag `allow_nokogiri_parse_huge_xml` is disabled' do
before do
stub_feature_flags(allow_nokogiri_parse_huge_xml: false)
end
it 'parses XML using Hash.from_xml instead of XmlConverter' do
expect(Gitlab::XmlConverter).not_to receive(:new)
expect(Hash).to receive(:from_xml).with(junit).once.and_call_original
subject
end
end
private
def flattened_test_cases(test_suite)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Gitlab::XmlConverter, feature_category: :pipeline_composition do
describe '#to_h' do
let(:xml_data) { '<root><key>value</key></root>' }
subject(:to_h) { described_class.new(xml_data).to_h }
context "when the xml is valid" do
let(:xml_data) { '<root><key>value</key></root>' }
it "parses the xml with huge option" do
expect(Nokogiri).to receive(:XML).and_wrap_original do |original_method, *args, &block|
expect(block).to be(Proc.new(&:huge))
original_method.call(*args, &block)
end
expect(to_h).to eq('root' => { 'key' => 'value' })
end
end
context "when the xml is invalid" do
let(:xml_data) { '<root><key>value</key>' }
it "raises an error" do
expect { to_h }.to raise_error(Nokogiri::XML::SyntaxError)
end
end
context "when the xml is too large" do
let(:xml_data) { instance_double(String, size: Gitlab::MAX_XML_SIZE + 1) }
it "raises an error" do
expect { to_h }.to raise_error(ArgumentError, "The XML file must be less than 30 MB.")
end
end
end
end
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册