Skip to content

Latest commit

 

History

History
97 lines (66 loc) · 4.48 KB

README.md

File metadata and controls

97 lines (66 loc) · 4.48 KB

Scanny

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.

Installation

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

$ git clone git://github.com/openSUSE/scanny.git

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

Usage

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>'
      end

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

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).

Tests

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)
      )
    end

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

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.

Acknowledgement

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.