From f7b1bf78bec716ae22cad8c512e933247047f3c3 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Wed, 13 Dec 2023 18:26:34 +0800 Subject: [PATCH 1/6] Add format_list method to handle Unordered and OrderedLists --- CHANGELOG.md | 5 +-- lib/nexpose/vulnerability.rb | 59 ++++++++++++++++++++++++------------ spec/fixtures/files/full.xml | 41 ++++++++++--------------- spec/nexpose_upload_spec.rb | 16 ++++++++++ 4 files changed, 75 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 161fcf3..a4236e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ v4.11.0 (XXX 2023) - Add port/protocol to evidences - - Use the details in as the OS node property - - Import `vulnerability.risk_score` as a new Issue field - Allow multiple evidence with the same test id & node address + - Format UnorderedList/OrderedList to textile + - Import `vulnerability.risk_score` as a new Issue field + - Use the details in as the OS node property v4.10.0 (September 2023) - Update gemspec links diff --git a/lib/nexpose/vulnerability.rb b/lib/nexpose/vulnerability.rb index 6363f83..0892cbe 100644 --- a/lib/nexpose/vulnerability.rb +++ b/lib/nexpose/vulnerability.rb @@ -20,7 +20,7 @@ def initialize(xml_node) def supported_tags [ # attributes - :added, :cvss_score, :cvss_vector, :modified, :nexpose_id, :pci_severity, + :added, :cvss_score, :cvss_vector, :modified, :nexpose_id, :pci_severity, :published, :risk_score, :severity, :title, # simple tags @@ -34,10 +34,9 @@ def supported_tags ] end - # This allows external callers (and specs) to check for implemented # properties - def respond_to?(method, include_private=false) + def respond_to?(method, include_private = false) return true if supported_tags.include?(method.to_sym) super end @@ -49,7 +48,6 @@ def respond_to?(method, include_private=false) # attribute, simple descendent or collection that it maps to in the XML # tree. def method_missing(method, *args) - # We could remove this check and return nil for any non-recognized tag. # The problem would be that it would make tricky to debug problems with # typos. For instance: <>.potr would return nil instead of raising an @@ -62,11 +60,11 @@ def method_missing(method, *args) # First we try the attributes. In Ruby we use snake_case, but in XML # CamelCase is used for some attributes translations_table = { - :nexpose_id => 'id', - :pci_severity => 'pciSeverity', - :risk_score => 'riskScore', - :cvss_score => 'cvssScore', - :cvss_vector =>'cvssVector' + nexpose_id: 'id', + pci_severity: 'pciSeverity', + risk_score: 'riskScore', + cvss_score: 'cvssScore', + cvss_vector: 'cvssVector' } method_name = translations_table.fetch(method, method.to_s) @@ -79,7 +77,8 @@ def method_missing(method, *args) # We need to clean up tags that have HTML content in them if tags_with_html_content.include?(method) - result = cleanup_html(tag) + result = format_list(tag) + result = cleanup_html(result) result = add_bc_to_ssl_cipher_list(result) if SSL_CIPHER_VULN_IDS.include?(@xml.attributes['id'].value) return result # And we need to clean up the tags with nested content in them @@ -96,7 +95,7 @@ def method_missing(method, *args) return @xml.xpath("//test[@id='#{vuln_id}']/Paragraph"). text.split("\n"). collect(&:strip). - reject{|line| line.empty?}.join("\n") + reject { |line| line.empty? }.join("\n") end nil @@ -106,13 +105,13 @@ def method_missing(method, *args) def add_bc_to_ssl_cipher_list(source) result = source.to_s - result.gsub!(/\n(.*?)!(.*?)/){"\nbc. #{ $1 }!#{ $2 }\n"} + result.gsub!(/\n(.*?)!(.*?)/) { "\nbc. #{ $1 }!#{ $2 }\n" } result end def cleanup_html(source) result = source.to_s - result.gsub!(/(.*?)<\/ContainerBlockElement>/m){|m| "#{ $1 }"} + result.gsub!(/(.*?)<\/ContainerBlockElement>/m) { |m| "#{ $1 }" } result.gsub!(/(\s*)(.*?)<\/Paragraph>(\s*)<\/Paragraph>/mi) do text = $2 text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@" @@ -121,11 +120,8 @@ def cleanup_html(source) text = $1 text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@" end - result.gsub!(/(.*?)<\/Paragraph>/m){|m| "#{ $1 }\n"} + result.gsub!(/(.*?)<\/Paragraph>/m) { |m| "#{ $1 }\n" } result.gsub!(/|<\/Paragraph>/, '') - result.gsub!(/(.*?)<\/UnorderedList>/m){|m| "#{ $2 }"} - result.gsub!(/(.*?)<\/OrderedList>/m){|m| "#{ $2 }"} - result.gsub!(/|<\/ListItem>/, '') result.gsub!(/ /, '') result.gsub!(/ /, '') result.gsub!(/\t\t/, '') @@ -141,10 +137,10 @@ def cleanup_nested(source) result = source.to_s result.gsub!(//, '') result.gsub!(/<\/references>/, '') - result.gsub!(/(.*?)<\/reference>/i) {"#{$1.strip}: #{$2.strip}\n"} + result.gsub!(/(.*?)<\/reference>/i) { "#{$1.strip}: #{$2.strip}\n" } result.gsub!(//, '') result.gsub!(/<\/tags>/, '') - result.gsub!(/(.*?)<\/tag>/) {"#{$1}\n"} + result.gsub!(/(.*?)<\/tag>/) { "#{$1}\n" } result.gsub!(/ /, '') result end @@ -157,5 +153,30 @@ def tags_with_nested_content [:references, :tags] end + def format_list(source) + source = source.to_s + return source unless source.include?('') || source.include?('') + + text = source.split(/<(?\/?(UnorderedList|OrderedList|ListItem)).*?>/) + result = '' + list_type = [] + text.each do |string| + string.strip! + case string + when 'UnorderedList', 'OrderedList' + list_type.push(string == 'UnorderedList' ? '*' : '#') + when '/UnorderedList', '/OrderedList' + list_type.pop + when 'ListItem' + result += ''.ljust(list_type.length, list_type.last) + else + if string != '/ListItem' && string.strip != '' + result += ' ' + string + "\n" + end + end + end + + result + end end end diff --git a/spec/fixtures/files/full.xml b/spec/fixtures/files/full.xml index a1bd7c8..922b76c 100644 --- a/spec/fixtures/files/full.xml +++ b/spec/fixtures/files/full.xml @@ -109,36 +109,27 @@ phase=8.259, freq=-141.24, error=11.32 + Microsoft Windows - You can remove inode information from the ETag header by adding the - following directive to your Apache config: - FileETag MTime Size + + Open the Windows Control Panel. + Select "Administrative Tools". + - OpenBSD - Download and apply the patch from: - - - - - - + Microsoft Windows - The OpenBSD team has released a - - - - - - - patchfor the Apache inode and pid leak problem. This patch can be applied - cleanly to 3.2 stable and rebuilt. Restart httpd for the changes to - take effect. OpenBSD 3.3 will ship with the patched httpd by default. - The patch can be applied to earlier 3.x versions of OpenBSD, but it - may require editing of the source code. - - + + Open the "Performance and Maintenance" control panel. + Select "Administrative Tools". + Restart the system for the changes to take effect. + + Microsoft Windows + + + Open the "Administrative Tools" control panel. + Restart the system for the changes to take effect. diff --git a/spec/nexpose_upload_spec.rb b/spec/nexpose_upload_spec.rb index 150ba37..f3a7ff7 100644 --- a/spec/nexpose_upload_spec.rb +++ b/spec/nexpose_upload_spec.rb @@ -167,6 +167,22 @@ @importer.import(file: @fixtures_dir + '/full.xml') end + + it 'formats the list to textile lists' do + expect(@content_service).to receive(:create_issue) do |args| + expect(args[:text]).to include("#[Title]#\nApache HTTPD: error responses can expose cookies (CVE-2012-0053)") + OpenStruct.new(args) + end.once + + expect(@content_service).to receive(:create_issue) do |args| + expect(args[:text]).to include('* Microsoft Windows') + expect(args[:text]).to include('## Open the Windows Control Panel.') + expect(args[:text]).to include('## Select "Administrative Tools".') + OpenStruct.new(args) + end.once + + @importer.import(file: @fixtures_dir + '/full.xml') + end end describe 'Importer: Full with duplicate nodes' do From 188ef866416eb7eadea6d761f9b8b6e21b83c487 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Tue, 19 Dec 2023 13:47:16 +0800 Subject: [PATCH 2/6] Strip the list item and remove the paragraph tags --- lib/nexpose/vulnerability.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/nexpose/vulnerability.rb b/lib/nexpose/vulnerability.rb index 0892cbe..fe88764 100644 --- a/lib/nexpose/vulnerability.rb +++ b/lib/nexpose/vulnerability.rb @@ -171,7 +171,12 @@ def format_list(source) result += ''.ljust(list_type.length, list_type.last) else if string != '/ListItem' && string.strip != '' - result += ' ' + string + "\n" + # Nexpose wraps the list items around tags. These tags + # will run through the cleanup_html method and be given newlines (\n). + # These newlines will break the lists in the export so we're cleaning + # up the tags here. + string.gsub!(/|<\/Paragraph>/, '') + result += ' ' + string.strip + "\n" end end end From 89c996239b3cf791795888618cb943459a846f3e Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 23 May 2024 19:29:40 +0800 Subject: [PATCH 3/6] Add ListFormatter --- lib/dradis/plugins/nexpose.rb | 1 + lib/dradis/plugins/nexpose/list_formatter.rb | 36 ++++++++++++++++++++ lib/nexpose/vulnerability.rb | 33 +----------------- spec/fixtures/files/lists.xml | 24 +++++++++++++ spec/list_formatter_spec.rb | 22 ++++++++++++ 5 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 lib/dradis/plugins/nexpose/list_formatter.rb create mode 100644 spec/fixtures/files/lists.xml create mode 100644 spec/list_formatter_spec.rb diff --git a/lib/dradis/plugins/nexpose.rb b/lib/dradis/plugins/nexpose.rb index 8d7b194..e55a4c2 100644 --- a/lib/dradis/plugins/nexpose.rb +++ b/lib/dradis/plugins/nexpose.rb @@ -8,4 +8,5 @@ module Nexpose require 'dradis/plugins/nexpose/engine' require 'dradis/plugins/nexpose/field_processor' require 'dradis/plugins/nexpose/importer' +require 'dradis/plugins/nexpose/list_formatter' require 'dradis/plugins/nexpose/version' diff --git a/lib/dradis/plugins/nexpose/list_formatter.rb b/lib/dradis/plugins/nexpose/list_formatter.rb new file mode 100644 index 0000000..96156ca --- /dev/null +++ b/lib/dradis/plugins/nexpose/list_formatter.rb @@ -0,0 +1,36 @@ +module Dradis::Plugins::Nexpose + class ListFormatter + def format_list(source) + # Add node in case the source is group of lists in the same level + xml = Nokogiri::XML("#{source}") { |conf| conf.noblanks } + xml = xml.at_xpath('./root') + + format_nexpose_list(xml) + end + + private + + def format_nexpose_list(xml, depth = 1) + xml.xpath('./UnorderedList | ./OrderedList').map do |list| + list_type = list.name == 'UnorderedList' ? '*' : '#' + + list.xpath('./ListItem').map do |list_item| + paragraphs = list_item.xpath('./Paragraph') + + if paragraphs.any? + paragraphs.map do |paragraph| + # nodes can either have more lists or just text + if paragraph.xpath('./UnorderedList | ./OrderedList').any? + format_nexpose_list(paragraph, depth + 1) + else + ''.ljust(depth, list_type) + ' ' + paragraph.text + end + end.join("\n") + else + ''.ljust(depth, list_type) + ' ' + list_item.text + end + end.join("\n") + end.join("\n") + end + end +end diff --git a/lib/nexpose/vulnerability.rb b/lib/nexpose/vulnerability.rb index fe88764..43f1061 100644 --- a/lib/nexpose/vulnerability.rb +++ b/lib/nexpose/vulnerability.rb @@ -77,7 +77,7 @@ def method_missing(method, *args) # We need to clean up tags that have HTML content in them if tags_with_html_content.include?(method) - result = format_list(tag) + result = Dradis::Plugins::Nexpose::ListFormatter.new.format_list(tag) result = cleanup_html(result) result = add_bc_to_ssl_cipher_list(result) if SSL_CIPHER_VULN_IDS.include?(@xml.attributes['id'].value) return result @@ -152,36 +152,5 @@ def tags_with_html_content def tags_with_nested_content [:references, :tags] end - - def format_list(source) - source = source.to_s - return source unless source.include?('') || source.include?('') - - text = source.split(/<(?\/?(UnorderedList|OrderedList|ListItem)).*?>/) - result = '' - list_type = [] - text.each do |string| - string.strip! - case string - when 'UnorderedList', 'OrderedList' - list_type.push(string == 'UnorderedList' ? '*' : '#') - when '/UnorderedList', '/OrderedList' - list_type.pop - when 'ListItem' - result += ''.ljust(list_type.length, list_type.last) - else - if string != '/ListItem' && string.strip != '' - # Nexpose wraps the list items around tags. These tags - # will run through the cleanup_html method and be given newlines (\n). - # These newlines will break the lists in the export so we're cleaning - # up the tags here. - string.gsub!(/|<\/Paragraph>/, '') - result += ' ' + string.strip + "\n" - end - end - end - - result - end end end diff --git a/spec/fixtures/files/lists.xml b/spec/fixtures/files/lists.xml new file mode 100644 index 0000000..e9ef161 --- /dev/null +++ b/spec/fixtures/files/lists.xml @@ -0,0 +1,24 @@ + + + Microsoft Windows + + + Open the Windows Control Panel. + Select "Administrative Tools". + + + + + Microsoft Windows + + + Open the "Performance and Maintenance" control panel. + Select "Administrative Tools". + Restart the system for the changes to take effect. + + Microsoft Windows + + + Open the "Administrative Tools" control panel. + Restart the system for the changes to take effect. + diff --git a/spec/list_formatter_spec.rb b/spec/list_formatter_spec.rb new file mode 100644 index 0000000..d164525 --- /dev/null +++ b/spec/list_formatter_spec.rb @@ -0,0 +1,22 @@ +# Run the spec by running the command: rspec spec/list_formatter_spec.rb" + +require 'spec_helper' + +describe Dradis::Plugins::Nexpose::ListFormatter do + let(:source) { File.read(File.expand_path('../fixtures/files/lists.xml', __FILE__)) } + + it 'parses the and elements' do + expect(Dradis::Plugins::Nexpose::ListFormatter.new.format_list(source)).to eq( + "* Microsoft Windows\n"\ + "## Open the Windows Control Panel.\n"\ + "## Select \"Administrative Tools\".\n"\ + "* Microsoft Windows\n"\ + "## Open the \"Performance and Maintenance\" control panel.\n"\ + "## Select \"Administrative Tools\".\n"\ + "## Restart the system for the changes to take effect.\n"\ + "* Microsoft Windows\n"\ + "## Open the \"Administrative Tools\" control panel.\n"\ + '## Restart the system for the changes to take effect.' + ) + end +end From 78a7a3dc058a59ba02036a7e9a49e8946d440fa3 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 23 May 2024 20:28:52 +0800 Subject: [PATCH 4/6] Add case for non XML fields --- lib/dradis/plugins/nexpose/list_formatter.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/dradis/plugins/nexpose/list_formatter.rb b/lib/dradis/plugins/nexpose/list_formatter.rb index 96156ca..84eef2f 100644 --- a/lib/dradis/plugins/nexpose/list_formatter.rb +++ b/lib/dradis/plugins/nexpose/list_formatter.rb @@ -1,11 +1,17 @@ module Dradis::Plugins::Nexpose class ListFormatter def format_list(source) - # Add node in case the source is group of lists in the same level + # Add node in case the source is an invalid xml xml = Nokogiri::XML("#{source}") { |conf| conf.noblanks } - xml = xml.at_xpath('./root') + xml = xml.at_xpath('./root/ContainerBlockElement') - format_nexpose_list(xml) + return source unless xml + + if xml.xpath('./UnorderedList | ./OrderedList').any? + format_nexpose_list(xml) + else + source + end end private From 8e343b59e6180ef9774e30c1155609481539ddae Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Fri, 24 May 2024 14:50:21 +0800 Subject: [PATCH 5/6] Use a more general XML Formatter --- lib/dradis/plugins/nexpose.rb | 2 +- lib/dradis/plugins/nexpose/list_formatter.rb | 42 ---------- lib/dradis/plugins/nexpose/xml_formatter.rb | 80 +++++++++++++++++++ lib/nexpose/vulnerability.rb | 43 +--------- ...ormatter_spec.rb => xml_formatter_spec.rb} | 6 +- 5 files changed, 88 insertions(+), 85 deletions(-) delete mode 100644 lib/dradis/plugins/nexpose/list_formatter.rb create mode 100644 lib/dradis/plugins/nexpose/xml_formatter.rb rename spec/{list_formatter_spec.rb => xml_formatter_spec.rb} (76%) diff --git a/lib/dradis/plugins/nexpose.rb b/lib/dradis/plugins/nexpose.rb index e646680..85a385c 100644 --- a/lib/dradis/plugins/nexpose.rb +++ b/lib/dradis/plugins/nexpose.rb @@ -8,6 +8,6 @@ module Nexpose require 'dradis/plugins/nexpose/engine' require 'dradis/plugins/nexpose/field_processor' require 'dradis/plugins/nexpose/importer' -require 'dradis/plugins/nexpose/list_formatter' require 'dradis/plugins/nexpose/mapping' require 'dradis/plugins/nexpose/version' +require 'dradis/plugins/nexpose/xml_formatter' diff --git a/lib/dradis/plugins/nexpose/list_formatter.rb b/lib/dradis/plugins/nexpose/list_formatter.rb deleted file mode 100644 index 84eef2f..0000000 --- a/lib/dradis/plugins/nexpose/list_formatter.rb +++ /dev/null @@ -1,42 +0,0 @@ -module Dradis::Plugins::Nexpose - class ListFormatter - def format_list(source) - # Add node in case the source is an invalid xml - xml = Nokogiri::XML("#{source}") { |conf| conf.noblanks } - xml = xml.at_xpath('./root/ContainerBlockElement') - - return source unless xml - - if xml.xpath('./UnorderedList | ./OrderedList').any? - format_nexpose_list(xml) - else - source - end - end - - private - - def format_nexpose_list(xml, depth = 1) - xml.xpath('./UnorderedList | ./OrderedList').map do |list| - list_type = list.name == 'UnorderedList' ? '*' : '#' - - list.xpath('./ListItem').map do |list_item| - paragraphs = list_item.xpath('./Paragraph') - - if paragraphs.any? - paragraphs.map do |paragraph| - # nodes can either have more lists or just text - if paragraph.xpath('./UnorderedList | ./OrderedList').any? - format_nexpose_list(paragraph, depth + 1) - else - ''.ljust(depth, list_type) + ' ' + paragraph.text - end - end.join("\n") - else - ''.ljust(depth, list_type) + ' ' + list_item.text - end - end.join("\n") - end.join("\n") - end - end -end diff --git a/lib/dradis/plugins/nexpose/xml_formatter.rb b/lib/dradis/plugins/nexpose/xml_formatter.rb new file mode 100644 index 0000000..b37eb7b --- /dev/null +++ b/lib/dradis/plugins/nexpose/xml_formatter.rb @@ -0,0 +1,80 @@ +module Dradis::Plugins::Nexpose + class XmlFormatter + def cleanup_html(source) + result = source.to_s + result.gsub!(/(.*?)<\/ContainerBlockElement>/m) { |m| "#{ $1 }" } + result.gsub!(/(\s*)(.*?)<\/Paragraph>(\s*)<\/Paragraph>/mi) do + text = $2 + text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@" + end + result.gsub!(/(.*?)<\/Paragraph>/mi) do + text = $1 + text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@" + end + result.gsub!(/(.*?)<\/Paragraph>/m) { |m| "#{ $1 }\n" } + result.gsub!(/|<\/Paragraph>/, '') + result.gsub!(/ /, '') + result.gsub!(/ /, '') + result.gsub!(/\t\t/, '') + result.gsub!(/(.*?)<\/URLLink>/im) { "\"#{$4.strip}\":#{$2.strip} " } + result.gsub!(//i) { "\"#{$1.strip}\":#{$3.strip} " } + result.gsub!(//i) { "\"#{$3.strip}\":#{$1.strip} " } + result.gsub!(/>/, '>') + result.gsub!(/</, '<') + result + end + + def cleanup_nested(source) + result = source.to_s + result.gsub!(//, '') + result.gsub!(/<\/references>/, '') + result.gsub!(/(.*?)<\/reference>/i) { "#{$1.strip}: #{$2.strip}\n" } + result.gsub!(//, '') + result.gsub!(/<\/tags>/, '') + result.gsub!(/(.*?)<\/tag>/) { "#{$1}\n" } + result.gsub!(/ /, '') + result + end + + def format_list(source) + # Add node in case the source is an invalid xml + xml = Nokogiri::XML("#{source}") { |conf| conf.noblanks } + xml = xml.at_xpath('./root/ContainerBlockElement') + + return source unless xml + + if xml.xpath('./UnorderedList | ./OrderedList').any? + format_nexpose_list(xml) + else + source + end + end + + private + + def format_nexpose_list(xml, depth = 1) + xml.xpath('./UnorderedList | ./OrderedList').map do |list| + list_item_element = list.name == 'UnorderedList' ? '*' : '#' + + list.xpath('./ListItem').map do |list_item| + paragraphs = list_item.xpath('./Paragraph') + + list_item_text = if paragraphs.any? + paragraphs.map do |paragraph| + # nodes can either have more lists or just text + if paragraph.xpath('./UnorderedList | ./OrderedList').any? + format_nexpose_list(paragraph, depth + 1) + else + cleanup_html(paragraph.to_s) + end + end.join("\n") + else + list_item.text + end + + ''.ljust(depth, list_item_element) + ' ' + list_item_text + end.join("\n") + end.join("\n") + end + end +end diff --git a/lib/nexpose/vulnerability.rb b/lib/nexpose/vulnerability.rb index 43f1061..5864e4d 100644 --- a/lib/nexpose/vulnerability.rb +++ b/lib/nexpose/vulnerability.rb @@ -76,14 +76,15 @@ def method_missing(method, *args) nest = @xml.xpath("./#{method_name}").first # We need to clean up tags that have HTML content in them + formatter = Dradis::Plugins::Nexpose::XmlFormatter.new if tags_with_html_content.include?(method) - result = Dradis::Plugins::Nexpose::ListFormatter.new.format_list(tag) - result = cleanup_html(result) + result = formatter.format_list(tag) + result = formatter.cleanup_html(result) result = add_bc_to_ssl_cipher_list(result) if SSL_CIPHER_VULN_IDS.include?(@xml.attributes['id'].value) return result # And we need to clean up the tags with nested content in them elsif tags_with_nested_content.include?(method) - return cleanup_nested(nest) + return formatter.cleanup_nested(nest) else return tag end @@ -109,42 +110,6 @@ def add_bc_to_ssl_cipher_list(source) result end - def cleanup_html(source) - result = source.to_s - result.gsub!(/(.*?)<\/ContainerBlockElement>/m) { |m| "#{ $1 }" } - result.gsub!(/(\s*)(.*?)<\/Paragraph>(\s*)<\/Paragraph>/mi) do - text = $2 - text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@" - end - result.gsub!(/(.*?)<\/Paragraph>/mi) do - text = $1 - text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@" - end - result.gsub!(/(.*?)<\/Paragraph>/m) { |m| "#{ $1 }\n" } - result.gsub!(/|<\/Paragraph>/, '') - result.gsub!(/ /, '') - result.gsub!(/ /, '') - result.gsub!(/\t\t/, '') - result.gsub!(/(.*?)<\/URLLink>/im) { "\"#{$4.strip}\":#{$2.strip} " } - result.gsub!(//i) { "\"#{$1.strip}\":#{$3.strip} " } - result.gsub!(//i) { "\"#{$3.strip}\":#{$1.strip} " } - result.gsub!(/>/, '>') - result.gsub!(/</, '<') - result - end - - def cleanup_nested(source) - result = source.to_s - result.gsub!(//, '') - result.gsub!(/<\/references>/, '') - result.gsub!(/(.*?)<\/reference>/i) { "#{$1.strip}: #{$2.strip}\n" } - result.gsub!(//, '') - result.gsub!(/<\/tags>/, '') - result.gsub!(/(.*?)<\/tag>/) { "#{$1}\n" } - result.gsub!(/ /, '') - result - end - def tags_with_html_content [:description, :solution] end diff --git a/spec/list_formatter_spec.rb b/spec/xml_formatter_spec.rb similarity index 76% rename from spec/list_formatter_spec.rb rename to spec/xml_formatter_spec.rb index d164525..330dfa1 100644 --- a/spec/list_formatter_spec.rb +++ b/spec/xml_formatter_spec.rb @@ -1,12 +1,12 @@ -# Run the spec by running the command: rspec spec/list_formatter_spec.rb" +# Run the spec by running the command: rspec spec/xml_formatter_spec.rb" require 'spec_helper' -describe Dradis::Plugins::Nexpose::ListFormatter do +describe Dradis::Plugins::Nexpose::XmlFormatter do let(:source) { File.read(File.expand_path('../fixtures/files/lists.xml', __FILE__)) } it 'parses the and elements' do - expect(Dradis::Plugins::Nexpose::ListFormatter.new.format_list(source)).to eq( + expect(Dradis::Plugins::Nexpose::XmlFormatter.new.format_list(source)).to eq( "* Microsoft Windows\n"\ "## Open the Windows Control Panel.\n"\ "## Select \"Administrative Tools\".\n"\ From 38249a46f76581b3fd0a98f4c772d053480aacee Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Fri, 24 May 2024 15:54:38 +0800 Subject: [PATCH 6/6] Aggregate methods and remove unnecessary xml root check --- lib/dradis/plugins/nexpose/xml_formatter.rb | 45 ++++++++++--------- lib/nexpose/vulnerability.rb | 3 +- spec/fixtures/files/lists.xml | 50 +++++++++++---------- spec/xml_formatter_spec.rb | 3 +- 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/lib/dradis/plugins/nexpose/xml_formatter.rb b/lib/dradis/plugins/nexpose/xml_formatter.rb index b37eb7b..5879977 100644 --- a/lib/dradis/plugins/nexpose/xml_formatter.rb +++ b/lib/dradis/plugins/nexpose/xml_formatter.rb @@ -1,5 +1,11 @@ module Dradis::Plugins::Nexpose class XmlFormatter + def format_html_content(source) + result = format_list(source) + + cleanup_html(result) + end + def cleanup_html(source) result = source.to_s result.gsub!(/(.*?)<\/ContainerBlockElement>/m) { |m| "#{ $1 }" } @@ -36,22 +42,16 @@ def cleanup_nested(source) result end - def format_list(source) - # Add node in case the source is an invalid xml - xml = Nokogiri::XML("#{source}") { |conf| conf.noblanks } - xml = xml.at_xpath('./root/ContainerBlockElement') + private - return source unless xml - - if xml.xpath('./UnorderedList | ./OrderedList').any? - format_nexpose_list(xml) + def format_list(source) + if source.xpath('./UnorderedList | ./OrderedList').any? + format_nexpose_list(source) else source end end - private - def format_nexpose_list(xml, depth = 1) xml.xpath('./UnorderedList | ./OrderedList').map do |list| list_item_element = list.name == 'UnorderedList' ? '*' : '#' @@ -59,18 +59,19 @@ def format_nexpose_list(xml, depth = 1) list.xpath('./ListItem').map do |list_item| paragraphs = list_item.xpath('./Paragraph') - list_item_text = if paragraphs.any? - paragraphs.map do |paragraph| - # nodes can either have more lists or just text - if paragraph.xpath('./UnorderedList | ./OrderedList').any? - format_nexpose_list(paragraph, depth + 1) - else - cleanup_html(paragraph.to_s) - end - end.join("\n") - else - list_item.text - end + list_item_text = + if paragraphs.any? + paragraphs.map do |paragraph| + # nodes can either have more lists or just text + if paragraph.xpath('./UnorderedList | ./OrderedList').any? + format_nexpose_list(paragraph, depth + 1) + else + cleanup_html(paragraph.to_s).chomp + end + end.join("\n") + else + list_item.text + end ''.ljust(depth, list_item_element) + ' ' + list_item_text end.join("\n") diff --git a/lib/nexpose/vulnerability.rb b/lib/nexpose/vulnerability.rb index 5864e4d..cc1866b 100644 --- a/lib/nexpose/vulnerability.rb +++ b/lib/nexpose/vulnerability.rb @@ -78,8 +78,7 @@ def method_missing(method, *args) # We need to clean up tags that have HTML content in them formatter = Dradis::Plugins::Nexpose::XmlFormatter.new if tags_with_html_content.include?(method) - result = formatter.format_list(tag) - result = formatter.cleanup_html(result) + result = formatter.format_html_content(tag) result = add_bc_to_ssl_cipher_list(result) if SSL_CIPHER_VULN_IDS.include?(@xml.attributes['id'].value) return result # And we need to clean up the tags with nested content in them diff --git a/spec/fixtures/files/lists.xml b/spec/fixtures/files/lists.xml index e9ef161..ef7b87d 100644 --- a/spec/fixtures/files/lists.xml +++ b/spec/fixtures/files/lists.xml @@ -1,24 +1,26 @@ - - - Microsoft Windows - - - Open the Windows Control Panel. - Select "Administrative Tools". - - - - - Microsoft Windows - - - Open the "Performance and Maintenance" control panel. - Select "Administrative Tools". - Restart the system for the changes to take effect. - - Microsoft Windows - - - Open the "Administrative Tools" control panel. - Restart the system for the changes to take effect. - + + + + Microsoft Windows + + + Open the Windows Control Panel. + Select "Administrative Tools". + + + + + Microsoft Windows + + + Open the "Performance and Maintenance" control panel. + Select "Administrative Tools". + Restart the system for the changes to take effect. + + Microsoft Windows + + + Open the "Administrative Tools" control panel. + Restart the system for the changes to take effect. + + diff --git a/spec/xml_formatter_spec.rb b/spec/xml_formatter_spec.rb index 330dfa1..d6a0a85 100644 --- a/spec/xml_formatter_spec.rb +++ b/spec/xml_formatter_spec.rb @@ -6,7 +6,8 @@ let(:source) { File.read(File.expand_path('../fixtures/files/lists.xml', __FILE__)) } it 'parses the and elements' do - expect(Dradis::Plugins::Nexpose::XmlFormatter.new.format_list(source)).to eq( + xml = Nokogiri::XML(source).at_xpath('./ContainerBlockElement') + expect(Dradis::Plugins::Nexpose::XmlFormatter.new.format_html_content(xml)).to eq( "* Microsoft Windows\n"\ "## Open the Windows Control Panel.\n"\ "## Select \"Administrative Tools\".\n"\