gitrob 0.0.6 → 1.0.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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +30 -7
  3. data/.rspec +0 -1
  4. data/.rubocop.yml +55 -0
  5. data/.travis.yml +4 -0
  6. data/CHANGELOG.md +42 -0
  7. data/CONTRIBUTING.md +137 -9
  8. data/Gemfile +11 -1
  9. data/Guardfile +42 -0
  10. data/LICENSE.txt +17 -18
  11. data/README.md +79 -29
  12. data/Rakefile +6 -0
  13. data/bin/console +34 -0
  14. data/bin/setup +7 -0
  15. data/db/migrations/001_create_assessments.rb +19 -0
  16. data/db/migrations/002_create_github_access_tokens.rb +11 -0
  17. data/db/migrations/003_create_owners.rb +24 -0
  18. data/db/migrations/004_create_repositories.rb +23 -0
  19. data/db/migrations/005_create_blobs.rb +16 -0
  20. data/db/migrations/006_create_flags.rb +13 -0
  21. data/db/migrations/007_create_comparisons.rb +17 -0
  22. data/db/migrations/008_create_blobs_comparisons.rb +8 -0
  23. data/db/migrations/009_create_comparisons_repositories.rb +8 -0
  24. data/db/migrations/010_create_comparisons_owners.rb +8 -0
  25. data/exe/gitrob +6 -0
  26. data/gitrob.gemspec +25 -18
  27. data/lib/gitrob/blob_observer.rb +103 -0
  28. data/lib/gitrob/cli/command.rb +58 -0
  29. data/lib/gitrob/cli/commands/accept_terms_of_use.rb +61 -0
  30. data/lib/gitrob/cli/commands/analyze/analysis.rb +75 -0
  31. data/lib/gitrob/cli/commands/analyze/gathering.rb +101 -0
  32. data/lib/gitrob/cli/commands/analyze.rb +63 -0
  33. data/lib/gitrob/cli/commands/banner.rb +25 -0
  34. data/lib/gitrob/cli/commands/configure.rb +123 -0
  35. data/lib/gitrob/cli/commands/server.rb +21 -0
  36. data/lib/gitrob/cli/progress_bar.rb +47 -0
  37. data/lib/gitrob/cli.rb +213 -0
  38. data/lib/gitrob/github/client_manager.rb +46 -0
  39. data/lib/gitrob/github/data_manager.rb +121 -0
  40. data/lib/gitrob/jobs/assessment.rb +12 -0
  41. data/lib/gitrob/jobs/comparison.rb +55 -0
  42. data/lib/gitrob/models/assessment.rb +96 -0
  43. data/lib/gitrob/models/blob.rb +50 -0
  44. data/lib/gitrob/models/comparison.rb +15 -0
  45. data/lib/gitrob/models/flag.rb +15 -0
  46. data/lib/gitrob/models/github_access_token.rb +17 -0
  47. data/lib/gitrob/models/owner.rb +23 -0
  48. data/lib/gitrob/models/repository.rb +20 -0
  49. data/lib/gitrob/utils.rb +19 -0
  50. data/lib/gitrob/version.rb +1 -1
  51. data/lib/gitrob/web_app.rb +292 -0
  52. data/lib/gitrob.rb +30 -113
  53. data/public/css/bootstrap.min.css +11 -0
  54. data/public/css/main.css +130 -0
  55. data/public/css/tomorrow-night.css +75 -0
  56. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/public/fonts/glyphicons-halflings-regular.svg +273 -214
  58. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  61. data/public/images/blob_spinner.gif +0 -0
  62. data/public/images/gear_spinner.gif +0 -0
  63. data/public/js/bootstrap.min.js +7 -0
  64. data/public/js/highlight.pack.js +2 -0
  65. data/public/js/highlight.worker.js +13 -0
  66. data/public/js/jquery-2.1.4.min.js +4 -0
  67. data/public/js/main.js +239 -0
  68. data/public/robots.txt +2 -0
  69. data/signatures.json +541 -0
  70. data/views/assessments/_assessments.erb +57 -0
  71. data/views/assessments/_comparable_assessments.erb +38 -0
  72. data/views/assessments/_comparisons.erb +111 -0
  73. data/views/assessments/compare.erb +22 -0
  74. data/views/assessments/findings.erb +55 -0
  75. data/views/assessments/repositories.erb +35 -0
  76. data/views/assessments/show.erb +1 -0
  77. data/views/assessments/users.erb +46 -0
  78. data/views/blobs/show.erb +37 -0
  79. data/views/comparisons/show.erb +125 -0
  80. data/views/errors/internal_server_error.erb +9 -0
  81. data/views/errors/not_found.erb +5 -0
  82. data/views/index.erb +43 -28
  83. data/views/layout.erb +38 -12
  84. data/views/repositories/show.erb +49 -0
  85. data/views/users/show.erb +54 -0
  86. metadata +217 -106
  87. data/bin/gitrob +0 -260
  88. data/lib/gitrob/github/blob.rb +0 -41
  89. data/lib/gitrob/github/http_client.rb +0 -127
  90. data/lib/gitrob/github/organization.rb +0 -99
  91. data/lib/gitrob/github/repository.rb +0 -72
  92. data/lib/gitrob/github/user.rb +0 -84
  93. data/lib/gitrob/observers/sensitive_files.rb +0 -83
  94. data/lib/gitrob/progressbar.rb +0 -52
  95. data/lib/gitrob/util.rb +0 -11
  96. data/lib/gitrob/webapp.rb +0 -76
  97. data/models/blob.rb +0 -35
  98. data/models/finding.rb +0 -14
  99. data/models/organization.rb +0 -32
  100. data/models/repo.rb +0 -22
  101. data/models/user.rb +0 -28
  102. data/patterns.json +0 -394
  103. data/public/javascripts/bootstrap.min.js +0 -7
  104. data/public/javascripts/gitrob.js +0 -75
  105. data/public/javascripts/jquery-2.1.1.min.js +0 -4
  106. data/public/javascripts/lang-apollo.js +0 -2
  107. data/public/javascripts/lang-basic.js +0 -3
  108. data/public/javascripts/lang-clj.js +0 -18
  109. data/public/javascripts/lang-css.js +0 -2
  110. data/public/javascripts/lang-dart.js +0 -3
  111. data/public/javascripts/lang-erlang.js +0 -2
  112. data/public/javascripts/lang-go.js +0 -1
  113. data/public/javascripts/lang-hs.js +0 -2
  114. data/public/javascripts/lang-lisp.js +0 -3
  115. data/public/javascripts/lang-llvm.js +0 -1
  116. data/public/javascripts/lang-lua.js +0 -2
  117. data/public/javascripts/lang-matlab.js +0 -6
  118. data/public/javascripts/lang-ml.js +0 -2
  119. data/public/javascripts/lang-mumps.js +0 -2
  120. data/public/javascripts/lang-n.js +0 -4
  121. data/public/javascripts/lang-pascal.js +0 -3
  122. data/public/javascripts/lang-proto.js +0 -1
  123. data/public/javascripts/lang-r.js +0 -2
  124. data/public/javascripts/lang-rd.js +0 -1
  125. data/public/javascripts/lang-scala.js +0 -2
  126. data/public/javascripts/lang-sql.js +0 -2
  127. data/public/javascripts/lang-tcl.js +0 -3
  128. data/public/javascripts/lang-tex.js +0 -1
  129. data/public/javascripts/lang-vb.js +0 -2
  130. data/public/javascripts/lang-vhdl.js +0 -3
  131. data/public/javascripts/lang-wiki.js +0 -2
  132. data/public/javascripts/lang-xq.js +0 -3
  133. data/public/javascripts/lang-yaml.js +0 -2
  134. data/public/javascripts/prettify.js +0 -30
  135. data/public/javascripts/run_prettify.js +0 -34
  136. data/public/stylesheets/bootstrap.min.css +0 -7
  137. data/public/stylesheets/bootstrap.min.css.vanilla +0 -5
  138. data/public/stylesheets/gitrob.css +0 -88
  139. data/public/stylesheets/prettify.css +0 -51
  140. data/spec/lib/gitrob/observers/sensitive_files_spec.rb +0 -691
  141. data/spec/spec_helper.rb +0 -127
  142. data/views/blob.erb +0 -22
  143. data/views/organization.erb +0 -126
  144. data/views/repository.erb +0 -51
  145. data/views/user.erb +0 -51
