Scanny — Ruby on Rails security scanner
Pull request Compare This branch is 221 commits behind openSUSE:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Scanny is a Ruby on Rails security scanner. It parses Ruby files, looks for various suspicious patterns in them (by traversing the AST) and produces a report. Scanny aims to be simple (it does one thing well) and extensible (it is easy to define new patterns).

This is currently work in progress and it's probably not useful yet.


You need to install Rubinius first. You can then install Scanny:

$ git clone git://

The scanner is not available as a gem yet (this will come soon hopefully).


To scan one or more Ruby file, use the bin/scanny command and pass the files to scan as arguments. Scanny will check the files and print a nice report:

$ cat bad.rb
`ls #{ARGV[1]}`
$ bin/scanny bad.rb
bad.rb [2 checks done | 2 nodes inspected | 1 issues]
  - [high] bad.rb:1: Backticks and %x{...} pass the executed command through shell expansion. (CWE-88, CWE-78)

Found 1 issues.

Writing New Checks

Internally, Scanny consists of multiple checks, each responsible for finding and reporting one suspicious pattern in the code. You can easily extend Scanny by writing new checks.

The checks are loaded automatically from files in the lib/scanny/checks directory. Let's look how a simple check may look like:

module Scanny
  module Checks
    # Finds all invocations of "boo" and "moo" methods.
    class BooMooCheck < Check
      def pattern
        'Send<name = :boo | :moo> | SendWithArguments<name = :boo | :moo>'

      def check(node)
        issue :high, "The \"#{}\" method indicates wandering cows in the code.",
              :cwe => 999

Checks are subclasses of the Scanny::Checks::Check class and they implement two methods: pattern and check.

The pattern method

The pattern method returns a Machete pattern describing Rubinius AST nodes this check is interested in. See Machete documentation to learn about the pattern syntax.

Tip: When creating a check pattern it's often useful to inspect how Rubinius transforms some Ruby constructs into AST nodes. You can do this using the to_ast method:

'42'.to_ast # => #<Rubinius::AST::FixnumLiteral:0x36fc @value=42 @line=1>

The check method

The check method will be called on all AST nodes in the scanned files matched by the pattern returned by the pattern method. It will be passed the suspicious node. It can perform additional checks on it and report an issue if the node really is problematic.

Issues are reported using the issue method. As its arguments it accepts issue impact level (:info, :low, :medium or :high) and a message for the user, optionally followed by an options hash. The only currently implemented option is :cwe, which allows associating the issue with a CWE number (or multiple numbers if you pass an array).


Each check should be tested. The tests are written in RSpec and they are stored in the spec/scanny/checks directory. This is how a test for our sample check may look like:

require "spec_helper"

module Scanny::Checks
  describe BooMooCheck do
    it "reports \"boo\" correctly" do
      @runner.should check('boo').with_issue(
        issue(:high, "The \"boo\" method indicates wandering cows in the code.", 999)

    it "reports \"moo\" correctly" do
      @runner.should check('moo').with_issue(
        issue(:high, "The \"moo\" method indicates wandering cows in the code.", 999)

Aim to create as simple test cases as possible. Also test different kinds of issues separately. See the existing tests to learn how more complex checks are tested.


The tool was written as a replacement of Thomas Biege's Ruby on Rails scanner which was used internally at SUSE. This tool needed replacement because it look for suspicious patterns using just regular expressions, which is very rough and has expressivity problems with more complex patterns.

The original AST parsing and checking code was copied and adapted from Roodi, a tool for detecting Ruby code design issues.