A client recently expressed concern with a number of gems added to his project. A quick explanation and a little documentation cleared up what each gem was doing/why we needed it.
This satisfied the client, but it got me wondering: what's the worst thing that could happen from a gem if it was malicious? The worst case I could imagine would be the client's customer's data getting stolen, the customers completely loosing faith in the site, and the client's project failing because of it.
How likely is this to happen? I don't really know.
How hard would it be for someone to do this?
I decided to see what it would take to harvest usernames and passwords from a typical Rails app using Devise for authentication. In less than 5 minutes, I had written an initializer which modified the behavior of the Devise controller to write out usernames and passwords to an HTML file in the public directory of the app.
The code wasn't clever at all. I copied/pasted the create action, and added three extra lines to write out the data to the file.
class Devise::SessionsController < ApplicationController
prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
include Devise::Controllers::InternalHelpers
# POST /resource/sign_in
def create
File.open("#{Rails.root}/public/passwords.html", 'a+') do |f|
f.write("#{params[:user][:email]} #{params[:user][:password]}<br />")
end
...
So the answer to my question, how hard would it be for someone to write a malicious gem that would compromise customer data: dead easy.
I packaged up the code as a gem. Anyone can easily pwn their own Devise Rails app by adding the following line to their Gemfile:
gem 'devise_hack'
Of course, who would install a gem that would pwn their own app? No one, but what about a "long con" approach?
Say I wrote a useful gem, pushed updates occasionally, and got a decent level adoption. At this point I could push a new version of the gem which contained a little hack, and wait for the usernames and passwords to roll in. Maybe like this little guy…
gem 'awesome_rails_flash_messages'
This little gem takes all of your Rails flash messages and makes them more awesome. Simple as that. Ohh, it also logs and requests containing a password to a file AND posts it to an external web service, but that's nothing to worry about.
So how do you avoid these malicious gems? For this dead simple hack, it is dead simple to identify. All you have to do is look at the source code. If you see code that is writing credentials to a file, maybe posting to an external web service, or sending emails when it really shouldn't be... you might want to reconsider using that gem.
Have you upgraded RubyGems lately? Is your console suddenly filled with warnings like this?
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /Users/chaffee/.rvm/gems/ruby-1.9.2-p0/specifications/thin-1.2.7.gemspec:10.
You may be showing signs of a new malady known as Warningitis! So far there is no cure, but doing the following will temporarily cure your symptoms:
gem update --system 1.7.2
Several experimental treatments are being hastily developed as well, but these have not yet been approved by the FDA. Check the "scary warnings are scary" bug thread for more details.
This has been a public health alert. Please do not panic. SARS masks and iodine pills are not recommended at this time.
Uh-oh!
ERROR: While executing gem ... (Gem::RemoteFetcher::FetchError)
bad response Moved Permanently 301 (http://gems.rubyforge.org/latest_specs.4.8)
Whew!
gem sources -a http://rubygems.org/
gem sources -r http://gems.rubyforge.org/
Looks like they weren't kidding when they said to switch from rubyforge to rubygems.org (née gemcutter)!
[Edited to change "http://production.s3.rubygems.org/" to "http://rubygems.org/". Note that the trailing slash is significant!]
Remember the annotate_models rake task? Dave Thomas wrote it many years ago and it corrects one of the flaws in ActiveRecord: it describes the schema for a table as a comment inside the Ruby model file that it maps to. Unfortunately Dave hasn't had time to maintain it, so a couple of years ago I cleaned up some bugs and re-published it as a pastie. Then Cuong Tran made it a gem and put it on Github, and since then, there's been a whole lotta forkin' goin' on!
I recently pulled in a bunch of the forks into ctran's master branch, and just pushed it to Gemcutter as version 2.4.0. Just run gem sources and make sure http://gemcutter.org is in your list -- otherwise do gem source -a http://gemcutter.org -- and sudo gem install annotate and it'll install a binary called annotate in /usr/bin. See the README on github for more info and have fun!
One caveat: ImageMagick installs a tool called annotate too (if you're using MacPorts it's in /opt/local/bin/annotate). So if you see
Usage: annotate imagein.jpg imageout.jpg
then put /usr/bin ahead on the path and you'll get ours instead.
I'm currently working on a large app where certain things have to happen when records are created, updated and deleted, such as:
- Publishing to an activity feed
- Generating emails
- Adding entries to a changelog
- Generating tasks and reminders
Further, the requirements state that admin users should be able to configure which of these actions happen for which objects in the system, who they go to, what the text is etc...
At first this looks like a great place for ActiveRecord Observers. However, after working with Observers there are a few things I dislike - namely that you can't easily apply observers to all of your models, and you can't selectively turn them on and off in tests. To remedy that problem, I created ActiveModelListener.
ActiveModelListener is a simple, global ActiveRecord event listener framework, using a middleware-esque architecture that can easily be turned on and off.
ActiveHash is a simple base class that allows you to use a ruby hash as a readonly datasource for an ActiveRecord-like model.
ActiveHash assumes that every hash has an :id key, which is what you would probably store in a database. This allows you to seemlessly upgrade from ActiveHash objects to full ActiveRecord objects without having to change any code in your app, or any foreign keys in your database.
It also allows you to use #belongs_to in your AR objects.
ActiveHash can also be useful to create simple test classes that run without a database - ideal for testing plugins or gems that rely on simple AR behavior, but don't want to deal with databases or migrations for the spec suite.
ActiveHash also ships with:
- ActiveFile: a base class that will reload data from a flat file every time the flat file is changed
- ActiveYaml: a base class that will turn YAML into a hash and load the data into an ActiveHash object
ActiveApi allows you to define a schema in Ruby, and use that schema to convert ruby objects to xml. An example looks like this:
Schema.version(:v1) do |schema|
schema.define :article do |t|
t.attribute :id
t.string :title
t.date :published_on
t.has_many :comments
end
end
[Updated: added instructions "If you are using a widget in rails, you now need to inherit from RailsWidget"]
Erector has been around for almost 2 years now, but we've always been a little reluctant to market it heavily. One reason has been that the API was a little inelegant. Another is that Rails integration wasn't as seamless as we'd like.
With today's release of version 0.6.3, we have hopefully fixed both of those problems. With the new RailsWidget we've got a clean separation between core Erector functionality and the magic we need to make it work with Rails. We've renamed "render" to "content" so we don't conflict with the standard Rails render method. And we've changed the API for smoother lifecycle management -- the constructor is about initializing the widget, and the refurbished "widget" method is about setting it up to emit its HTML.
The bad news is that you'll have to change your existing code. The good news is there's an update guide, which you'll find below the fold in this blog post.
Please visit our Google Group to register comments or complaints, our project web site for full documentation and FAQs, and feel free to clone or fork our GitHub repo.
(Why 0.6.3 and not 0.6.0? Because we had to work through some glitches in the new deploy process with GitHub and Jeweler and whatnot. We're only human...)
AutoTagger is a gem that helps you automatically create a date-stamped tag for each stage of your deployment, and deploy from the last tag from the previous environment.
Let's say you have the following workflow:
- Run all test on a Continuous Integration (CI) server
- Deploy to a staging server
- Deploy to a production server
You can use the autotag command to tag releases on your CI box, then use the capistrano tasks to auto-tag each release.
Interesting Things
When you specify a gem from a custom source, and it has dependencies on a separate source, you need to list both sources in geminstaller.yml.
This comes up when you are installing a gem from github and that gem depends on other gems from rubyforge. You can specify multiple sources by adding more --source attributes.
