Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add format_list method to handle Unordered and OrderedLists #65

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v4.12.0 (Month 2024)
- Format UnorderedList/OrderedList to textile

v4.12.0 (May 2024)
- Migrate integration to use Mappings Manager
- Update Dradis links in README
Expand Down
1 change: 1 addition & 0 deletions lib/dradis/plugins/nexpose.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ module Nexpose
require 'dradis/plugins/nexpose/importer'
require 'dradis/plugins/nexpose/mapping'
require 'dradis/plugins/nexpose/version'
require 'dradis/plugins/nexpose/xml_formatter'
81 changes: 81 additions & 0 deletions lib/dradis/plugins/nexpose/xml_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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>(.*?)<\/ContainerBlockElement>/m) { |m| "#{ $1 }" }
result.gsub!(/<Paragraph preformat=\"true\">(\s*)<Paragraph preformat=\"true\">(.*?)<\/Paragraph>(\s*)<\/Paragraph>/mi) do
text = $2
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
end
result.gsub!(/<Paragraph preformat=\"true\">(.*?)<\/Paragraph>/mi) do
text = $1
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
end
result.gsub!(/<Paragraph>(.*?)<\/Paragraph>/m) { |m| "#{ $1 }\n" }
result.gsub!(/<Paragraph>|<\/Paragraph>/, '')
result.gsub!(/ /, '')
result.gsub!(/ /, '')
result.gsub!(/\t\t/, '')
result.gsub!(/<URLLink(.*)LinkURL=\"(.*?)\"(.*?)>(.*?)<\/URLLink>/im) { "\"#{$4.strip}\":#{$2.strip} " }
result.gsub!(/<URLLink LinkTitle=\"(.*?)\"(.*?)LinkURL=\"(.*?)\"\/>/i) { "\"#{$1.strip}\":#{$3.strip} " }
result.gsub!(/<URLLink LinkURL=\"(.*?)\"(.*?)LinkTitle=\"(.*?)\"\/>/i) { "\"#{$3.strip}\":#{$1.strip} " }
result.gsub!(/&gt;/, '>')
result.gsub!(/&lt;/, '<')
result
end

def cleanup_nested(source)
result = source.to_s
result.gsub!(/<references>/, '')
result.gsub!(/<\/references>/, '')
result.gsub!(/<reference source=\"(.*?)\">(.*?)<\/reference>/i) { "#{$1.strip}: #{$2.strip}\n" }
result.gsub!(/<tags>/, '')
result.gsub!(/<\/tags>/, '')
result.gsub!(/<tag>(.*?)<\/tag>/) { "#{$1}\n" }
result.gsub!(/ /, '')
result
end

private

def format_list(source)
if source.xpath('./UnorderedList | ./OrderedList').any?
format_nexpose_list(source)
else
source
end
end

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|
# <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")
end.join("\n")
end
end
end
65 changes: 12 additions & 53 deletions lib/nexpose/vulnerability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -78,13 +76,14 @@ 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 = cleanup_html(tag)
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
elsif tags_with_nested_content.include?(method)
return cleanup_nested(nest)
return formatter.cleanup_nested(nest)
else
return tag
end
Expand All @@ -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
Expand All @@ -106,46 +105,7 @@ 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
end

def cleanup_html(source)
result = source.to_s
result.gsub!(/<ContainerBlockElement>(.*?)<\/ContainerBlockElement>/m){|m| "#{ $1 }"}
result.gsub!(/<Paragraph preformat=\"true\">(\s*)<Paragraph preformat=\"true\">(.*?)<\/Paragraph>(\s*)<\/Paragraph>/mi) do
text = $2
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
end
result.gsub!(/<Paragraph preformat=\"true\">(.*?)<\/Paragraph>/mi) do
text = $1
text[/\n/] ? "\nbc.. #{ text }\n\np. " : "@#{text}@"
end
result.gsub!(/<Paragraph>(.*?)<\/Paragraph>/m){|m| "#{ $1 }\n"}
result.gsub!(/<Paragraph>|<\/Paragraph>/, '')
result.gsub!(/<UnorderedList(.*?)>(.*?)<\/UnorderedList>/m){|m| "#{ $2 }"}
result.gsub!(/<OrderedList(.*?)>(.*?)<\/OrderedList>/m){|m| "#{ $2 }"}
result.gsub!(/<ListItem>|<\/ListItem>/, '')
result.gsub!(/ /, '')
result.gsub!(/ /, '')
result.gsub!(/\t\t/, '')
result.gsub!(/<URLLink(.*)LinkURL=\"(.*?)\"(.*?)>(.*?)<\/URLLink>/im) { "\"#{$4.strip}\":#{$2.strip} " }
result.gsub!(/<URLLink LinkTitle=\"(.*?)\"(.*?)LinkURL=\"(.*?)\"\/>/i) { "\"#{$1.strip}\":#{$3.strip} " }
result.gsub!(/<URLLink LinkURL=\"(.*?)\"(.*?)LinkTitle=\"(.*?)\"\/>/i) { "\"#{$3.strip}\":#{$1.strip} " }
result.gsub!(/&gt;/, '>')
result.gsub!(/&lt;/, '<')
result
end

def cleanup_nested(source)
result = source.to_s
result.gsub!(/<references>/, '')
result.gsub!(/<\/references>/, '')
result.gsub!(/<reference source=\"(.*?)\">(.*?)<\/reference>/i) {"#{$1.strip}: #{$2.strip}\n"}
result.gsub!(/<tags>/, '')
result.gsub!(/<\/tags>/, '')
result.gsub!(/<tag>(.*?)<\/tag>/) {"#{$1}\n"}
result.gsub!(/ /, '')
result.gsub!(/\n(.*?)!(.*?)/) { "\nbc. #{ $1 }!#{ $2 }\n" }
result
end

Expand All @@ -156,6 +116,5 @@ def tags_with_html_content
def tags_with_nested_content
[:references, :tags]
end

end
end
20 changes: 18 additions & 2 deletions spec/fixtures/files/full.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,27 @@
<ContainerBlockElement>
<UnorderedList>
<ListItem>
<Paragraph>Microsoft Windows</Paragraph>
<Paragraph>
<Paragraph>You can remove inode information from the ETag header by adding the following directive to your Apache config:</Paragraph>
<Paragraph preformat="true">FileETag MTime Size</Paragraph>
<OrderedList>
<ListItem>Open the Windows Control Panel.</ListItem>
<ListItem>Select &quot;Administrative Tools&quot;.</ListItem>
</OrderedList>
</Paragraph>
</ListItem>
<ListItem>
<Paragraph>Microsoft Windows</Paragraph>
<Paragraph>
<OrderedList>
<ListItem>Open the &quot;Performance and Maintenance&quot; control panel.</ListItem>
<ListItem>Select &quot;Administrative Tools&quot;.</ListItem>
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
<ListItem>
<Paragraph>Microsoft Windows</Paragraph>
<Paragraph>
<OrderedList>
<ListItem>Open the &quot;Administrative Tools&quot; control panel.</ListItem>
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
<ListItem>
<Paragraph>OpenBSD</Paragraph>
<Paragraph>Download and apply the patch from:
Expand Down
26 changes: 26 additions & 0 deletions spec/fixtures/files/lists.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<ContainerBlockElement>
<UnorderedList>
<ListItem>
<Paragraph>Microsoft Windows</Paragraph>
<Paragraph>
<OrderedList>
<ListItem>Open the Windows Control Panel.</ListItem>
<ListItem>Select &quot;Administrative Tools&quot;.</ListItem>
</OrderedList>
</Paragraph>
</ListItem>
<ListItem>
<Paragraph>Microsoft Windows</Paragraph>
<Paragraph>
<OrderedList>
<ListItem>Open the &quot;Performance and Maintenance&quot; control panel.</ListItem>
<ListItem>Select &quot;Administrative Tools&quot;.</ListItem>
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
<ListItem>
<Paragraph>Microsoft Windows</Paragraph>
<Paragraph>
<OrderedList>
<ListItem>Open the &quot;Administrative Tools&quot; control panel.</ListItem>
<ListItem>Restart the system for the changes to take effect.</ListItem></OrderedList></Paragraph></ListItem>
</UnorderedList>
</ContainerBlockElement>
16 changes: 16 additions & 0 deletions spec/nexpose_upload_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,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
Expand Down
23 changes: 23 additions & 0 deletions spec/xml_formatter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Run the spec by running the command: rspec spec/xml_formatter_spec.rb"

require 'spec_helper'

describe Dradis::Plugins::Nexpose::XmlFormatter do
let(:source) { File.read(File.expand_path('../fixtures/files/lists.xml', __FILE__)) }

it 'parses the <UnorderedList> and <OrderedList> elements' do
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"\
"* 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