offrep 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +57 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/pry +8 -0
- data/bin/setup +8 -0
- data/examples/nessus-merge.rb +18 -0
- data/exe/offrep +159 -0
- data/lib/offrep/commonxml.rb +303 -0
- data/lib/offrep/nessusxml.rb +107 -0
- data/lib/offrep/report.rb +43 -0
- data/lib/offrep/translation.rb +26 -0
- data/lib/offrep/version.rb +3 -0
- data/lib/offrep.rb +9 -0
- data/offrep.gemspec +28 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eafbd69d3335c844f4f092a004cf12caeaffbbb8
|
4
|
+
data.tar.gz: ac7d89b6a74d82d472f5f6acfc2a7f25063cbe59
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b651ac2f4433b01092763914e57dcc56f39ff50a038016986314be98b97bb7bdf79d450954a55e57486ae461e568e0c8913ebc05e861480083be3708549fa3a5
|
7
|
+
data.tar.gz: 3508a2edc4c13a1db66fa9ff201d9f6190197a9991cb243e2db9bae11f9e5a3053b474c4aa66d7402d161acfe42fb92091673320fcb385e172148ad0b7dd8bed
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at kost@linux.hr. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 kost
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Offrep
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/offrep`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'offrep'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install offrep
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```
|
26
|
+
```
|
27
|
+
|
28
|
+
## Scripting
|
29
|
+
|
30
|
+
```
|
31
|
+
require "bundler/setup"
|
32
|
+
require "offrep"
|
33
|
+
|
34
|
+
ot=Offrep::Translation.new
|
35
|
+
oc=Offrep::CommonXML.new
|
36
|
+
on=Offrep::NessusXML.new
|
37
|
+
oc.readxml(File.open('bla.commonxml'))
|
38
|
+
ot.readxml(File.open('nessus-translations-language.xml'))
|
39
|
+
mis=oc.translate(ot.xml)
|
40
|
+
puts oc.xmldoc.to_s
|
41
|
+
```
|
42
|
+
|
43
|
+
## Development
|
44
|
+
|
45
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
46
|
+
|
47
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/offrep. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
52
|
+
|
53
|
+
|
54
|
+
## License
|
55
|
+
|
56
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
57
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "offrep"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/pry
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# nessus-merge.rb fileout *.nessus
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "offrep"
|
6
|
+
|
7
|
+
out=ARGV.shift
|
8
|
+
ot=Offrep::Translation.new
|
9
|
+
oc=Offrep::CommonXML.new
|
10
|
+
on=Offrep::NessusXML.new
|
11
|
+
on.readxml(File.open(ARGV.shift))
|
12
|
+
while opt = ARGV.shift do
|
13
|
+
puts opt
|
14
|
+
on.mergexml(opt)
|
15
|
+
end
|
16
|
+
File.write(out+".nessusxml",on.xmldoc.to_s)
|
17
|
+
File.write(out+".commonxml",on.to_common.to_s)
|
18
|
+
|
data/exe/offrep
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# require "bundler/setup"
|
4
|
+
require "offrep"
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
|
10
|
+
$PRGNAME="offrep"
|
11
|
+
$options = {}
|
12
|
+
$options['loglevel'] = 'WARN'
|
13
|
+
$options['logname'] = nil
|
14
|
+
|
15
|
+
# helpful class for logger
|
16
|
+
class MultiDelegator
|
17
|
+
def initialize(*targets)
|
18
|
+
@targets = targets
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.delegate(*methods)
|
22
|
+
methods.each do |m|
|
23
|
+
define_method(m) do |*args|
|
24
|
+
@targets.map { |t| t.send(m, *args) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
class <<self
|
31
|
+
alias to new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
begin
|
37
|
+
optyaml = YAML::load_file(ENV['HOME']+'/.offrep')
|
38
|
+
rescue # Errno::ENOENT
|
39
|
+
end
|
40
|
+
|
41
|
+
if optyaml != nil then
|
42
|
+
$options.merge!(optyaml)
|
43
|
+
end
|
44
|
+
|
45
|
+
# initialize logger
|
46
|
+
if $options['logname'] != nil then
|
47
|
+
log_file = File.open($options['logname'], 'a')
|
48
|
+
@log = Logger.new MultiDelegator.delegate(:write, :close).to(STDERR, log_file)
|
49
|
+
else
|
50
|
+
@log = Logger.new MultiDelegator.delegate(:write, :close).to(STDERR)
|
51
|
+
end
|
52
|
+
loglevel = Logger.const_get $options['loglevel'] # Logger::INFO # default is ::WARN
|
53
|
+
@log.level = loglevel
|
54
|
+
|
55
|
+
|
56
|
+
OptionParser.new do |opts|
|
57
|
+
opts.banner = "Usage: #{$PRGNAME} [options]"
|
58
|
+
|
59
|
+
opts.on("-h", "--help", "Prints this help") do
|
60
|
+
puts opts
|
61
|
+
exit
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
65
|
+
$options['verbose'] = v
|
66
|
+
@log.level = Logger::INFO
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on("-d", "--[no-]debug", "Run in debug mode") do |v|
|
70
|
+
$options['debug'] = v
|
71
|
+
@log.level = Logger::DEBUG
|
72
|
+
end
|
73
|
+
|
74
|
+
opts.on("-r", "--read NAME", "use NAME as list of files") do |optarg|
|
75
|
+
$options['read'] = optarg
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on("-c", "--convert NAME", "use NAME to convert text (translation file)") do |optarg|
|
79
|
+
$options['convert'] = optarg
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.on("-t", "--type NAME", "use NAME as type of output") do |optarg|
|
83
|
+
$options['type'] = optarg
|
84
|
+
end
|
85
|
+
|
86
|
+
opts.on("-o", "--output NAME", "use NAME as output xml file") do |optarg|
|
87
|
+
$options['output'] = optarg
|
88
|
+
end
|
89
|
+
|
90
|
+
opts.on("-m", "--missing NAME", "use NAME as missing xml file for translations") do |optarg|
|
91
|
+
$options['missing'] = optarg
|
92
|
+
end
|
93
|
+
|
94
|
+
opts.on("-l", "--log FILE", "log to FILE") do |optarg|
|
95
|
+
$options['logname'] = optarg
|
96
|
+
end
|
97
|
+
|
98
|
+
opts.separator ""
|
99
|
+
opts.separator "Example #1: #{$PRGNAME} -o out.commonxml *.nessus"
|
100
|
+
opts.separator "Example #2: #{$PRGNAME} -c file-hr.xml -m missing.xml all.commonxml"
|
101
|
+
end.parse!
|
102
|
+
|
103
|
+
# start
|
104
|
+
if ARGV.empty? then
|
105
|
+
@log.error("Provide at least one file to process as argument")
|
106
|
+
end
|
107
|
+
|
108
|
+
total_oc=Offrep::CommonXML.new
|
109
|
+
total_on=Offrep::NessusXML.new
|
110
|
+
nessuscount=0
|
111
|
+
allcount=0
|
112
|
+
ARGV.each do |arg|
|
113
|
+
extension = File.extname arg
|
114
|
+
@log.info("processing #{allcount}: #{arg}...with extension #{extension}")
|
115
|
+
if extension == ".nessus" then
|
116
|
+
@log.debug("processing nessus #{arg} with extension #{extension}")
|
117
|
+
if nessuscount==0 then
|
118
|
+
total_on.readxml(File.open(arg))
|
119
|
+
else
|
120
|
+
total_on.mergexml(arg)
|
121
|
+
end
|
122
|
+
nessuscount=nessuscount+1
|
123
|
+
end
|
124
|
+
|
125
|
+
if extension == ".commonxml" then
|
126
|
+
@log.debug("processing commonxml #{arg} with extension #{extension}")
|
127
|
+
total_oc.readxml(File.open(arg)) # TODO: mergexml
|
128
|
+
end
|
129
|
+
|
130
|
+
# on.xmldoc.to_s
|
131
|
+
# on.to_common
|
132
|
+
allcount=allcount+1
|
133
|
+
end
|
134
|
+
@log.info("Processed #{allcount} items.")
|
135
|
+
|
136
|
+
if nessuscount>0 then
|
137
|
+
@log.debug("Converting all nessus to common XML format")
|
138
|
+
total_oc.readxml(on.to_common.to_s)
|
139
|
+
end
|
140
|
+
|
141
|
+
if $options['convert'] then
|
142
|
+
@log.debug("Converting all with translations #{$options['convert']}")
|
143
|
+
ot=Offrep::Translation.new
|
144
|
+
ot.readxml(File.open($options['convert']))
|
145
|
+
missingxml=total_oc.translate(ot.xml)
|
146
|
+
if $options['missing'] then
|
147
|
+
@log.debug("Writting all missing to #{$options['missing']}")
|
148
|
+
anonmisxml=oc.anonymize(missingxml)
|
149
|
+
File.write($options['missing'],anonmisxml.to_s)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
if $options['output'] then
|
154
|
+
@log.debug("Outputing to #{$options['output']}")
|
155
|
+
File.write(ARGV[0],total_oc.xmldoc.to_s)
|
156
|
+
else
|
157
|
+
puts total_oc.xmldoc.to_s
|
158
|
+
end
|
159
|
+
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
module Offrep
|
7
|
+
class CommonXML
|
8
|
+
attr_accessor :xmldoc, :log
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@log=Logger.new(STDERR)
|
12
|
+
@log.level = Logger::WARN
|
13
|
+
end
|
14
|
+
|
15
|
+
def readxml(trxml)
|
16
|
+
# f=File.open(trxml)
|
17
|
+
@xmldoc=Nokogiri::XML(trxml)
|
18
|
+
#f.close
|
19
|
+
end
|
20
|
+
|
21
|
+
def importxml(trxml)
|
22
|
+
if @xmldoc.nil? then
|
23
|
+
readxml(trxml)
|
24
|
+
else
|
25
|
+
mergexml(trxml)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def sevnum2text(sev)
|
30
|
+
str="NOT"
|
31
|
+
nsev=0
|
32
|
+
if sev.is_a? Integer
|
33
|
+
nsev=sev
|
34
|
+
else
|
35
|
+
nsev=sev.to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
case nsev
|
39
|
+
when 4
|
40
|
+
str="CRITICAL"
|
41
|
+
when 3
|
42
|
+
str="HIGH"
|
43
|
+
when 2
|
44
|
+
str="MEDIUM"
|
45
|
+
when 1
|
46
|
+
str="LOW"
|
47
|
+
when 0
|
48
|
+
str="INFO"
|
49
|
+
else
|
50
|
+
str="UNKNOWN"
|
51
|
+
end
|
52
|
+
return str
|
53
|
+
end
|
54
|
+
|
55
|
+
def sev2i(sev)
|
56
|
+
sevnum=0
|
57
|
+
if sev.is_a? Integer
|
58
|
+
sevnum=sev
|
59
|
+
else
|
60
|
+
sevnum=sev.to_i
|
61
|
+
end
|
62
|
+
return sevnum
|
63
|
+
end
|
64
|
+
|
65
|
+
def cvss2f(score)
|
66
|
+
scorenum=0.to_f
|
67
|
+
if score.is_a? Float
|
68
|
+
scorenum=score
|
69
|
+
else
|
70
|
+
scorenum=score.to_f
|
71
|
+
end
|
72
|
+
return scorenum
|
73
|
+
end
|
74
|
+
|
75
|
+
def removesev(sev)
|
76
|
+
@xmldoc.xpath("/vulnerabilities/vulnerability[./data/common/severity='#{sev}']").each do |vuln|
|
77
|
+
vuln.remove
|
78
|
+
end
|
79
|
+
@xmldoc
|
80
|
+
end
|
81
|
+
|
82
|
+
def anonymize(xmln)
|
83
|
+
xmln.xpath('/vulnerabilities/vulnerability/target').each do |target|
|
84
|
+
target.remove
|
85
|
+
end
|
86
|
+
xmln.xpath('/vulnerabilities/vulnerability/data/common/output').each do |output|
|
87
|
+
output.remove
|
88
|
+
end
|
89
|
+
return xmln
|
90
|
+
end
|
91
|
+
|
92
|
+
def emptyxml
|
93
|
+
misvulns = Nokogiri::XML::Builder.new do |xml|
|
94
|
+
xml.vulnerabilities {
|
95
|
+
}
|
96
|
+
end # misvulns
|
97
|
+
misxml = Nokogiri::XML(misvulns.to_xml)
|
98
|
+
return misxml
|
99
|
+
end
|
100
|
+
|
101
|
+
def removebydef(defxml)
|
102
|
+
defxml.xpath("/vulnerabilities/vulnerability").each do |vulndef|
|
103
|
+
vulndef.element_children.each do |vulnele|
|
104
|
+
vulnele.element_children.each do |vulnele|
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def swapvuln(vuln1,vuln2)
|
111
|
+
tempvuln=vuln1.dup
|
112
|
+
vuln1=vuln2
|
113
|
+
vuln2=tempvuln
|
114
|
+
end
|
115
|
+
|
116
|
+
def cmpvuln(vuln1,vuln2)
|
117
|
+
sev1=sev2i(getcontent(vuln1.at_xpath('./data/common/severity'),'0'))
|
118
|
+
cvss1=cvss2f(getcontent(vuln1.at_xpath('./data/common/score'),'0.0'))
|
119
|
+
sev2=sev2i(getcontent(vuln2.at_xpath('./data/common/severity'),'0'))
|
120
|
+
cvss2=cvss2f(getcontent(vuln2.at_xpath('./data/common/score'),'0.0'))
|
121
|
+
ret=0
|
122
|
+
case
|
123
|
+
when sev1>sev2
|
124
|
+
ret=1
|
125
|
+
when sev1<sev2
|
126
|
+
ret=-1
|
127
|
+
when sev1==sev2
|
128
|
+
case
|
129
|
+
when cvss1>cvss2
|
130
|
+
ret=1
|
131
|
+
when cvss1<cvss2
|
132
|
+
ret=-1
|
133
|
+
when cvss1==cvss2
|
134
|
+
ret=0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
return ret
|
138
|
+
end
|
139
|
+
|
140
|
+
def isortbysev!
|
141
|
+
vulnxml=sortbysev
|
142
|
+
@xmldoc=vulnxml
|
143
|
+
end
|
144
|
+
|
145
|
+
def isortbysevrev!
|
146
|
+
vulnxml=sortbysevrev
|
147
|
+
@xmldoc=vulnxml
|
148
|
+
end
|
149
|
+
|
150
|
+
def sortbysevrev
|
151
|
+
sorted=xsortbysev.reverse
|
152
|
+
sorted.each do |vuln|
|
153
|
+
@xmldoc.at_xpath('/vulnerabilities').add_child(vuln)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def sortbysev
|
158
|
+
sorted=xsortbysev
|
159
|
+
sorted.each do |vuln|
|
160
|
+
@xmldoc.at_xpath('/vulnerabilities').add_child(vuln)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def xsortbysev
|
165
|
+
vulnsnode=@xmldoc.at_xpath('/vulnerabilities')
|
166
|
+
vulns=vulnsnode.xpath('./vulnerability')
|
167
|
+
sorted=vulns.sort {|a,b| cmpvuln(a,b) }
|
168
|
+
return sorted
|
169
|
+
end
|
170
|
+
|
171
|
+
def osortbysev!
|
172
|
+
vulns=@xmldoc.xpath('/vulnerabilities/vulnerability')
|
173
|
+
sorted=vulns.sort{|a,b| cmpvuln(a,b) }
|
174
|
+
vulns=sorted
|
175
|
+
end
|
176
|
+
|
177
|
+
def osortbysev
|
178
|
+
vulns=@xmldoc.xpath('/vulnerabilities/vulnerability')
|
179
|
+
sorted=vulns.sort{|a,b| cmpvuln(a,b) }
|
180
|
+
return sorted
|
181
|
+
end
|
182
|
+
|
183
|
+
def sortbyvuln
|
184
|
+
vulnxml=emptyxml()
|
185
|
+
xmldoc=@xmldoc
|
186
|
+
xmldoc.xpath("/vulnerabilities/vulnerability").each do |vuln|
|
187
|
+
foundid=false
|
188
|
+
vuln.at_xpath('./id').element_children.each do |ids|
|
189
|
+
if foundid then
|
190
|
+
next
|
191
|
+
end
|
192
|
+
foundvuln=vulnxml.at_xpath("/vulnerabilities/vulnerability[./id/#{ids.name}='#{ids.content}']")
|
193
|
+
if foundvuln.nil?
|
194
|
+
# if not found add complete vulnerability
|
195
|
+
vulnxml.at_xpath('/vulnerabilities').add_child(vuln.dup)
|
196
|
+
else
|
197
|
+
# if found, add only target part
|
198
|
+
foundvuln.add_child(vuln.at_xpath('./target').dup)
|
199
|
+
# TODO: add output as well
|
200
|
+
foundid=true
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
return vulnxml
|
205
|
+
end
|
206
|
+
|
207
|
+
def translate(trxml)
|
208
|
+
misxml = emptyxml()
|
209
|
+
@xmldoc.xpath('/vulnerabilities/vulnerability').each do |vuln|
|
210
|
+
foundit=false
|
211
|
+
# binding.pry
|
212
|
+
vuln.at_xpath('./id').element_children.each do |ids|
|
213
|
+
if ids.name=='cve' then
|
214
|
+
next
|
215
|
+
end
|
216
|
+
# binding.pry
|
217
|
+
trid=trxml.at_xpath("/vulnerabilities/vulnerability[./id/#{ids.name}='#{ids.content}']")
|
218
|
+
if not trid.nil? then
|
219
|
+
foundit=true
|
220
|
+
# replace all XML elements inside /data/common to translated ones
|
221
|
+
trid.at_xpath('./data/common').element_children.each do |ele|
|
222
|
+
# puts ele.name
|
223
|
+
foundele=vuln.at_xpath("./data/common/#{ele.name}")
|
224
|
+
# if element not found, add as a child in common
|
225
|
+
# binding.pry
|
226
|
+
if foundele.nil?
|
227
|
+
vuln.at_xpath("./data/common").add_child(trid.at_xpath("./data/common").dup)
|
228
|
+
else
|
229
|
+
foundele.content = ele.content
|
230
|
+
end
|
231
|
+
end # trid.at_xpath
|
232
|
+
end # if not trid
|
233
|
+
end # vuln.at_xpath .. ids
|
234
|
+
|
235
|
+
# trxml.xpath('/vulnerabilities/vulnerability').each do |trvuln|
|
236
|
+
# vuln.at_xpath('./data/common').element_children.each { |e| puts e.name }
|
237
|
+
|
238
|
+
if foundit then
|
239
|
+
# puts "Found for #{vuln.to_s[0..60]}"
|
240
|
+
else
|
241
|
+
# puts "Not found for #{vuln.to_s[0..60]}"
|
242
|
+
# binding.pry
|
243
|
+
misxml.at_xpath('/vulnerabilities').add_child(vuln.dup)
|
244
|
+
end
|
245
|
+
end # @xmldoc ... vuln
|
246
|
+
# puts misxml
|
247
|
+
return misxml
|
248
|
+
end
|
249
|
+
|
250
|
+
def mergexml(trxml)
|
251
|
+
doc = Nokogiri::XML(trxml)
|
252
|
+
doc.xpath("/vulnerabilities/vulnerability").each do |vuln|
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def getcontent(cont,defvalue)
|
257
|
+
if cont.nil? then
|
258
|
+
return defvalue
|
259
|
+
else
|
260
|
+
return cont.content
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def to_common
|
265
|
+
return @xmldoc
|
266
|
+
end
|
267
|
+
|
268
|
+
def to_com
|
269
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
270
|
+
xml.vulnerabilities {
|
271
|
+
@xmldoc.xpath("/NessusClientData_v2/Report/ReportHost").each do |host|
|
272
|
+
host.xpath("./ReportItem").each do |ri|
|
273
|
+
xml.vulnerability_ {
|
274
|
+
xml.target_ {
|
275
|
+
xml.ip_ host.attribute("name").to_s || '0'
|
276
|
+
xml.port_ ri.attribute("port").to_s || '0'
|
277
|
+
xml.protocol_ ri.attribute("protocol").to_s || 'ip'
|
278
|
+
xml.service_ ri.attribute("svc_name").to_s || 'general'
|
279
|
+
}
|
280
|
+
xml.id_ {
|
281
|
+
xml.nessusPluginId_ ri.attribute("pluginID").to_s || '0'
|
282
|
+
}
|
283
|
+
xml.data_ {
|
284
|
+
xml.common {
|
285
|
+
xml.severity_ ri.attribute("severity").to_s || '0'
|
286
|
+
xml.score_ getcontent(ri.at_xpath('./cvss_base_score'),'')
|
287
|
+
xml.title_ ri.attribute("pluginName").to_s || ''
|
288
|
+
xml.synopsis_ getcontent(ri.at_xpath('./synopsis'),'')
|
289
|
+
xml.description_ getcontent(ri.at_xpath('./description'),'')
|
290
|
+
xml.solution_ getcontent(ri.at_xpath('./solution'),'')
|
291
|
+
}
|
292
|
+
}
|
293
|
+
}
|
294
|
+
end # host.xpath
|
295
|
+
end # @xmldoc.xpath
|
296
|
+
}
|
297
|
+
end
|
298
|
+
return builder.to_xml
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Offrep
|
5
|
+
class NessusXML
|
6
|
+
attr_accessor :xmldoc, :log
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@log=Logger.new(STDERR)
|
10
|
+
@log.level = Logger::WARN
|
11
|
+
end
|
12
|
+
|
13
|
+
def readxml(trxml)
|
14
|
+
# f=File.open(trxml)
|
15
|
+
@xmldoc=Nokogiri::XML(trxml)
|
16
|
+
#f.close
|
17
|
+
end
|
18
|
+
|
19
|
+
def importxml(trxml)
|
20
|
+
if @xmldoc.nil? then
|
21
|
+
readxml(trxml)
|
22
|
+
else
|
23
|
+
mergexml(trxml)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def mergexml(trxml)
|
28
|
+
f = File.open(trxml)
|
29
|
+
doc = Nokogiri::XML(f)
|
30
|
+
|
31
|
+
reportnode=@xmldoc.at_xpath("/NessusClientData_v2/Report")
|
32
|
+
doc.xpath("/NessusClientData_v2/Report/ReportHost").each { |host|
|
33
|
+
hostname=host.attribute("name").to_s
|
34
|
+
result=@xmldoc.xpath('/NessusClientData_v2/Report/ReportHost[@name="'+hostname+'"]')
|
35
|
+
if result.empty?
|
36
|
+
reportnode.add_child(host)
|
37
|
+
@log.debug("host "+hostname+": not found, added")
|
38
|
+
else
|
39
|
+
@log.debug("host "+hostname+": found, not added new")
|
40
|
+
rhnode=@xmldoc.at_xpath('/NessusClientData_v2/Report/ReportHost[@name="'+hostname+'"]')
|
41
|
+
host.xpath("./ReportItem").each { |ri|
|
42
|
+
port=ri.attribute("port").to_s
|
43
|
+
pluginid=ri.attribute("pluginID").to_s
|
44
|
+
protocol=ri.attribute("protocol").to_s
|
45
|
+
|
46
|
+
resultri=@xmldoc.xpath('/NessusClientData_v2/Report/ReportHost[@name="'+hostname+'"]/ReportItem[@pluginID="'+pluginid+
|
47
|
+
'" and @port="'+port+
|
48
|
+
'" and @protocol="'+protocol+'"]')
|
49
|
+
if resultri.empty?
|
50
|
+
rhnode.add_child(ri)
|
51
|
+
@log.debug(port+";"+protocol+";"+pluginid+": not found, added")
|
52
|
+
else
|
53
|
+
@log.debug(port+";"+protocol+";"+pluginid+": found, not added new")
|
54
|
+
end
|
55
|
+
} # host.xpath
|
56
|
+
end
|
57
|
+
} # xpath.each host
|
58
|
+
|
59
|
+
f.close
|
60
|
+
end
|
61
|
+
|
62
|
+
def getcontent(cont,defvalue)
|
63
|
+
if cont.nil? then
|
64
|
+
return defvalue
|
65
|
+
else
|
66
|
+
return cont.content
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_common
|
71
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
72
|
+
xml.vulnerabilities {
|
73
|
+
@xmldoc.xpath("/NessusClientData_v2/Report/ReportHost").each do |host|
|
74
|
+
host.xpath("./ReportItem").each do |ri|
|
75
|
+
xml.vulnerability_ {
|
76
|
+
xml.target_ {
|
77
|
+
xml.ip_ host.attribute("name").to_s || '0'
|
78
|
+
xml.port_ ri.attribute("port").to_s || '0'
|
79
|
+
xml.protocol_ ri.attribute("protocol").to_s || 'ip'
|
80
|
+
xml.service_ ri.attribute("svc_name").to_s || 'general'
|
81
|
+
}
|
82
|
+
xml.id_ {
|
83
|
+
xml.nessusPluginId_ ri.attribute("pluginID").to_s || '0'
|
84
|
+
}
|
85
|
+
xml.data_ {
|
86
|
+
xml.common {
|
87
|
+
xml.severity_ ri.attribute("severity").to_s || '0'
|
88
|
+
xml.score_ getcontent(ri.at_xpath('./cvss_base_score'),'')
|
89
|
+
xml.title_ ri.attribute("pluginName").to_s || ''
|
90
|
+
xml.synopsis_ getcontent(ri.at_xpath('./synopsis'),'')
|
91
|
+
xml.description_ getcontent(ri.at_xpath('./description'),'')
|
92
|
+
xml.solution_ getcontent(ri.at_xpath('./solution'),'')
|
93
|
+
xml.output_ getcontent(ri.at_xpath('./plugin_output'),'')
|
94
|
+
xml.references_ getcontent(ri.at_xpath('./see_also'),'')
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
end # host.xpath
|
99
|
+
end # @xmldoc.xpath
|
100
|
+
}
|
101
|
+
end
|
102
|
+
return builder.to_xml
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module Offrep
|
7
|
+
class Reportvuln
|
8
|
+
def initialize (vulns)
|
9
|
+
@vulns=vulns
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_binding
|
13
|
+
binding
|
14
|
+
end
|
15
|
+
end # of class
|
16
|
+
|
17
|
+
class Report
|
18
|
+
attr_accessor :xml, :log, :template
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@log = Logger.new
|
22
|
+
@template="<% @vulns.each do |v| %>\n"+
|
23
|
+
"<%= lineitem=v['pid']+';'+v['title']+';'+v['hosts'] %>"+
|
24
|
+
"<% end %>"
|
25
|
+
end
|
26
|
+
|
27
|
+
def output (allvulns)
|
28
|
+
rhtml = ERB.new(@template)
|
29
|
+
# Set up template data.
|
30
|
+
gvuln = Reportvuln.new( allvulns )
|
31
|
+
# vulns=allvulns
|
32
|
+
# rhtml.result
|
33
|
+
rhtml.run(gvuln.get_binding)
|
34
|
+
end
|
35
|
+
|
36
|
+
def readxml(trxml)
|
37
|
+
f=File.open(fn)
|
38
|
+
@xml=Nokogiri::XML(trxml)
|
39
|
+
f.close
|
40
|
+
end
|
41
|
+
|
42
|
+
end # class
|
43
|
+
end # module
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Offrep
|
4
|
+
class Translation
|
5
|
+
attr_accessor :xml
|
6
|
+
|
7
|
+
def readxml(xmlparm)
|
8
|
+
@xml = Nokogiri::XML(xmlparm)
|
9
|
+
end
|
10
|
+
|
11
|
+
def nessusid(pluginid)
|
12
|
+
@xml.xpath("/vulnerabilities/vulnerability[./id/nessusPluginId='#{pluginid}']")
|
13
|
+
end
|
14
|
+
|
15
|
+
# getelement('nessusPluginId',10072)
|
16
|
+
def getvulnele(pluginelement,pluginid)
|
17
|
+
@xml.at_xpath("/vulnerabilities/vulnerability[./id/#{pluginelement}='#{pluginid}']")
|
18
|
+
end
|
19
|
+
|
20
|
+
# getvulncommon('nessusPluginId',10072,'title')
|
21
|
+
def getvulncommon(pluginelement,pluginid,commonelement)
|
22
|
+
getvulnele(pluginelement,pluginid).at_xpath("./data/common/#{commonelement}").content
|
23
|
+
end
|
24
|
+
|
25
|
+
end # class
|
26
|
+
end # module
|
data/lib/offrep.rb
ADDED
data/offrep.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'offrep/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "offrep"
|
8
|
+
spec.version = Offrep::VERSION
|
9
|
+
spec.authors = ["Vlatko Kosturjak"]
|
10
|
+
spec.email = ["kost@linux.hr"]
|
11
|
+
|
12
|
+
spec.summary = %q{Offensive reporting.}
|
13
|
+
spec.description = %q{Offensive reporting.}
|
14
|
+
spec.homepage = "https://github.com/kost/offrep"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", ">= 1.3"
|
23
|
+
spec.add_development_dependency "rake", ">= 1.0"
|
24
|
+
spec.add_development_dependency "rspec", ">= 1.0"
|
25
|
+
spec.add_development_dependency "pry", ">= 0"
|
26
|
+
|
27
|
+
spec.add_runtime_dependency 'nokogiri', '>= 0'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: offrep
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vlatko Kosturjak
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: nokogiri
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Offensive reporting.
|
84
|
+
email:
|
85
|
+
- kost@linux.hr
|
86
|
+
executables:
|
87
|
+
- offrep
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".travis.yml"
|
94
|
+
- CODE_OF_CONDUCT.md
|
95
|
+
- Gemfile
|
96
|
+
- LICENSE
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- bin/console
|
100
|
+
- bin/pry
|
101
|
+
- bin/setup
|
102
|
+
- examples/nessus-merge.rb
|
103
|
+
- exe/offrep
|
104
|
+
- lib/offrep.rb
|
105
|
+
- lib/offrep/commonxml.rb
|
106
|
+
- lib/offrep/nessusxml.rb
|
107
|
+
- lib/offrep/report.rb
|
108
|
+
- lib/offrep/translation.rb
|
109
|
+
- lib/offrep/version.rb
|
110
|
+
- offrep.gemspec
|
111
|
+
homepage: https://github.com/kost/offrep
|
112
|
+
licenses:
|
113
|
+
- MIT
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 2.6.10
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: Offensive reporting.
|
135
|
+
test_files: []
|