@@ -0,0 +1,11 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:github_access_tokens) do
4
+ primary_key :id
5
+ foreign_key :assessment_id, :assessments, :on_delete => :cascade, :index => true
6
+ String :token, :size => 40, :fixed => true
7
+ DateTime :updated_at
8
+ DateTime :created_at
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:owners) do
4
+ primary_key :id
5
+ Integer :github_id, :index => true
6
+ foreign_key :assessment_id, :assessments, :on_delete => :cascade, :index => true
7
+ String :login
8
+ String :type, :size => 12, :index => true
9
+ String :url
10
+ String :html_url
11
+ String :avatar_url
12
+ String :name
13
+ String :blog
14
+ String :location
15
+ String :email
16
+ String :bio
17
+ Integer :repositories_count, :index => true, :default => 0
18
+ Integer :blobs_count, :index => true, :default => 0
19
+ Integer :findings_count, :index => true, :default => 0
20
+ DateTime :updated_at
21
+ DateTime :created_at
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:repositories) do
4
+ primary_key :id
5
+ Integer :github_id, :index => true
6
+ foreign_key :assessment_id, :assessments, :on_delete => :cascade, :index => true
7
+ foreign_key :owner_id, :owners, :on_delete => :cascade, :index => true
8
+ String :name
9
+ String :full_name
10
+ String :description
11
+ Boolean :private
12
+ String :url
13
+ String :html_url
14
+ String :homepage
15
+ Integer :size
16
+ String :default_branch
17
+ Integer :blobs_count, :default => 0
18
+ Integer :findings_count, :index => true, :default => 0
19
+ DateTime :updated_at
20
+ DateTime :created_at
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:blobs) do
4
+ primary_key :id
5
+ foreign_key :assessment_id, :assessments, :on_delete => :cascade, :index => true
6
+ foreign_key :owner_id, :owners, :on_delete => :cascade, :index => true
7
+ foreign_key :repository_id, :repositories, :on_delete => :cascade, :index => true
8
+ String :path
9
+ Integer :size, :index => true
10
+ String :sha, :size => 40, :fixed => true, :index => true
11
+ Integer :flags_count, :index => true, :default => 0
12
+ DateTime :updated_at
13
+ DateTime :created_at
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:flags) do
4
+ primary_key :id
5
+ foreign_key :assessment_id, :assessments, :on_delete => :cascade, :index => true
6
+ foreign_key :blob_id, :blobs, :on_delete => :cascade, :index => true
7
+ String :caption
8
+ String :description
9
+ DateTime :updated_at
10
+ DateTime :created_at
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:comparisons) do
4
+ primary_key :id
5
+ foreign_key :primary_assessment_id, :assessments, :on_delete => :cascade, :index => true
6
+ foreign_key :secondary_assessment_id, :assessments, :on_delete => :cascade, :index => true
7
+ Integer :blobs_count, :default => 0
8
+ Integer :repositories_count, :default => 0
9
+ Integer :owners_count, :default => 0
10
+ Integer :findings_count, :default => 0
11
+ Boolean :finished, :default => false, :index => true
12
+ Boolean :deleted, :default => false, :index => true
13
+ DateTime :updated_at
14
+ DateTime :created_at
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:blobs_comparisons) do
4
+ foreign_key :comparison_id, :comparisons, :on_delete => :cascade, :index => true
5
+ foreign_key :blob_id, :blobs, :on_delete => :cascade, :index => true
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:comparisons_repositories) do
4
+ foreign_key :comparison_id, :comparisons, :on_delete => :cascade, :index => true
5
+ foreign_key :repository_id, :repositories, :on_delete => :cascade, :index => true
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:comparisons_owners) do
4
+ foreign_key :comparison_id, :comparisons, :on_delete => :cascade, :index => true
5
+ foreign_key :owner_id, :owners, :on_delete => :cascade, :index => true
6
+ end
7
+ end
8
+ end
data/exe/gitrob ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # :nocov:
3
+ require "gitrob"
4
+
5
+ Gitrob::CLI.start(ARGV)
6
+ # :nocov:
data/gitrob.gemspec CHANGED
@@ -1,36 +1,43 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'gitrob/version'
4
+ require "gitrob/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "gitrob"
8
8
  spec.version = Gitrob::VERSION
