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.
- checksums.yaml +4 -4
- data/.gitignore +30 -7
- data/.rspec +0 -1
- data/.rubocop.yml +55 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +42 -0
- data/CONTRIBUTING.md +137 -9
- data/Gemfile +11 -1
- data/Guardfile +42 -0
- data/LICENSE.txt +17 -18
- data/README.md +79 -29
- data/Rakefile +6 -0
- data/bin/console +34 -0
- data/bin/setup +7 -0
- data/db/migrations/001_create_assessments.rb +19 -0
- data/db/migrations/002_create_github_access_tokens.rb +11 -0
- data/db/migrations/003_create_owners.rb +24 -0
- data/db/migrations/004_create_repositories.rb +23 -0
- data/db/migrations/005_create_blobs.rb +16 -0
- data/db/migrations/006_create_flags.rb +13 -0
- data/db/migrations/007_create_comparisons.rb +17 -0
- data/db/migrations/008_create_blobs_comparisons.rb +8 -0
- data/db/migrations/009_create_comparisons_repositories.rb +8 -0
- data/db/migrations/010_create_comparisons_owners.rb +8 -0
- data/exe/gitrob +6 -0
- data/gitrob.gemspec +25 -18
- data/lib/gitrob/blob_observer.rb +103 -0
- data/lib/gitrob/cli/command.rb +58 -0
- data/lib/gitrob/cli/commands/accept_terms_of_use.rb +61 -0
- data/lib/gitrob/cli/commands/analyze/analysis.rb +75 -0
- data/lib/gitrob/cli/commands/analyze/gathering.rb +101 -0
- data/lib/gitrob/cli/commands/analyze.rb +63 -0
- data/lib/gitrob/cli/commands/banner.rb +25 -0
- data/lib/gitrob/cli/commands/configure.rb +123 -0
- data/lib/gitrob/cli/commands/server.rb +21 -0
- data/lib/gitrob/cli/progress_bar.rb +47 -0
- data/lib/gitrob/cli.rb +213 -0
- data/lib/gitrob/github/client_manager.rb +46 -0
- data/lib/gitrob/github/data_manager.rb +121 -0
- data/lib/gitrob/jobs/assessment.rb +12 -0
- data/lib/gitrob/jobs/comparison.rb +55 -0
- data/lib/gitrob/models/assessment.rb +96 -0
- data/lib/gitrob/models/blob.rb +50 -0
- data/lib/gitrob/models/comparison.rb +15 -0
- data/lib/gitrob/models/flag.rb +15 -0
- data/lib/gitrob/models/github_access_token.rb +17 -0
- data/lib/gitrob/models/owner.rb +23 -0
- data/lib/gitrob/models/repository.rb +20 -0
- data/lib/gitrob/utils.rb +19 -0
- data/lib/gitrob/version.rb +1 -1
- data/lib/gitrob/web_app.rb +292 -0
- data/lib/gitrob.rb +30 -113
- data/public/css/bootstrap.min.css +11 -0
- data/public/css/main.css +130 -0
- data/public/css/tomorrow-night.css +75 -0
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +273 -214
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/public/images/blob_spinner.gif +0 -0
- data/public/images/gear_spinner.gif +0 -0
- data/public/js/bootstrap.min.js +7 -0
- data/public/js/highlight.pack.js +2 -0
- data/public/js/highlight.worker.js +13 -0
- data/public/js/jquery-2.1.4.min.js +4 -0
- data/public/js/main.js +239 -0
- data/public/robots.txt +2 -0
- data/signatures.json +541 -0
- data/views/assessments/_assessments.erb +57 -0
- data/views/assessments/_comparable_assessments.erb +38 -0
- data/views/assessments/_comparisons.erb +111 -0
- data/views/assessments/compare.erb +22 -0
- data/views/assessments/findings.erb +55 -0
- data/views/assessments/repositories.erb +35 -0
- data/views/assessments/show.erb +1 -0
- data/views/assessments/users.erb +46 -0
- data/views/blobs/show.erb +37 -0
- data/views/comparisons/show.erb +125 -0
- data/views/errors/internal_server_error.erb +9 -0
- data/views/errors/not_found.erb +5 -0
- data/views/index.erb +43 -28
- data/views/layout.erb +38 -12
- data/views/repositories/show.erb +49 -0
- data/views/users/show.erb +54 -0
- metadata +217 -106
- data/bin/gitrob +0 -260
- data/lib/gitrob/github/blob.rb +0 -41
- data/lib/gitrob/github/http_client.rb +0 -127
- data/lib/gitrob/github/organization.rb +0 -99
- data/lib/gitrob/github/repository.rb +0 -72
- data/lib/gitrob/github/user.rb +0 -84
- data/lib/gitrob/observers/sensitive_files.rb +0 -83
- data/lib/gitrob/progressbar.rb +0 -52
- data/lib/gitrob/util.rb +0 -11
- data/lib/gitrob/webapp.rb +0 -76
- data/models/blob.rb +0 -35
- data/models/finding.rb +0 -14
- data/models/organization.rb +0 -32
- data/models/repo.rb +0 -22
- data/models/user.rb +0 -28
- data/patterns.json +0 -394
- data/public/javascripts/bootstrap.min.js +0 -7
- data/public/javascripts/gitrob.js +0 -75
- data/public/javascripts/jquery-2.1.1.min.js +0 -4
- data/public/javascripts/lang-apollo.js +0 -2
- data/public/javascripts/lang-basic.js +0 -3
- data/public/javascripts/lang-clj.js +0 -18
- data/public/javascripts/lang-css.js +0 -2
- data/public/javascripts/lang-dart.js +0 -3
- data/public/javascripts/lang-erlang.js +0 -2
- data/public/javascripts/lang-go.js +0 -1
- data/public/javascripts/lang-hs.js +0 -2
- data/public/javascripts/lang-lisp.js +0 -3
- data/public/javascripts/lang-llvm.js +0 -1
- data/public/javascripts/lang-lua.js +0 -2
- data/public/javascripts/lang-matlab.js +0 -6
- data/public/javascripts/lang-ml.js +0 -2
- data/public/javascripts/lang-mumps.js +0 -2
- data/public/javascripts/lang-n.js +0 -4
- data/public/javascripts/lang-pascal.js +0 -3
- data/public/javascripts/lang-proto.js +0 -1
- data/public/javascripts/lang-r.js +0 -2
- data/public/javascripts/lang-rd.js +0 -1
- data/public/javascripts/lang-scala.js +0 -2
- data/public/javascripts/lang-sql.js +0 -2
- data/public/javascripts/lang-tcl.js +0 -3
- data/public/javascripts/lang-tex.js +0 -1
- data/public/javascripts/lang-vb.js +0 -2
- data/public/javascripts/lang-vhdl.js +0 -3
- data/public/javascripts/lang-wiki.js +0 -2
- data/public/javascripts/lang-xq.js +0 -3
- data/public/javascripts/lang-yaml.js +0 -2
- data/public/javascripts/prettify.js +0 -30
- data/public/javascripts/run_prettify.js +0 -34
- data/public/stylesheets/bootstrap.min.css +0 -7
- data/public/stylesheets/bootstrap.min.css.vanilla +0 -5
- data/public/stylesheets/gitrob.css +0 -88
- data/public/stylesheets/prettify.css +0 -51
- data/spec/lib/gitrob/observers/sensitive_files_spec.rb +0 -691
- data/spec/spec_helper.rb +0 -127
- data/views/blob.erb +0 -22
- data/views/organization.erb +0 -126
- data/views/repository.erb +0 -51
- 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
|
data/exe/gitrob
ADDED
data/gitrob.gemspec
CHANGED
|
@@ -1,36 +1,43 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
-
lib = File.expand_path(
|
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require
|
|
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
|
-
|
|
12
|
-
spec.
|
|
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.
|
|
18
|
-
spec.
|
|
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 "
|
|
22
|
-
spec.add_dependency "
|
|
23
|
-
spec.add_dependency "highline", "~> 1.
|
|
24
|
-
spec.add_dependency "
|
|
25
|
-
spec.add_dependency "ruby-progressbar", "~> 1.
|
|
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 "
|
|
30
|
-
spec.add_dependency "
|
|
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.
|
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
|
33
34
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
34
|
-
spec.add_development_dependency "rspec"
|
|
35
|
-
spec.add_development_dependency "
|
|
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
|