redmine_audit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 182a256634fc3c05360c058215fa5c0bdef09bc1
4
+ data.tar.gz: 2619e900354882da15f5e6775552ee95e2d68168
5
+ SHA512:
6
+ metadata.gz: 50b2639203a7c5540b3cee8d85e71ae67914dbb4137f02c803e9303c0374b3b982d2554bbff43b24632d5ca9c6ef53b597553eb8cc0da909ad6222e83eaa2a09
7
+ data.tar.gz: 4b273431a5b814f7b9d63a7d256f60a8b05026dad18fe35a7d916ad9f7c1c84fe63750089e1bd339d5720eb10aa51773c750fb081ea03ca30f8911447a3d06e6
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in redmine_audit.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Sho Hashimoto
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,41 @@
1
+ # RedmineAudit
2
+
3
+ This is Redmine plugin for checking vulnerabilities. provides redmine:audit rake task.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your Redmine's Gemfile.local:
8
+
9
+ ```ruby
10
+ gem 'redmine_audit'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ Excecute redmine:audit rake task with users environment variable.
20
+
21
+ ```
22
+ $ rake redmine:audit users=1,2 RAILS_ENV=production
23
+ ```
24
+
25
+ Or, add same commant to crontab.
26
+
27
+ ```
28
+ 30 6 * * * www-data cd /path/to/redmine ; rake redmine:audit users=1,2 RAILS_ENV=production
29
+ ```
30
+
31
+ users environment variable can set only system administrator.
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sho-h/redmine_audit.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ files = FileList['test/**/*test.rb']
7
+ t.test_files = files
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => :test
File without changes
data/app/helpers/.keep ADDED
File without changes
data/app/models/.keep ADDED
File without changes
@@ -0,0 +1,23 @@
1
+ # reopen Redmine Mailer
2
+ class Mailer < ActionMailer::Base
3
+ # Sends notification to specified administrator
4
+ #
5
+ # @param [Gem::Version] advisories
6
+ # The version to compare against {#unaffected_versions}.
7
+ # @param [Array] user_ids
8
+ # Array of user ids who should be notified
9
+ def unfixed_advisories_found(advisories, user_ids)
10
+ if advisories.nil? || advisories.empty?
11
+ raise "Couldn't find user specified: #{advisories.inspect}"
12
+ end
13
+
14
+ users = User.active.where(admin: true, id: user_ids).to_a
15
+ if users.empty?
16
+ raise ActiveRecord::RecordNotFound.new("Couldn't find user specified: #{user_ids.inspect}")
17
+ end
18
+
19
+ @advisories = advisories
20
+ # TODO: Internationalize suject and body.
21
+ mail(to: users, subject: "[Redmine] Security notification")
22
+ end
23
+ end
data/app/views/.keep ADDED
File without changes
@@ -0,0 +1,25 @@
1
+ <div><%= l(:mail_summary_advisories_found) %></div>
2
+
3
+ <%-
4
+ @advisories.each do |advisory|
5
+ if advisory.external_references && !advisory.external_references.empty?
6
+ ext_refs = advisory.external_references
7
+ else
8
+ ext_refs = 'none'
9
+ end
10
+ solution = advisory.fixed_versions.join(', ')
11
+ -%>
12
+
13
+ <div>
14
+ <ul style="list-style:none;">
15
+ <li>Severity: <%= advisory.severity %></li>
16
+ <li>URL: <%= ext_refs %></li>
17
+ <li>Detail: <%= advisory.details %></li>
18
+ <li>Solution: upgrade to <%= solution %></li>
19
+ </ul>
20
+ </div>
21
+ <%- end -%>
22
+
23
+ <div>
24
+ <%= l(:mail_detail_link_head_advisories_found) %> <%= link_to 'redmine.org', RedmineAudit::Database::URL %> <%= l(:mail_detail_link_tail_advisories_found) %>
25
+ </div>
@@ -0,0 +1,19 @@
1
+ <%= l(:mail_summary_advisories_found) %>
2
+
3
+ <%-
4
+ @advisories.each do |advisory|
5
+ if advisory.external_references && !advisory.external_references.empty?
6
+ ext_refs = advisory.external_references
7
+ else
8
+ ext_refs = 'none'
9
+ end
10
+ solution = advisory.fixed_versions.join(', ')
11
+ -%>
12
+
13
+ Severity: <%= advisory.severity %>
14
+ URL: <%= ext_refs %>
15
+ Detail: <%= advisory.details %>
16
+ Solution: upgrade to <%= solution %>
17
+ <%- end -%>
18
+
19
+ <%= l(:mail_detail_link_head_advisories_found) %> <%= RedmineAudit::Database::URL %> <%= l(:mail_detail_link_tail_advisories_found) %>
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "redmine_audit"
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(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ # English strings go here for Rails i18n
2
+ en:
3
+ mail_summary_advisories_found: "Vulnerabilities found your Redmine!"
4
+ mail_detail_link_head_advisories_found: "See"
5
+ mail_detail_link_tail_advisories_found: "for more detail."
@@ -0,0 +1,4 @@
1
+ ja:
2
+ mail_summary_advisories_found: "Redmineの脆弱性が見つかりました!"
3
+ mail_detail_link_head_advisories_found: "詳しくは"
4
+ mail_detail_link_tail_advisories_found: "を参照してください。"
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Plugin's routes
2
+ # See: http://guides.rubyonrails.org/routing.html
data/db/migrate/.keep ADDED
File without changes
data/init.rb ADDED
@@ -0,0 +1,8 @@
1
+ Redmine::Plugin.register :redmine_audit do
2
+ name 'Redmine Audit plugin'
3
+ author 'Sho Hashimoto'
4
+ description 'Redmine plugin for checking vulnerabilities'
5
+ version RedmineAudit::VERSION
6
+ url 'https://github.com/sho-h/redmine_audit/'
7
+ author_url 'https://github.com/sho-h/'
8
+ end
@@ -0,0 +1,10 @@
1
+ require 'redmine_audit/version'
2
+
3
+ module RedmineAudit
4
+ # Run the classic redmine plugin initializer after rails boot
5
+ class Plugin < ::Rails::Engine
6
+ config.after_initialize do
7
+ require File.expand_path('../init', __dir__)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,48 @@
1
+ module RedmineAudit
2
+ # Redmine advisory
3
+ #
4
+ # ex) Advisory.new('High', 'http://someurl.example.com',
5
+ # [Gem::Requirement], [Gem::Requirement])
6
+ class Advisory < Struct.new(:severity,
7
+ :details,
8
+ :external_references,
9
+ :unaffected_versions,
10
+ :fixed_versions)
11
+ # Checks whether the version is not affected by the advisory.
12
+ #
13
+ # @param [Gem::Version] version
14
+ # The version to compare against {#unaffected_versions}.
15
+ #
16
+ # @return [Boolean]
17
+ # Specifies whether the version is not affected by the advisory.
18
+ def unaffected?(version)
19
+ unaffected_versions.any? do |unaffected_version|
20
+ unaffected_version === version
21
+ end
22
+ end
23
+
24
+ # Checks whether the version is fixed against the advisory.
25
+ #
26
+ # @param [Gem::Version] version
27
+ # The version to compare against {#fixed_versions}.
28
+ #
29
+ # @return [Boolean]
30
+ # Specifies whether the version is fixed against the advisory.
31
+ def fixed?(version)
32
+ fixed_versions.any? do |fixed_version|
33
+ fixed_version === version
34
+ end
35
+ end
36
+
37
+ # Checks whether the version is vulnerable to the advisory.
38
+ #
39
+ # @param [Gem::Version] version
40
+ # The version to compare against {#fixed_versions}.
41
+ #
42
+ # @return [Boolean]
43
+ # Specifies whether the version is vulnerable to the advisory or not.
44
+ def vulnerable?(version)
45
+ !fixed?(version) && !unaffected?(version)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,71 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'redmine_audit/advisory'
4
+
5
+ module RedmineAudit
6
+ # Redmine advisory database
7
+ class Database
8
+ URL = 'http://www.redmine.org/projects/redmine/wiki/Security_Advisories'
9
+ TABLE_XPATH = '//*[@id="content"]/div[2]/table'
10
+
11
+ attr_reader :vulnerabilities
12
+
13
+ # Get unfixed advisories against specified Redmine version.
14
+ #
15
+ # @param [String] version
16
+ # The Redmine version to compare against {#unaffected_versions}.
17
+ #
18
+ # @return [[Redmine::Advisory]]
19
+ # The array of Redmine::Advisory unfixed.
20
+ def advisories(v)
21
+ if @known_advisories.nil?
22
+ @known_advisories = []
23
+ html = fetch_advisory_data
24
+ doc = Nokogiri::HTML(html)
25
+ doc.xpath(TABLE_XPATH).xpath('tr')[1..-1].each do |tr|
26
+ if res = parse_tds(tr.xpath('td'))
27
+ @known_advisories << Advisory.new(*res)
28
+ end
29
+ end
30
+ end
31
+
32
+ # tarball version has '.stable'.
33
+ # This is hack to avoid treating prerelease by Gem::Version.
34
+ # TODO: refactoring such as fix Gem::Version like setting @prerelease = false.
35
+ redmine_version = Gem::Version.new(v.gsub(/\.stable\z/, ''))
36
+ unfixed_advisories = []
37
+ @known_advisories.each do |advisory|
38
+ if advisory.vulnerable?(redmine_version)
39
+ unfixed_advisories.push(advisory)
40
+ end
41
+ end
42
+ return unfixed_advisories
43
+ end
44
+
45
+ private
46
+
47
+ def fetch_advisory_data(url = URL)
48
+ open(url).read
49
+ end
50
+
51
+ # [severity, details, external_references, unaffected_versions, fixed_versions]
52
+ def parse_tds(tds)
53
+ # TODO: treat affected_versions if needed.
54
+ # Last element is unaffected_versions. Always empty array now.
55
+ res = tds[0..2].map(&:content) + [[]]
56
+
57
+ # Ignore depends gem
58
+ return nil if /Ruby on Rails vulnerability/.match(res[1])
59
+
60
+ versions = tds[4].content.split(/\s*(?:and|,)\s*/)
61
+ fixed_versions = []
62
+ if versions.length > 1
63
+ fixed_versions =
64
+ versions[0..-2].map {|v| Gem::Requirement.new('~> ' + v)}
65
+ end
66
+ fixed_versions.push(Gem::Requirement.new('>= ' + versions.last))
67
+ res.push(fixed_versions)
68
+ return res
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module RedmineAudit
2
+ VERSION = '0.1.0'
3
+ end
data/lib/tasks/.keep ADDED
File without changes
@@ -0,0 +1,30 @@
1
+ require 'redmine/version'
2
+ require 'redmine_audit/advisory'
3
+ require 'redmine_audit/database'
4
+
5
+ desc <<-END_DESC
6
+ Check redmine vulnerabilities.
7
+
8
+ Available options:
9
+ * users => comma separated list of user/group ids who should be notified
10
+
11
+ Example:
12
+ rake redmine:bundle_audit users="1,23, 56" RAILS_ENV="production"
13
+ END_DESC
14
+
15
+ namespace :redmine do
16
+ task audit: :environment do
17
+ # TODO: More better if requires mailer automatically.
18
+ require_dependency 'mailer'
19
+ require_relative '../../app/models/mailer.rb'
20
+
21
+ redmine_ver = Redmine::VERSION
22
+ advisories = RedmineAudit::Database.new.advisories(redmine_ver.to_s)
23
+ if advisories.length > 0
24
+ users = (ENV['users'] || '').split(',').each(&:strip!)
25
+ Mailer.with_synched_deliveries do
26
+ Mailer.unfixed_advisories_found(advisories, users).deliver
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redmine_audit/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'redmine_audit'
8
+ spec.version = RedmineAudit::VERSION
9
+ spec.authors = ['Sho Hashimoto']
10
+ spec.email = ['sho.hsmt@gmail.com']
11
+
12
+ spec.summary = %q{Redmine plugin for checking vulnerabilities}
13
+ spec.description = %q{Redmine plugin for checking vulnerabilities}
14
+ spec.homepage = 'https://github.com/sho-h/redmine_audit'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_runtime_dependency 'nokogiri'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.14'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'test-unit'
29
+ spec.add_development_dependency 'test-unit-rr'
30
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redmine_audit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sho Hashimoto
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-06-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
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: test-unit-rr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Redmine plugin for checking vulnerabilities
84
+ email:
85
+ - sho.hsmt@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE
93
+ - README.md
94
+ - Rakefile
95
+ - app/controllers/.keep
96
+ - app/helpers/.keep
97
+ - app/models/.keep
98
+ - app/models/mailer.rb
99
+ - app/views/.keep
100
+ - app/views/mailer/unfixed_advisories_found.html.erb
101
+ - app/views/mailer/unfixed_advisories_found.text.erb
102
+ - bin/console
103
+ - bin/setup
104
+ - config/locales/en.yml
105
+ - config/locales/ja.yml
106
+ - config/routes.rb
107
+ - db/migrate/.keep
108
+ - init.rb
109
+ - lib/redmine_audit.rb
110
+ - lib/redmine_audit/advisory.rb
111
+ - lib/redmine_audit/database.rb
112
+ - lib/redmine_audit/version.rb
113
+ - lib/tasks/.keep
114
+ - lib/tasks/redmine_audit.rake
115
+ - redmine_audit.gemspec
116
+ homepage: https://github.com/sho-h/redmine_audit
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.5.2
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Redmine plugin for checking vulnerabilities
140
+ test_files: []