9
9
  spec.authors = ["Michael Henriksen"]
10
10
  spec.email = ["michenriksen@neomailbox.ch"]
11
- spec.summary = %q{Reconnaissance tool for GitHub organizations.}
12
- spec.description = %q{Reconnaissance tool for GitHub organizations.}
11
+
12
+ spec.summary = %q{Reconnaissance tool for GitHub organizations}
13
13
  spec.homepage = "https://github.com/michenriksen/gitrob"
14
14
  spec.license = "MIT"
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "httparty", "~> 0.13"
22
- spec.add_dependency "methadone", "~> 1.7"
23
- spec.add_dependency "highline", "~> 1.6"
24
- spec.add_dependency "paint", "~> 0.8"
25
- spec.add_dependency "ruby-progressbar", "~> 1.6"
26
- spec.add_dependency "thread", "~> 0.1"
21
+ spec.add_dependency "thor", "~> 0.19"
22
+ spec.add_dependency "colorize", "~> 0.7"
23
+ spec.add_dependency "highline", "~> 1.7"
24
+ spec.add_dependency "thread", "~> 0.2"
25
+ spec.add_dependency "ruby-progressbar", "~> 1.7"
27
26
  spec.add_dependency "sinatra", "~> 1.4"
28
27
  spec.add_dependency "thin", "~> 1.6"
