vcsmap 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ce25dd4820391402c9309ce065ee9d349e566985
4
+ data.tar.gz: 81fbabbbc3ade9495cb3aa3b1307ee70ba1efa8e
5
+ SHA512:
6
+ metadata.gz: 621f538ba408036582602c9a1e4958e7c2f6f1626cf7f74acaa01d1bd3d0acc147f932cf1a5630bddd5b5112bbf5897879965570b3b723886d26bcfb271ab618
7
+ data.tar.gz: 68638ec08e026e5584a0dedb0f958b590a420dd383a47e51a06117443789ea1213553a95bf5be497f26ae84e816815fc0bfb21c5f1b702d68e1b46e7498a5d38
data/bin/vcsmap ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
5
+
6
+ require 'vcsmap'
7
+
8
+ cli = Vcsmap::CLI.new(ARGV)
9
+ cli.run
data/lib/vcsmap/cli.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'tty'
2
+
3
+ module Vcsmap
4
+ class CLI
5
+ def initialize(arguments)
6
+ @command = arguments[0]
7
+ @plugin = arguments[1]
8
+ @pages = arguments[2]
9
+ @output = arguments[3]
10
+ end
11
+
12
+ def run
13
+ case @command
14
+ when 'list'
15
+ list_plugins
16
+ when 'run'
17
+ run_plugin(@output === '--no-ascii')
18
+ when nil
19
+ abort "vcsmap requires a command. #{usage}"
20
+ else
21
+ abort "Command not recognized. #{usage}"
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def usage
28
+ "See http://vcsmap.org or open #{Helpers.project_directory}/README.md for instructions."
29
+ end
30
+
31
+ def list_plugins
32
+ puts "Available plugins:\n\n"
33
+ PluginList.render_list
34
+ exit
35
+ end
36
+
37
+ def run_plugin(no_ascii)
38
+ begin
39
+ plugin = PluginList.get_object(@plugin)
40
+ rescue KeyError
41
+ abort "Cannot find plugin with name '#{@plugin}'."
42
+ rescue NameError
43
+ abort "The plugin '#{@plugin}' has not been implemented yet."
44
+ end
45
+
46
+ unless @pages && (1..100).cover?(@pages.to_i)
47
+ abort 'Specify a number of pages (1-100) to load after the plugin name (1 page = ~10 results).'
48
+ end
49
+
50
+ puts 'Searching for matching files ...'
51
+ provider = Vcsmap::Provider::GitHub.new
52
+ results = provider.search(plugin, @pages.to_i)
53
+
54
+ bar = Vcsmap::ProgressBar.new(results.count)
55
+
56
+ data = []
57
+
58
+ abort "No files were found matching the search string (#{plugin.search_string})." if results.empty?
59
+
60
+ results.each do |result|
61
+ bar.step
62
+ file = HTTP.follow(true).get(result).body.to_s
63
+ credentials = plugin.credentials(file)
64
+ credentials << result.split('/').slice(3, 2).join('/')
65
+ # TODO: make an object that holds credentials and has empty? and valid? methods.
66
+ # this object should also be able to filter 'false' creds like localhost and default ones.
67
+ data << credentials unless credentials[1].nil? || credentials[1].empty?
68
+ end
69
+
70
+ if data.empty?
71
+ abort "Some files were loaded (#{results.count}), but none of them contained matching credentials. " \
72
+ 'You could try a higher page number.'
73
+ end
74
+
75
+ bar.clear
76
+
77
+ csv_writer = Vcsmap::CsvWriter.new(@plugin, plugin.table_header, data)
78
+ csv_writer.write!
79
+
80
+ unless no_ascii
81
+ table = TTY::Table.new plugin.table_header << 'Repo', data
82
+ puts table.render(:ascii)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,35 @@
1
+ require 'csv'
2
+
3
+ module Vcsmap
4
+ class CsvWriter
5
+ def initialize(plugin_name, plugin_header, data)
6
+ @file_path = file_path(plugin_name)
7
+ @header = header(plugin_header)
8
+ @data = data
9
+ end
10
+
11
+ def write!
12
+ puts "Writing CSV to #{Helpers.project_directory}/#{@file_path} ..."
13
+ CSV.open(@file_path, 'wb', force_quotes: true) do |csv|
14
+ csv << @header
15
+ @data.each do |line|
16
+ csv << line
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def folder
24
+ 'output/'
25
+ end
26
+
27
+ def file_path(plugin_name)
28
+ folder + "#{DateTime.now.strftime('%Y%m%dT%H%M')}-#{plugin_name}.csv"
29
+ end
30
+
31
+ def header(plugin_header)
32
+ plugin_header << 'Repo'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ module Vcsmap
2
+ module Helpers
3
+ def self.project_directory
4
+ File.dirname(File.dirname(__FILE__)).to_s
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ end
4
+ end
@@ -0,0 +1,67 @@
1
+ module Vcsmap
2
+ class PluginList
3
+ PLUGINS = {
4
+ aws: {
5
+ title: 'AWS access key',
6
+ description: 'Extracts AWS credentials from config and credentials files.',
7
+ class_name: 'Vcsmap::Plugin::AwsAccessToken'
8
+ },
9
+ wordpress_config: {
10
+ title: 'Wordpress configuration files',
11
+ description: 'Extracts database credentials from wp-config.php.',
12
+ class_name: 'Vcsmap::Plugin::WordpressConfig'
13
+ },
14
+ google_oauth: {
15
+ title: 'Google oAuth tokens',
16
+ description: 'Extracts oAuth credentials from client_secrets.json.',
17
+ class_name: 'Vcsmap::Plugin::GoogleOauth'
18
+ },
19
+ filezilla_xml: {
20
+ title: 'Filezilla configuration XML',
21
+ description: 'Extracts FTP credentials from Filezilla configuration files.',
22
+ class_name: 'Vcsmap::Plugin::FilezillaXml'
23
+ },
24
+ solr_dataconfig: {
25
+ title: 'Solr dataConfig.xml credentials',
26
+ description: 'Extracts JdbcDataSource credentials from a Solor dataConfig.xml.',
27
+ class_name: 'Vcsmap::Plugin::SolrDataconfig'
28
+ },
29
+ sublime_github: {
30
+ title: 'Sublime Text GitHub tokens',
31
+ description: 'Extracts GitHub tokens from the Sublime Text settings file for GitHub.',
32
+ class_name: 'Vcsmap::Plugin::GithubSublimesettings'
33
+ },
34
+ facebook_secrets: {
35
+ title: 'Facebook app secret',
36
+ description: 'Extracts Facebook tokens from fb_client_secrets.json.',
37
+ class_name: 'Vcsmap::Plugin::FacebookClientSecrets'
38
+ },
39
+ instagram_tokens: {
40
+ title: 'Instagram access tokens',
41
+ description: 'Extracts Instagram access tokens.',
42
+ class_name: 'Vcsmap::Plugin::InstagramTokens'
43
+ }
44
+ }.freeze
45
+
46
+ def self.all
47
+ PLUGINS.sort
48
+ end
49
+
50
+ def self.find(name)
51
+ PLUGINS.fetch(name.to_sym)
52
+ end
53
+
54
+ def self.get_object(name)
55
+ plugin = find(name)
56
+ Object.const_get(plugin[:class_name]).new
57
+ end
58
+
59
+ def self.render_list
60
+ all.each do |plugin|
61
+ puts Pastel.new.green "[#{plugin[0]}] #{plugin[1][:title]}"
62
+ puts plugin[1][:description]
63
+ puts
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,25 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class AwsAccessToken < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = ['aws_secret_access_key',
6
+ 'filename:config',
7
+ 'filename:credentials'].join('+')
8
+ @access_key_id_regex = /=\s+(AKIA[0-9A-Z]{16})/
9
+ @secret_access_key_regex = %r{=\s+([0-9a-zA-Z\/+]{40})}
10
+ end
11
+
12
+ def credentials(file)
13
+ @access_key_id = capture_match(@access_key_id_regex, file)
14
+ @secret_access_key = capture_match(@secret_access_key_regex, file)
15
+ [@access_key_id, @secret_access_key]
16
+ rescue NoMethodError
17
+ []
18
+ end
19
+
20
+ def table_header
21
+ ['Access Key ID', 'Secret Access Key']
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class BasePlugin
4
+ attr_reader :search_string
5
+
6
+ def capture_match(regex, file)
7
+ match = regex.match(clean_file(file))
8
+ return '' if match.nil?
9
+ match.captures.first
10
+ end
11
+
12
+ private
13
+
14
+ def clean_file(file)
15
+ file.scrub
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class FacebookClientSecrets < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = 'filename:fb_client_secrets.json+app_secret'
6
+ @app_id_regex = /(?:\"|')app_id(?:\"|')\:(?:\ |)(?:\"|')(.*?)(?:\"|')/i
7
+ @app_secret_regex = /(?:\"|')app_secret(?:\"|')\:(?:\ |)(?:\"|')(.*?)(?:\"|')/i
8
+ end
9
+
10
+ def credentials(file)
11
+ @app_id = capture_match(@app_id_regex, file)
12
+ @app_secret = capture_match(@app_secret_regex, file)
13
+ ['oAuth', @app_id, @app_secret]
14
+ rescue NoMethodError
15
+ []
16
+ end
17
+
18
+ def table_header
19
+ %w(Protocol app_id app_secret)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class FilezillaXml < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = 'filename:filezilla.xml+Pass'
6
+ @host_regex = /<Host>(.*)<\/Host>/
7
+ @username_regex = /<User>(.*)<\/User>/
8
+ @password_regex = /<Pass>(.*)<\/Pass>/
9
+ @encoded_password_regex = /<Pass encoding="base64">(.*)<\/Pass>/
10
+ @port_regex = /<Port>(.*)<\/Port>/
11
+ end
12
+
13
+ def credentials(file)
14
+ @host = capture_match(@host_regex, file)
15
+ @user = capture_match(@username_regex, file)
16
+ @pass = find_password(file)
17
+ @port = capture_match(@port_regex, file)
18
+ ['FTP', @host, @port, @user, @pass]
19
+ end
20
+
21
+ def table_header
22
+ %w(Protocol Host Port Username Password)
23
+ end
24
+
25
+ private
26
+
27
+ def find_password(file)
28
+ @pass = capture_match(@password_regex, file)
29
+ @base64_pass = capture_match(@encoded_password_regex, file)
30
+
31
+ return @pass unless @pass.empty?
32
+ return Base64.decode64(@base64_pass) unless @base64_pass.empty?
33
+ ''
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class GithubSublimesettings < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = 'filename:GitHub.sublime-settings+github_token'
6
+ @token_regex = /(?:\"|')github_token(?:\"|')\:(?:\ |)(?:\"|')(.*?)(?:\"|')/i
7
+ end
8
+
9
+ def credentials(file)
10
+ @token = capture_match(@token_regex, file)
11
+ ['GitHub token', @token]
12
+ rescue NoMethodError
13
+ []
14
+ end
15
+
16
+ def table_header
17
+ %w(Protocol Token)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class GoogleOauth < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = 'filename:client_secrets.json+.apps.googleusercontent.com'
6
+ @client_id_regex = /(?:\"|')client_id(?:\"|')\:(?:\ |)(?:\"|')(.*?)(?:\"|')/i
7
+ @client_secret_regex = /(?:\"|')client_secret(?:\"|')\:(?:\ |)(?:\"|')(.*?)(?:\"|')/i
8
+ end
9
+
10
+ def credentials(file)
11
+ @client_id = capture_match(@client_id_regex, file)
12
+ @client_secret = capture_match(@client_secret_regex, file)
13
+ ['oAuth', @client_id, @client_secret]
14
+ rescue NoMethodError
15
+ []
16
+ end
17
+
18
+ def table_header
19
+ %w(Protocol client_id client_secret)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class InstagramTokens < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = 'instagram+access_token'
6
+ @access_token_regex = /([0-9]{9,10}\.[a-f0-9]{7}\.[a-f0-9]{31,32})/
7
+ end
8
+
9
+ def credentials(file)
10
+ @access_token = capture_match(@access_token_regex, file)
11
+ ['Instagram', @access_token]
12
+ end
13
+
14
+ def table_header
15
+ ['Protocol', 'Access Token']
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class SolrDataconfig < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = 'filename:dataConfig.xml+dataSource+JdbcDataSource+password'
6
+ @url_regex = /url=\"(jdbc(.*?))\"/
7
+ @username_regex = /user=\"(.*?)\"/
8
+ @password_regex = /password=\"(.*?)\"/
9
+ end
10
+
11
+ def credentials(file)
12
+ @url = capture_match(@url_regex, file)
13
+ @user = capture_match(@username_regex, file)
14
+ @pass = capture_match(@password_regex, file)
15
+ ['JDBC', @url, @user, @pass]
16
+ rescue NoMethodError
17
+ []
18
+ end
19
+
20
+ def table_header
21
+ %w(Protocol URL Username Password)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ module Vcsmap
2
+ module Plugin
3
+ class WordpressConfig < Vcsmap::Plugin::BasePlugin
4
+ def initialize
5
+ @search_string = 'filename:wp-config.php+DB_PASSWORD'
6
+ @host_regex = /(?:\'|\")DB_HOST(?:\'|\")\,(?:\ |)(?:\'|\")(.*?)(?:\'|\")/i
7
+ @username_regex = /(?:\'|\")DB_USER(?:\'|\")\,(?:\ |)(?:\'|\")(.*?)(?:\'|\")/i
8
+ @password_regex = /(?:\'|\")DB_PASSWORD(?:\'|\")\,(?:\ |)(?:\'|\")(.*?)(?:\'|\")/i
9
+ @database_regex = /(?:\'|\")DB_NAME(?:\'|\")\,(?:\ |)(?:\'|\")(.*?)(?:\'|\")/i
10
+ end
11
+
12
+ def credentials(file)
13
+ @host = capture_match(@host_regex, file)
14
+ @user = capture_match(@username_regex, file)
15
+ @pass = capture_match(@password_regex, file)
16
+ @database = capture_match(@database_regex, file)
17
+ ['MySQL', @host, @user, @pass, @database]
18
+ rescue NoMethodError
19
+ []
20
+ end
21
+
22
+ def table_header
23
+ %w(Protocol Host Username Password Database)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ require 'tty'
2
+
3
+ module Vcsmap
4
+ class ProgressBar
5
+ def initialize(count)
6
+ @bar = TTY::ProgressBar.new("Loading #{count} possible results [:bar]", total: count)
7
+ end
8
+
9
+ def step
10
+ @bar.advance
11
+ end
12
+
13
+ def clear
14
+ print "\r"
15
+ $stdout.flush
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ module Vcsmap
2
+ module Provider
3
+ end
4
+ end
@@ -0,0 +1,46 @@
1
+ module Vcsmap
2
+ module Provider
3
+ class GitHub
4
+ def initialize
5
+ @cookie = get_cookie
6
+ end
7
+
8
+ def search(plugin, total_pages)
9
+ result_urls = []
10
+ @query = plugin.search_string
11
+ (1..total_pages).each do |page|
12
+ result_urls += get_results_from_page(page)
13
+ end
14
+ result_urls
15
+ end
16
+
17
+ private
18
+
19
+ def get_cookie
20
+ cookie = ENV['GITHUB_COOKIE']
21
+ http = HTTP.cookies(user_session: cookie).get('https://github.com/login')
22
+ abort "[Error] No valid session cookie in ENV['GITHUB_COOKIE']." unless http.status == 302
23
+ cookie
24
+ end
25
+
26
+ def get_results_from_page(page)
27
+ url = "https://github.com/search?p=#{page}&o=desc&q=#{@query}&s=indexed&type=Code"
28
+ html = HTTP.cookies(user_session: @cookie).get(url)
29
+ parse_response(html.body.to_s)
30
+ end
31
+
32
+ def parse_response(body)
33
+ doc = Nokogiri::HTML(body)
34
+ hits = doc.css('p.title a:last')
35
+ # TODO: use this when there are no file boxes.
36
+ # hits = doc.css('.code-list-item .full-path a')
37
+ urls = hits.map { |hit| 'https://github.com' + hit.attr('href') }
38
+ urls.map { |url| get_raw(url) }
39
+ end
40
+
41
+ def get_raw(url)
42
+ url.gsub('blob', 'raw')
43
+ end
44
+ end
45
+ end
46
+ end
data/lib/vcsmap.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'http'
2
+ require 'yaml'
3
+ require 'nokogiri'
4
+
5
+ # TODO: include only if needed
6
+ require_relative 'vcsmap/cli'
7
+ require_relative 'vcsmap/csv_writer'
8
+ require_relative 'vcsmap/progress_bar'
9
+
10
+ require_relative 'vcsmap/helpers'
11
+ require_relative 'vcsmap/plugin'
12
+ require_relative 'vcsmap/plugin_list'
13
+ require_relative 'vcsmap/provider'
14
+
15
+ # TODO: work on require_all
16
+ require_relative 'vcsmap/plugins/base_plugin'
17
+ require_relative 'vcsmap/plugins/aws_access_token'
18
+ require_relative 'vcsmap/plugins/facebook_client_secrets'
19
+ require_relative 'vcsmap/plugins/filezilla_xml'
20
+ require_relative 'vcsmap/plugins/github_sublimesettings'
21
+ require_relative 'vcsmap/plugins/google_oauth'
22
+ require_relative 'vcsmap/plugins/instagram_tokens'
23
+ require_relative 'vcsmap/plugins/solr_dataconfig'
24
+ require_relative 'vcsmap/plugins/wordpress_config'
25
+
26
+ require_relative 'vcsmap/providers/github'
27
+
28
+ module Vcsmap
29
+ VERSION = '2.0.1'.freeze
30
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vcsmap
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Melvin Lammerts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A plugin-based tool to scan public version control systems for sensitive
14
+ information.
15
+ email: hi@melvin.sh
16
+ executables:
17
+ - vcsmap
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/vcsmap
22
+ - lib/vcsmap.rb
23
+ - lib/vcsmap/cli.rb
24
+ - lib/vcsmap/csv_writer.rb
25
+ - lib/vcsmap/helpers.rb
26
+ - lib/vcsmap/plugin.rb
27
+ - lib/vcsmap/plugin_list.rb
28
+ - lib/vcsmap/plugins/aws_access_token.rb
29
+ - lib/vcsmap/plugins/base_plugin.rb
30
+ - lib/vcsmap/plugins/facebook_client_secrets.rb
31
+ - lib/vcsmap/plugins/filezilla_xml.rb
32
+ - lib/vcsmap/plugins/github_sublimesettings.rb
33
+ - lib/vcsmap/plugins/google_oauth.rb
34
+ - lib/vcsmap/plugins/instagram_tokens.rb
35
+ - lib/vcsmap/plugins/solr_dataconfig.rb
36
+ - lib/vcsmap/plugins/wordpress_config.rb
37
+ - lib/vcsmap/progress_bar.rb
38
+ - lib/vcsmap/provider.rb
39
+ - lib/vcsmap/providers/github.rb
40
+ homepage: http://vcsmap.org
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.5.2
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Scans public repositories for sensitive information.
64
+ test_files: []