appcanary 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d4e52c8e7c0d2fea19bcfa7967090d91db713bf8
4
+ data.tar.gz: 79207694fe161235240740d2ffa695a5f6955409
5
+ SHA512:
6
+ metadata.gz: e884ca5e62d5b68bea65ee3a9b8035828fef0b2e6258aa456023f059cdb5077e75b1842722711e6e63cc8d769aabc5c804d972c3c98f1a7f09e995bf5f9a2e8d
7
+ data.tar.gz: 8cca868bda7871c9c7253e030572addf4d502a45c9183d031c17de79b5a6a020ee957bd98e344ef3e32820da8d0ad23859a5d4bf505f9318dbd09ef13905f243
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in appcanary.gemspec
4
+ gemspec
@@ -0,0 +1,155 @@
1
+ # Appcanary
2
+
3
+ [![CircleCI](https://circleci.com/gh/appcanary/appcanary.rb.svg?style=svg)](https://circleci.com/gh/appcanary/appcanary.rb)
4
+
5
+ [Appcanary](https://appcanary.co) is a service which keeps track of which
6
+ versions of what packages are vulnerable to which security vulnerabilities, so
7
+ you don't have to.
8
+
9
+ The Appcanary ruby gem offers a way to automate your vulnerability checks either
10
+ as part of your Continuous Integration builds, or just programmatically
11
+ elsewhere. It also provides rake tasks for convenience.
12
+
13
+ ## Quickstart
14
+
15
+ These instructions will get you going on CircleCI with a rails project.
16
+
17
+ First, add the appcanary gem to your Gemfile:
18
+
19
+ ```ruby
20
+ gem "appcanary", :git => "https://github.com/appcanary/appcanary.rb"
21
+ ```
22
+
23
+ `bundle install` it to update your `Gemfile.lock`.
24
+
25
+ Add some configuration to your `config/initializers/appcanary.rb` file:
26
+
27
+ ```ruby
28
+ Appcanary.api_key = ENV["APPCANARY_API_KEY"] || "api key not set"
29
+ ```
30
+
31
+ Now, add the following lines to your `circle.yml` file:
32
+
33
+ ```yaml
34
+ dependencies:
35
+ # [ ... other dependency bits elided ... ]
36
+ post:
37
+ # outputs CVEs and references
38
+ - bundle exec rake appcanary:check
39
+ # update the appcanary monitor for this app
40
+ - bundle exec rake appcanary:update_monitor
41
+ ```
42
+
43
+ Don't forget to add the `APPCANARY_API_KEY` environment variable in your
44
+ project settings in the CircleCI web app. You can find your API key in
45
+ your [Appcanary settings](https://appcanary.com/settings).
46
+
47
+ Commit and push your changes, and CircleCI should do the right thing.
48
+
49
+ ## Alternative setups
50
+
51
+ There are several ways to use the Appcanary gem. The simplest of all is to write
52
+ a small program, in the context of a Bundler managed project, like this:
53
+
54
+ ```ruby
55
+ require "appcanary"
56
+
57
+ config = {
58
+ base_uri: "https://appcanary.com/api/v3",
59
+ api_key: "XXXXXXXXXXXXXXXXXXXXXXXXXXX",
60
+ monitor_name: "my_monitor"
61
+ }
62
+
63
+ canary = Appcanary::Client.new(config)
64
+
65
+ if canary.is_this_app_vulnerable?
66
+ puts "you appear to have your ass in the air"
67
+ end
68
+ ```
69
+
70
+ Instead, you can use a global configuration block, in the traditional rails
71
+ idiom:
72
+
73
+ ```ruby
74
+ Appcanary.configure do |canary|
75
+ canary.api_key = ENV["APPCANARY_API_KEY"] || "api key not set"
76
+ canary.base_uri = "https://appcanary.com/api/v3"
77
+ canary.monitor_name = "my_monitor"
78
+ end
79
+ ```
80
+
81
+ This config style is perhaps best suited to use an initializer file in rails
82
+ projects.
83
+
84
+ Here's a static configuration which is a bit less railsish:
85
+
86
+ ```ruby
87
+ Appcanary.api_key = ENV["APPCANARY_API_KEY"] || "api key not set"
88
+ Appcanary.gemfile_lock_path = "/path/to/gemfile"
89
+ ```
90
+
91
+ The gem may then be used without instantiating a client, like this:
92
+
93
+ ```ruby
94
+ if Appcanary.is_this_app_vulnerable? :critical
95
+ puts "I see your shiny attack surface! It BIG!"
96
+ end
97
+ ```
98
+
99
+ Finally, we provide two rake tasks, which are installed automatically in rails
100
+ projects. They are as follows:
101
+
102
+ ```
103
+ $ rake -T
104
+ ...
105
+ rake appcanary:check # Check vulnerability status
106
+ rake appcanary:update_monitor # Update the appcanary monitor for this project
107
+ ...
108
+
109
+ $ rake appcanary:check
110
+ CVE-2016-6316
111
+ CVE-2016-6317
112
+ $ rake appcanary:update_monitor
113
+ $
114
+ ```
115
+
116
+ If you're using the rake tasks in a non-Rails environment, you'll need to
117
+ configure the appcanary gem using the third and final method; a YAML file called
118
+ `appcanary.yml`, in your project root. The contents, unsurprisingly, look like
119
+ this:
120
+
121
+ ```yaml
122
+ api_token: "xxxxxxxxxxxxxxxxxxxxxxxxxx"
123
+ base_uri: "https://appcanary.com/api/v3"
124
+ monitor_name: "my_monitor"
125
+ ```
126
+
127
+ ## Configuration
128
+
129
+ As we've seen, you can configure the appcanary gem several different ways. All
130
+ configurations include the following items however.
131
+
132
+ | Key | Required? | Description | Notes |
133
+ | ------------------- | --------- | ----------- | ----- |
134
+ | `api_key` | Y | Your Appcanary API key, found in your [Appcanary settings](https://appcanary.com/settings). | |
135
+ | `gemfile_lock_path` | N | Path to your `Gemfile.lock`, which gets shipped to Appcanary for analysis. | Most of the time you can leave this undefined. |
136
+ | `monitor_name` | Y* | The base name for the monitor to be updated. *This is required if and only if you plan to use the `update_monitor` functionality. | If you're running in CI, the gem will attempt to acquire the name of the current branch and append that to your monitor name before sending the update. If a monitor does not already exist, it will be created. If this attribute is unset and the gem is loaded in the context of a Rails application, it will use the rails application name as the monitor name. |
137
+ | `base_uri` | N | The url for the Appcanary service endpoint. | You should leave this unset unless you have a very good reason not to. |
138
+
139
+
140
+ ## Development
141
+
142
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
143
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
144
+ prompt that will allow you to experiment.
145
+
146
+ To install this gem onto your local machine, run `bundle exec rake install`. To
147
+ release a new version, update the version number in `version.rb`, and then run
148
+ `bundle exec rake release`, which will create a git tag for the version, push
149
+ git commits and tags, and push the `.gem` file
150
+ to [rubygems.org](https://rubygems.org).
151
+
152
+ ## Contributing
153
+
154
+ Bug reports and pull requests are welcome on GitHub at https://github.com/appcanary/appcanary.rb.
155
+
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ load "lib/appcanary/tasks/appcanary/check.rake"
11
+ load "lib/appcanary/tasks/appcanary/monitor.rake"
12
+
13
+ task :default => :test
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'appcanary/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "appcanary"
8
+ spec.version = Appcanary::VERSION
9
+ spec.authors = ["J Irving", "Phill MV"]
10
+ spec.email = ["hello@appcanary.com"]
11
+
12
+ spec.summary = %q{Check your dependencies against Appcanary's database.}
13
+ spec.description = %q{}
14
+ spec.homepage = "https://appcanary.co"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against " \
22
+ "public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(test|spec|features)/})
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_runtime_dependency "multipart-post", "~> 2.0"
33
+ spec.add_runtime_dependency "json", ">= 1.8.3"
34
+
35
+ spec.add_development_dependency "bundler", "~> 1.13"
36
+ spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "minitest", "~> 5.0"
38
+ spec.add_development_dependency "pry", "~> 0.10"
39
+ end
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "appcanary"
5
+ require "optparse"
6
+
7
+ class AppcanaryCLI
8
+ class << self
9
+ def api_key_opt(opts)
10
+ opts.on("-a", "--api-key API_KEY", :REQUIRED,
11
+ "Your Appcanary API key. Find it at https://appcanary.com/settings") do |ak|
12
+ Appcanary.api_key = ak
13
+ end
14
+ end
15
+
16
+ def gemfile_lock_opt(opts)
17
+ opts.on("-g", "--gemfile-lock GEMFILE_LOCK", :OPTIONAL,
18
+ "Path to the Gemfile.lock to ship to Appcanary") do |gl|
19
+ Appcanary.gemfile_lock_path = gl
20
+ end
21
+ end
22
+
23
+ def base_uri_opt(opts)
24
+ opts.on("-b", "--base-uri BASE_URI", :OPTIONAL,
25
+ "The URL for the Appcanary endpoint to use.") do |bu|
26
+ Appcanary.base_uri = bu
27
+ end
28
+ end
29
+
30
+ def monitor_name_opt(opts)
31
+ opts.on("-m", "--monitor-name MONITOR_NAME", :REQUIRED,
32
+ "The name of the Appcanary monitor to update.") do |mn|
33
+ Appcanary.monitor_name = mn
34
+ end
35
+ end
36
+
37
+ def parse
38
+ top_level_help = <<-HELP
39
+ Subcommands are:
40
+ check - Check your gem bundle for vulnerabilities
41
+ update - Update an Appcanary monitor
42
+
43
+ See "appcanary COMMAND --help" for more information about a specific command.
44
+ HELP
45
+
46
+ common = OptionParser.new do |opts|
47
+ opts.banner = "Usage: appcanary check|update [options]"
48
+ opts.separator ""
49
+ opts.separator top_level_help
50
+ end
51
+
52
+ subcommands = {
53
+ "update" => OptionParser.new do |opts|
54
+ opts.banner = "Usage: update [options]"
55
+ api_key_opt(opts)
56
+ base_uri_opt(opts)
57
+ gemfile_lock_opt(opts)
58
+ monitor_name_opt(opts)
59
+ end,
60
+ "check" => OptionParser.new do |opts|
61
+ opts.banner = "Usage: check [options]"
62
+ api_key_opt(opts)
63
+ base_uri_opt(opts)
64
+ gemfile_lock_opt(opts)
65
+ end
66
+ }
67
+
68
+ common.order!
69
+
70
+ command = ARGV.shift
71
+ if command.nil?
72
+ puts "No subcommand found -- try appcanary --help"
73
+ else
74
+ subcommands[command].order!
75
+ end
76
+
77
+ command
78
+ end
79
+ end
80
+ end
81
+
82
+ def run_check
83
+ response = Appcanary.check
84
+ if response["meta"]["vulnerable"]
85
+ response["included"].map do |vuln|
86
+ vuln["attributes"]["reference-ids"]
87
+ end.flatten.uniq.each do |ref|
88
+ puts ref
89
+ end
90
+ end
91
+ end
92
+
93
+ def run_appcanary_command
94
+ case AppcanaryCLI.parse
95
+ when "update"
96
+ Appcanary.update_monitor!
97
+ when "check"
98
+ run_check
99
+ end
100
+ rescue => e
101
+ puts e
102
+ end
103
+
104
+ run_appcanary_command
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "appcanary"
5
+
6
+ require "pry"
7
+ Pry.start
@@ -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
+ require "appcanary/version"
2
+ require "appcanary/configuration"
3
+ require "appcanary/http"
4
+ require "appcanary/assert"
5
+ require "appcanary/railtie" if defined?(Rails)
@@ -0,0 +1,59 @@
1
+ require "json"
2
+
3
+ module Appcanary
4
+ class Client
5
+ include HTTP
6
+
7
+ attr_reader :config
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ def is_this_app_vulnerable?(criticality = nil)
14
+ check do |response|
15
+ vulnerable = response["meta"]["vulnerable"]
16
+ if vulnerable == true || vulnerable == "true"
17
+ return true if criticality.nil?
18
+
19
+ cnt = count_criticalities(response)[criticality.to_s]
20
+ cnt && cnt > 0
21
+ else
22
+ false
23
+ end
24
+ end
25
+ end
26
+
27
+ def check(&block)
28
+ response = ship_gemfile(:check, config)
29
+
30
+ if block
31
+ block.call(response)
32
+ else
33
+ response
34
+ end
35
+ end
36
+
37
+ def update_monitor!
38
+ if config.sufficient_for_monitor?
39
+ ship_gemfile(:monitors, config)
40
+ else
41
+ raise Appcanary::ConfigurationError.new("Appcanary.monitor_name = ???")
42
+ end
43
+ end
44
+
45
+ private
46
+ def count_frequencies(arr)
47
+ arr.inject({}) do |freqs, i|
48
+ freqs[i] ||= 0
49
+ freqs[i] += 1
50
+ freqs
51
+ end
52
+ end
53
+
54
+ def count_criticalities(response)
55
+ count_frequencies(
56
+ response["included"].map { |vuln| vuln["attributes"]["criticality"] })
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,154 @@
1
+ require "yaml"
2
+
3
+ module Appcanary
4
+ APPCANARY_DEFAULT_BASE_URI = "https://appcanary.com/api/v3"
5
+
6
+ APPCANARY_YAML = "appcanary.yml"
7
+ GEMFILE_LOCK = "Gemfile.lock"
8
+
9
+ class Configuration
10
+ attr_accessor :base_uri, :api_key, :monitor_name, :gemfile_lock_path
11
+
12
+ def [](k)
13
+ self.send(k.to_s)
14
+ end
15
+
16
+ def initialize
17
+ self.base_uri = APPCANARY_DEFAULT_BASE_URI
18
+ self.monitor_name = maybe_guess_monitor_name
19
+ self.gemfile_lock_path = locate_gemfile_lockpath
20
+ end
21
+
22
+ def maybe_guess_monitor_name
23
+ Rails.application.class.parent_name if defined?(Rails)
24
+ end
25
+
26
+ def locate_gemfile_lockpath
27
+ # make an educated guess
28
+ gemfile_lock_path = "#{Dir.pwd}/#{GEMFILE_LOCK}"
29
+ return gemfile_lock_path if File.exist?(gemfile_lock_path)
30
+
31
+ # otherwise try out bundler
32
+ begin
33
+ require "bundler"
34
+ if defined?(Bundler)
35
+ Bundler.default_lockfile.to_s
36
+ end
37
+ rescue LoadError
38
+ # ignore, handle at resolution time
39
+ end
40
+ end
41
+
42
+ def sufficient_for_check?
43
+ ! (base_uri.nil? || api_key.nil? || gemfile_lock_path.nil?)
44
+ end
45
+
46
+ def sufficient_for_monitor?
47
+ sufficient_for_check? && !monitor_name.nil?
48
+ end
49
+
50
+ def resolve!
51
+ # 1. static configuration takes precedence over yaml, and only one may be
52
+ # used. If the configuration block is present and valid, use it
53
+ # exclusively, otherwise looks for yaml.
54
+ #
55
+ # 2. within that context, use the following rules
56
+ # - api_key: required, no attempt to guess/derive
57
+ #
58
+ # - gemfile_lock_path: path to Gemfile.lock; if missing, attempt to
59
+ # guess based on Bundler (if defined -- if not, attempt to require
60
+ # it, and fail angrily if that doesn't work).
61
+ #
62
+ # - monitor_name: name to use as the base for the monitor update. If
63
+ # this is missing, attempt to derive it by finding the rails app name
64
+ # (if Rails is defined). Otherwise fail, but only when updating
65
+ # monitors, not when running checks.
66
+ #
67
+ # - base_uri: if this is missing (as it probably should be in all cases
68
+ # except working on this gem), default to prod appcanary.com.
69
+ unless self.sufficient_for_check?
70
+ yaml_file = "#{Dir.pwd}/#{APPCANARY_YAML}"
71
+ begin
72
+ load_yaml_config!(yaml_file)
73
+ rescue
74
+ load_yaml_config!("#{Bundler.root}/#{APPCANARY_YAML}") if defined?(Bundler)
75
+ end
76
+ end
77
+
78
+ # UX for validation
79
+ errors = []
80
+ errors << "\tAppcanary.api_key = ???" if api_key.nil?
81
+ errors << "\tAppcanary.gemfile_lock_path = ???" if gemfile_lock_path.nil?
82
+ unless errors.empty?
83
+ raise ConfigurationError.new("Missing configuration:\n#{errors.join("\n")}")
84
+ end
85
+
86
+ self
87
+ end
88
+
89
+ def load_yaml_config!(path)
90
+ begin
91
+ yaml_config = YAML.load_file(path)
92
+ self.api_key = yaml_config["api_key"]
93
+ self.gemfile_lock_path = yaml_config["gemfile_lock_path"]
94
+ self.monitor_name = yaml_config["monitor_name"]
95
+ self.base_uri = yaml_config["base_uri"] || APPCANARY_DEFAULT_BASE_URI
96
+ rescue Errno::ENOENT
97
+ # ignore, fall through
98
+ rescue => e
99
+ # there was a file, but something was wrong with it
100
+ raise ConfigurationError.new(e)
101
+ end
102
+ end
103
+ end
104
+
105
+ class ConfigurationError < RuntimeError
106
+ SUFFIX = <<-EOS
107
+ Consult the following docs for more information:
108
+ - https://github.com/appcanary/appcanary.rb
109
+ - https://appcanary.com/settings
110
+ EOS
111
+
112
+ def initialize(msg)
113
+ super("#{msg}\n\n#{SUFFIX}")
114
+ end
115
+ end
116
+
117
+ class << self
118
+ def configuration
119
+ @@configuration ||= Configuration.new
120
+ end
121
+
122
+ def reset
123
+ @@configuration = Configuration.new
124
+ end
125
+
126
+ def configure(&block)
127
+ block.call(configuration) if block
128
+ end
129
+
130
+ # another way to do static configuration
131
+ def api_key=(val); configuration.api_key = val; end
132
+ def gemfile_lock_path=(val); configuration.gemfile_lock_path = val; end
133
+ def monitor_name=(val); configuration.monitor_name = val; end
134
+ def base_uri=(val); configuration.base_uri = val; end
135
+
136
+ # static API
137
+ def is_this_app_vulnerable?(criticality = nil)
138
+ canary.is_this_app_vulnerable?(criticality)
139
+ end
140
+
141
+ def update_monitor!
142
+ canary.update_monitor!
143
+ end
144
+
145
+ def check
146
+ canary.check
147
+ end
148
+
149
+ private
150
+ def canary
151
+ @@canary ||= Appcanary::Client.new(Appcanary.configuration.resolve!)
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,96 @@
1
+ require "net/http"
2
+ require "net/http/post/multipart"
3
+ require "json"
4
+
5
+ module Appcanary
6
+ class ServiceError < RuntimeError
7
+ end
8
+
9
+ # In this module, `config` should always be a hash, or an object that responds
10
+ # to `[](k)`, typically obtained by calling `Appcanary::Configuration#resolve!`.
11
+ module HTTP
12
+ def ship_gemfile(endpoint, config, &block)
13
+ payload = {
14
+ file: config[:gemfile_lock_path],
15
+ platform: "ruby"
16
+ }
17
+
18
+ parsed_response = ship_file(endpoint, payload, config)
19
+
20
+ if block
21
+ block.call(parsed_response)
22
+ else
23
+ parsed_response
24
+ end
25
+ end
26
+
27
+ def ship_file(endpoint, payload, config)
28
+ resp = try_request_with(:put, endpoint, payload, config)
29
+
30
+ unless resp.code.to_s == "200"
31
+ resp = try_request_with(:post, endpoint, payload, config)
32
+ end
33
+
34
+ unless %w[200 201].include? resp.code.to_s
35
+ raise ServiceError.new("Failed to ship file to Appcanary: #{resp}")
36
+ end
37
+
38
+ JSON.parse(resp.body)
39
+ end
40
+
41
+ private
42
+ def url_for(endpoint, config)
43
+ case endpoint
44
+ when :monitors
45
+ monitor = "#{config[:monitor_name]}"
46
+
47
+ if ENV["CIRCLECI"] && ENV["CIRCLECI"] == "true"
48
+ monitor = "#{monitor}_#{ENV['CIRCLE_BRANCH']}"
49
+ end
50
+
51
+ # these are rails routing delimiters
52
+ monitor.gsub!("/", "_")
53
+ monitor.gsub!(".", "_")
54
+
55
+ URI.parse("#{config[:base_uri]}/monitors/#{monitor}")
56
+ when :check
57
+ URI.parse("#{config[:base_uri]}/check")
58
+ else
59
+ # internal brokenness
60
+ raise RuntimeError.new("Unknown Appcanary endpoint: #{endpoint.to_s}!")
61
+ end
62
+ end
63
+
64
+ REQUEST_TYPES = {
65
+ post: Net::HTTP::Post::Multipart,
66
+ put: Net::HTTP::Put::Multipart
67
+ }
68
+
69
+ def try_request_with(method, endpoint, payload, config)
70
+ request_type = REQUEST_TYPES[method]
71
+ url = url_for(endpoint, config)
72
+ filename = File.basename(payload[:file])
73
+ url.query = URI.encode_www_form("platform" => payload[:platform])
74
+
75
+ File.open(payload[:file]) do |file|
76
+ params = {}.tap do |p|
77
+ p["file"] = UploadIO.new(file, "text/plain", filename)
78
+ p["platform"] = payload[:platform]
79
+
80
+ if payload[:version]
81
+ p["version"] = payload[:version]
82
+ url.query = url.query.merge("version", payload[:version])
83
+ end
84
+ end
85
+
86
+ headers = {"Authorization" => "Token #{config[:api_key]}"}
87
+ req = request_type.new(url.path, params, headers, SecureRandom.base64)
88
+ options = { use_ssl: url.scheme == "https" }
89
+
90
+ Net::HTTP.start(url.host, url.port, options) do |http|
91
+ http.request(req)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,12 @@
1
+ require "rails/railtie"
2
+
3
+ module Appcanary
4
+ class Railtie < Rails::Railtie
5
+ rake_tasks do
6
+ spec = Gem::Specification.find_by_name("appcanary")
7
+ gem_root = spec.gem_dir
8
+ load "#{gem_root}/lib/appcanary/tasks/appcanary/check.rake"
9
+ load "#{gem_root}/lib/appcanary/tasks/appcanary/monitor.rake"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ require "appcanary"
2
+ require "rake"
3
+
4
+ def run_check
5
+ response = Appcanary.check
6
+ if response["meta"]["vulnerable"]
7
+ response["included"].map do |vuln|
8
+ vuln["attributes"]["reference-ids"]
9
+ end.flatten.uniq.each do |ref|
10
+ puts ref
11
+ end
12
+ end
13
+ rescue => e
14
+ puts e
15
+ end
16
+
17
+ namespace :appcanary do
18
+ desc "Check vulnerability status"
19
+ if defined?(Rails)
20
+ task :check => :environment do
21
+ run_check
22
+ end
23
+ else
24
+ task :check do
25
+ run_check
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ require "appcanary"
2
+ require "rake"
3
+
4
+ def run_update_monitor
5
+ Appcanary.update_monitor!
6
+ rescue => e
7
+ puts e
8
+ end
9
+
10
+ namespace :appcanary do
11
+ desc "Update the appcanary monitor for this project"
12
+ if defined?(Rails)
13
+ task :update_monitor => :environment do
14
+ run_update_monitor
15
+ end
16
+ else
17
+ task :update_monitor do
18
+ run_update_monitor
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Appcanary
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appcanary
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - J Irving
8
+ - Phill MV
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2017-01-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: multipart-post
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: json
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 1.8.3
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 1.8.3
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.13'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.13'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '10.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '5.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '5.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: pry
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '0.10'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '0.10'
98
+ description: ''
99
+ email:
100
+ - hello@appcanary.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - README.md
109
+ - Rakefile
110
+ - appcanary.gemspec
111
+ - bin/appcanary
112
+ - bin/console
113
+ - bin/setup
114
+ - lib/appcanary.rb
115
+ - lib/appcanary/assert.rb
116
+ - lib/appcanary/configuration.rb
117
+ - lib/appcanary/http.rb
118
+ - lib/appcanary/railtie.rb
119
+ - lib/appcanary/tasks/appcanary/check.rake
120
+ - lib/appcanary/tasks/appcanary/monitor.rake
121
+ - lib/appcanary/version.rb
122
+ homepage: https://appcanary.co
123
+ licenses: []
124
+ metadata:
125
+ allowed_push_host: https://rubygems.org
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.5.2
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Check your dependencies against Appcanary's database.
146
+ test_files: []