appcanary 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []