In this chapter we will learn about discovering and fixing security vulnerabilities in a Rails application. The following content is only for example purposes and needn't be added to granite.
Time to time people discover security vulnerabilities in published software. Rubysec maintains a plain-text database of such security vulnerabilities.
ruby-advisory-db maintains a database of vulnerable Ruby Gems.
If we want to know whether our Ruby application is using any of the versions of Gems that are vulnerable, then we should run a scan of our Gemfile.lock against this database. This is exactly what bundler-audit does.
bundler-audit is a utility program which looks at an application's Gemfile.lock and then looks at ruby-advisory-db to see if we are using a vulnerable version of Gem or not.
Installing bundle-audit gem
Currently, we don't have to install these gems. But if we were to install it, then we would have to add the following lines into our Gemfile:
1gem 'bundler-audit', require: false 2gem 'ruby_audit', require: false
The bundle-audit command which is shown below is only mentioned to illustrate how auditing works. Do not run this command as it might lead to automatic version updates of gems in order to fix the security vulnerabilities that bundle-audit finds. This can further lead to gem version-specific issues throughout the flow of the book.
Bundler has a command bundle audit to invoke bundler-audit to do this:
1bundle exec bundle-audit 2 3Name: rails-html-sanitizer 4Version: 1.3.0 5Advisory: CVE-2015-7580 6Criticality: Unknown 7URL: https://groups.google.com/forum/#!topic/rubyonrails-security/uh--W4TDwmI 8Title: Possible XSS vulnerability in rails-html-sanitizer 9Solution: upgrade to ~> 1.0.3 10 11Vulnerabilities found!
This scan was run on a project, and we can see that one vulnerability is found.
Bundler downloads the Ruby Advisory DB to the local machine from time to time. We should always be running the scan against the latest copy of the Ruby Advisory DB.
To update our local copy of the Ruby Advisory DB, we should execute the following command:
1bundle audit update 2 3Updating ruby-advisory-db ... 4Cloning into '/Users/raj.singh/.local/share/ruby-advisory-db'... 5remote: Enumerating objects: 10, done. 6remote: Counting objects: 100% (10/10), done. 7remote: Compressing objects: 100% (6/6), done. 8remote: Total 4863 (delta 4), reused 10 (delta 4), pack-reused 4853 9Receiving objects: 100% (4863/4863), 867.08 KiB | 3.82 MiB/s, done. 10Resolving deltas: 100% (2400/2400), done. 11Updated ruby-advisory-db 12ruby-advisory-db: 456 advisories
Note that bundle audit update does not do any audit. It only clones the ruby-advisory-db to the local machine so that next time when we do the audit, it is performed against this update copy.
Every time we want to do a scan we need to do two things.
- Update the local Ruby Advisory DB
- Run the audit
Both of these operations can be combined to a single command:
1bundle exec bundle-audit --update
Running audit as part of CI
Who is going to run this audit on a regular basis? The best way to do it is to run this audit as a part of CI.
bundle-audit --update is in our config file for Wheel.
Let's say that at 10 AM all our tests were passing. Then at 10:05 AM Ruby Advisory DB added a new vulnerability that was recently detected. Any test that runs after 10:05 AM will fail the audit check.
Here is what we see in the console for a recent run:
1#!/bin/bash 2bundle exec bundle-audit check --update --ignore=CVE-2015-9284 3Updating ruby-advisory-db ... 4 5Cloning into '/home/circleci/.local/share/ruby-advisory-db'... 6 7Warning: Permanently added the RSA host key for IP address '220.127.116.11' to the list of known hosts. 8remote: Enumerating objects: 10, done. 9 10remote: Counting objects: 100% (10/10), done. 11 12remote: Compressing objects: 100% (6/6), done. 13 14remote: Total 4863 (delta 4), reused 10 (delta 4), pack-reused 4853 15 16Receiving objects: 100% (4863/4863), 865.47 KiB | 21.11 MiB/s, done. 17 18Resolving deltas: 100% (2400/2400), done. 19 20Updated ruby-advisory-db 21 22ruby-advisory-db: 456 advisories 23 24Name: actionpack 25 26Version: 18.104.22.168 27 28Advisory: CVE-2020-8185 29 30Criticality: Unknown 31 32URL: https://groups.google.com/g/rubyonrails-security/c/pAe9EV8gbM0 33 34Title: Untrusted users able to run pending migrations in production 35 36Solution: upgrade to >= 22.214.171.124 37 38 39 40Name: rack 41 42Version: 2.2.2 43 44Advisory: CVE-2020-8184 45 46Criticality: Unknown 47 48URL: https://groups.google.com/g/rubyonrails-security/c/OWtmozPH9Ak 49 50Title: Percent-encoded cookies can be used to overwrite existing prefixed cookie names 51 52Solution: upgrade to ~> 2.1.4, >= 2.2.3 53 54 55 56Vulnerabilities found! 57 58 59Exited with code exit status 1 60CircleCI received exit code 1
In this case two gems actionpack and rack are found to be vulnerable. The solution is also printed in the log:
1Solution: upgrade to >= 126.96.36.199 2Solution: upgrade to ~> 2.1.4, >= 2.2.3
Upgrade the Gems to fix the error.
There is nothing to commit in this chapter since all we had done was understand the usage of bundler-audit.