29
- spec.add_dependency "datamapper", "~> 1.2"
30
- spec.add_dependency "dm-postgres-adapter"
28
+ spec.add_dependency "pg", "~> 0.18"
29
+ spec.add_dependency "sequel", "~> 4.27"
30
+ spec.add_dependency "github_api", "~> 0.12"
31
+ spec.add_dependency "sucker_punch", "~> 2.0", ">= 2.0.1"
31
32
 
32
- spec.add_development_dependency "bundler", "~> 1.7"
33
+ spec.add_development_dependency "bundler", "~> 1.10"
33
34
  spec.add_development_dependency "rake", "~> 10.0"
34
- spec.add_development_dependency "rspec", "~> 3.1"
35
- spec.add_development_dependency "webmock", "~> 1.20"
35
+ spec.add_development_dependency "rspec"
36
+ spec.add_development_dependency "simplecov"
37
+ spec.add_development_dependency "timecop"
38
+ spec.add_development_dependency "rubocop"
39
+ spec.add_development_dependency "factory_girl"
40
+ spec.add_development_dependency "faker"
41
+ spec.add_development_dependency "awesome_print"
42
+ spec.add_development_dependency "webmock"
36
43
  end
@@ -0,0 +1,103 @@
1
+ module Gitrob
2
+ class BlobObserver
3
+ SIGNATURES_FILE_PATH = File.expand_path(
4
+ "../../../signatures.json", __FILE__)
5
+
6
+ REQUIRED_SIGNATURE_KEYS = %w(part type pattern caption description)
7
+ ALLOWED_TYPES = %w(regex match)
8
+ ALLOWED_PARTS = %w(path filename extension)
9
+
10
+ class Signature < OpenStruct; end
11
+ class CorruptSignaturesError < StandardError; end
12
+
13
+ def self.observe(blob)
14
+ signatures.each do |signature|
15
+ if signature.type == "match"
16
+ observe_with_match_signature(blob, signature)
17
+ else
18
+ observe_with_regex_signature(blob, signature)
19
+ end
20
+ end
21
+ blob.flags_count = blob.flags.count
22
+ blob.save
23
+ blob.flags_count
24
+ end
25
+
26
+ def self.signatures
27
+ load_signatures! unless @signatures
28
+ @signatures
29
+ end
30
+
31
+ def self.load_signatures!
32
+ @signatures = []
33
+ JSON.load(File.read(SIGNATURES_FILE_PATH)).each do |signature|
34
+ @signatures << Signature.new(signature)
35
+ end
36
+ validate_signatures!
37
+ rescue CorruptSignaturesError => e
38
+ raise e
39
+ rescue
40
+ raise CorruptSignaturesError, "Could not parse signature file"
41
+ end
42
+
43
+ def self.validate_signatures!
44
+ if !signatures.is_a?(Array) || signatures.empty?
45
+ fail CorruptSignaturesError,
46
+ "Signature file contains no signatures"
47
+ end
48
+ signatures.each do |signature|
49
+ validate_signature!(signature)
50
+ end
51
+ end
52
+
53
+ def self.validate_signature!(signature)
54
+ validate_signature_keys!(signature)
55
+ validate_signature_type!(signature)
56
+ validate_signature_part!(signature)
57
+ end
58
+
59
+ def self.validate_signature_keys!(signature)
60
+ REQUIRED_SIGNATURE_KEYS.each do |key|
61
+ unless signature.respond_to?(key)
62
+ fail CorruptSignaturesError,
63
+ "Missing required signature key: #{key}"
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.validate_signature_type!(signature)
69
+ unless ALLOWED_TYPES.include?(signature.type)
70
+ fail CorruptSignaturesError,
71
+ "Invalid signature type: #{signature.type}"
72
+ end
73
+ end
74
+
75
+ def self.validate_signature_part!(signature)
76
+ unless ALLOWED_PARTS.include?(signature.part)
77
+ fail CorruptSignaturesError,
78
+ "Invalid signature part: #{signature.part}"
79
+ end
80
+ end
81
+
82
+ def self.observe_with_match_signature(blob, signature)
83
+ haystack = blob.send(signature.part.to_sym)
84
+ return unless haystack == signature.pattern
85
+ blob.add_flag(
86
+ :caption => signature.caption,
87
+ :description => signature.description,
88
+ :assessment => blob.assessment
89
+ )
90
+ end
91
+
92
+ def self.observe_with_regex_signature(blob, signature)
93
+ haystack = blob.send(signature.part.to_sym)
94
+ regex = Regexp.new(signature.pattern, Regexp::IGNORECASE)
95
+ return if regex.match(haystack).nil?
96
+ blob.add_flag(
97
+ :caption => signature.caption,
98
+ :description => signature.description,
99
+ :assessment => blob.assessment
100
+ )
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,58 @@
1
+ module Gitrob
2
+ class CLI
3
+ class Command
4
+ attr_reader :options
5
+
6
+ def self.start(*args)
7
+ new(*args)
8
+ end
9
+
10
+ def initialize(*args) # rubocop:disable Lint/UnusedMethodArgument
11
+ end
12
+
13
+ def info(*args)
14
+ Gitrob::CLI.info(*args)
15
+ end
16
+
17
+ def task(message, fatal_error=false, &block)
18
+ Gitrob::CLI.task(message, fatal_error, &block)
19
+ end
20
+
21
+ def warn(*args)
22
+ Gitrob::CLI.warn(*args)
23
+ end
24
+
25
+ def error(*args)
26
+ Gitrob::CLI.error(*args)
27
+ end
28
+
29
+ def fatal(*args)
30
+ Gitrob::CLI.fatal(*args)
31
+ end
32
+
33
+ def debug(*args)
34
+ Gitrob::CLI.debug(*args)
35
+ end
36
+
37
+ def debugging_enabled?
38
+ Gitrob::CLI.debugging_enabled?
39
+ end
40
+
41
+ def output(*args)
42
+ Gitrob::CLI.output(*args)
43
+ end
44
+
45
+ def thread_pool
46
+ pool = Thread::Pool.new(options[:threads] || 5)
47
+ yield pool
48
+ pool.shutdown
49
+ end
50
+
51
+ def progress_bar(message, options)
52
+ progress_bar = Gitrob::CLI::ProgressBar.new(message, options)
53
+ yield progress_bar
54
+ progress_bar.finish
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,61 @@
1
+ module Gitrob
2
+ class CLI
3
+ module Commands
4
+ class AcceptTermsOfUse < Gitrob::CLI::Command
5
+ AGREEMENT_FILE_PATH = File.expand_path(
6
+ "../../../../../agreement.txt", __FILE__)
7
+
8
+ LICENSE_FILE_PATH = File.expand_path(
9
+ "../../../../../LICENSE.txt", __FILE__)
10
+
11
+ AGREEMENT = "Gitrob is designed for security professionals. " \
12
+ "If you use any information\nfound through this " \
13
+ "tool for malicious purposes that are not " \
14
+ "authorized by\nthe target, you are " \
15
+ "violating the terms of use and license of " \
16
+ "this\ntool. By typing y/yes, you agree to the " \
17
+ "terms of use and that you will use\nthis tool " \
18
+ "for lawful purposes only."
19
+
20
+ VALID_ANSWERS = %w(y yes n no)
21
+ POSITIVE_ANSWERS = %w(y yes)
22
+
23
+ def initialize(options)
24
+ @options = options
25
+ return if terms_of_use_accepted?
26
+ present_terms_of_use
27
+ handle_user_input
28
+ end
29
+
30
+ private
31
+
32
+ def terms_of_use_accepted?
33
+ File.exist?(AGREEMENT_FILE_PATH)
34
+ end
35
+
36
+ def present_terms_of_use
37
+ output "\n#{license}\n\n"
38
+ output AGREEMENT.light_red
39
+ end
40
+
41
+ def handle_user_input
42
+ if agree("\n\nDo you agree to the terms of use? (y/n): ".light_green)
43
+ accept_terms_of_use
44
+ else
45
+ fatal("Exiting Gitrob.")
46
+ end
47
+ end
48
+
49
+ def accept_terms_of_use
50
+ File.open(AGREEMENT_FILE_PATH, "w") do |file|
51
+ file.write("user accepted")
52
+ end
53
+ end
54
+
55
+ def license
56
+ File.read(LICENSE_FILE_PATH)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,75 @@
1
+ module Gitrob
2
+ class CLI
3
+ module Commands
4
+ class Analyze < Gitrob::CLI::Command
5
+ module Analysis
6
+ def analyze_repositories
7
+ repo_progress_bar do |progress|
8
+ github_data_manager.owners.each do |owner|
9
+ @db_owner = @db_assessment.save_owner(owner)
10
+ thread_pool do |pool|
11
+ repositories_for_owner(owner).each do |repo|
12
+ pool.process do
13
+ db_repo = @db_assessment.save_repository(repo, @db_owner)
14
+ blobs = blobs_for_repository(repo)
15
+ analyze_blobs(blobs, db_repo, @db_owner, progress)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def analyze_blobs(blobs, db_repo, db_owner, progress)
24
+ findings = 0
25
+ blobs.each do |blob|
26
+ db_blob = @db_assessment.save_blob(blob, db_repo, db_owner)
27
+ Gitrob::BlobObserver.observe(db_blob)
28
+ if db_blob.flags.count > 0
29
+ findings += 1
30
+ @db_assessment.findings_count += 1
31
+ db_owner.findings_count += 1
32
+ db_repo.findings_count += 1
33
+ end
34
+ end
35
+ db_owner.save
36
+ db_repo.save
37
+ progress.increment
38
+ report_findings(findings, db_repo, progress)
39
+ rescue => e
40
+ progress.error("#{e.class}: #{e.message}")
41
+ end
42
+
43
+ def report_findings(finding_count, repo, progress)
44
+ return if finding_count.zero?
45
+ files = finding_count == 1 ? "1 file" : "#{finding_count} files"
46
+ progress.info(
47
+ "Flagged #{files.to_s.light_yellow} " \
48
+ "in #{repo.full_name.bold}")
49
+ end
50
+ end
51
+
52
+ def repo_progress_bar
53
+ progress_bar(
54
+ "Analyzing repositories...",
55
+ :total => repository_count) do |progress|
56
+ yield progress
57
+ end
58
+ sleep 0.1
59
+ end
60
+
61
+ def repositories_for_owner(owner)
62
+ github_data_manager.repositories_for_owner(owner)
63
+ end
64
+
65
+ def blobs_for_repository(repo)
66
+ github_data_manager.blobs_for_repository(repo)
67
+ end
68
+
69
+ def repository_count
70
+ @github_data_manager.repositories.count
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,101 @@
1
+ module Gitrob
2
+ class CLI
3
+ module Commands
4
+ class Analyze < Gitrob::CLI::Command
5
+ module Gathering
6
+ def gather_owners
7
+ task("Gathering targets...") do
8
+ thread_pool do |pool|
9
+ github_data_manager.gather_owners(pool)
10
+ end
11
+ end
12
+ fatal("No users or organizations " \
13
+ "found; exiting") if owner_count.zero?
14
+ end
15
+
16
+ def gather_repositories
17
+ make_repo_gathering_progress_bar do |progress|
18
+ thread_pool do |pool|
19
+ github_data_manager
20
+ .gather_repositories(pool) do |owner, repositories|
21
+ repository_gathering_update(owner, repositories, progress)
22
+ end
23
+ end
24
+ end
25
+ fatal("No repositories found; exiting") if repo_count.zero?
26
+ info "Gathered #{repo_count} repositories"
27
+ end
28
+
29
+ def github_data_manager
30
+ unless @github_data_manager
31
+ @github_data_manager = Gitrob::Github::DataManager.new(
32
+ @targets, github_client_manager
33
+ )
34
+ end
35
+ @github_data_manager
36
+ end
37
+
38
+ def github_client_manager
39
+ unless @github_client_manager
40
+ @github_client_manager = Gitrob::Github::ClientManager.new(
41
+ client_manager_configuration
42
+ )
43
+ end
44
+ @github_client_manager
45
+ end
46
+
47
+ def client_manager_configuration
48
+ {
49
+ :endpoint => @options[:endpoint],
50
+ :site => @options[:site],
51
+ :ssl => {
52
+ :verify => @options[:verify_ssl]
53
+ },
54
+ :access_tokens => github_access_tokens
55
+ }
56
+ end
57
+
58
+ def github_access_tokens
59
+ if @options[:access_tokens]
60
+ @options[:access_tokens].gsub("\n", ",").split(",").map(&:strip)
61
+ else
62
+ Gitrob::CLI.configuration["github_access_tokens"]
63
+ end
64
+ end
65
+
66
+ def make_repo_gathering_progress_bar
67
+ total = github_data_manager.owners.count
68
+ progress_bar(
69
+ "Gathering repositories from #{total} targets...",
70
+ :total => total
71
+ ) do |progress|
72
+ yield progress
73
+ end
74
+ sleep 0.1
75
+ end
76
+
77
+ def repo_count
78
+ github_data_manager.repositories.count
79
+ end
80
+
81
+ def owner_count
82
+ github_data_manager.owners.count
83
+ end
84
+
85
+ def repository_gathering_update(owner, repositories, progress_bar)
86
+ count = repositories.count
87
+ progress_bar.increment
88
+ return if count.zero?
89
+ gathered = Gitrob::Utils.pluralize(
90
+ count,
91
+ "repository",
92
+ "repositories"
93
+ )
94
+ progress_bar.info("Gathered #{gathered} " \
95
+ "from #{owner['login'].bold}")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,63 @@
1
+ require "gitrob/cli/commands/analyze/gathering"
2
+ require "gitrob/cli/commands/analyze/analysis"
3
+
4
+ module Gitrob
5
+ class CLI
6
+ module Commands
7
+ class Analyze < Gitrob::CLI::Command
8
+ include Gathering
9
+ include Analysis
10
+
11
+ def initialize(targets, options)
12
+ Thread.abort_on_exception = true
13
+ @options = options
14
+ @targets = targets.split(",").map(&:strip).uniq
15
+ load_signatures!
16
+ create_database_assessment
17
+ gather_owners
18
+ gather_repositories
19
+ analyze_repositories
20
+ @db_assessment.finished = true
21
+ @db_assessment.save
22
+ start_web_server
23
+ end
24
+
25
+ private
26
+
27
+ def create_database_assessment
28
+ @db_assessment = Gitrob::Models::Assessment.create(
29
+ :endpoint => @options[:endpoint],
30
+ :site => @options[:site],
31
+ :verify_ssl => @options[:verify_ssl],
32
+ :finished => false
33
+ )
34
+ github_access_tokens.each do |access_token|
35
+ @db_assessment.save_github_access_token(access_token)
36
+ end
37
+ end
38
+
39
+ def load_signatures!
40
+ task("Loading signatures...", true) do
41
+ Gitrob::BlobObserver.load_signatures!
42
+ end
43
+ end
44
+
45
+ def start_web_server
46
+ return unless options[:server]
47
+ info "Starting web application on port #{options[:port]}..."
48
+ info "Browse to http://#{options[:bind_address]}:" \
49
+ "#{options[:port]}/ to see results!"
50
+
51
+ if debugging_enabled?
52
+ Sequel::Model.db.logger = QueryLogger.new(STDOUT)
53
+ end
54
+
55
+ Gitrob::WebApp.run!(
56
+ :port => options[:port].to_i,
57
+ :bind => options[:bind_address]
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end