Ruby on Rails: Rack 1.0.0 not ~> Rack 1.0.1 Caused by Stranded Rack Gem

no comments

The error message

/public/../config/../vendor/rails/railties/lib/initializer.rb:271:in `require_frameworks': RubyGem version error: rack(1.0.0 not ~> 1.0.1) (RuntimeError)
	from /public/../config/../vendor/rails/railties/lib/initializer.rb:134:in `process'
	from /public/../config/../vendor/rails/railties/lib/initializer.rb:113:in `send'
	from /public/../config/../vendor/rails/railties/lib/initializer.rb:113:in `run'
	from /public/../config/environment.rb:9
	from /public/dispatch.fcgi:21:in `require'
	from /public/dispatch.fcgi:21

Old news?

If you have this problem, odds are you’ve been pouring over Google and you’re here because you have not resolved (comment #7) your problem. I’ve seen the “solutions” out there and they all claim: “Remove rack 1.1.0. Install rack 1.0.1”. I tried it and it didn’t solve my problem. The trick is, my error message was different. Also, most others are having trouble with WEBrick. I was having issues with lighttpd. I tried everything: commenting out line 23 in action_controller.rb (bad idea, by the way) in my vendor/rails/actionpack/action_controller/lib/action_controller.rb. Added gem ‘rack’, ‘= 1.0.1’ in dispatch.fcgi (moved the error message, but didn’t fix it).

The solution

Manually remove the residual rack-1.0.0 installation.

I will assume you’ve already tried:

sudo gem uninstall rack
and specified all versions.

Do be careful!

sudo rm -rf /opt/local/lib/ruby/gems/1.8/gems/rack-1.0.0
sudo gem install rack --version "= 1.0.1"

What happened?

The problem was that rack-1.0.0 wasn’t uninstalled completely. RubyGems thought it was, but it was still being included in the code. Why? Because it existed here:

/opt/local/lib/ruby/gems/1.8/gems/rack-1.0.0
. It existed even though
wojno% gem list | rack
rack (1.0.1)
. WTF, right?

I don’t know how it was stranded there. I only use gem to add/remove gems, yet somehow it was left there to rot my brain while I’m under deadline.

Fun!

Keep in mind, I use MacPorts and keep it fairly updated.

Rails Security Article - Brainstorm for Brute Force?

no comments

NOTE: This is a disproven thought experiment: unable to prove guessed password succeeded and pass the information along.

Prompt

I’ve just finished reading (yes, the whole thing) an article about securing Rails applications. While it talks about security with Rails specifically in mind, it isn’t limited to just Rails applications. It is a good read for anyone who creates, maintains, or audits web applications of any flavor. You can read the Ruby on Rails Security Guide now or later if you like. I’ll explain as much as necessary to get my point across for this article.

One section in particular concerns Cross-Site Request Forgery attacks. According to the article, such attacks are carried out by placing HTML (yes, HTML) pointing to a specific action in a Rails application. The article used the example of using an image to delete critical information from a target site. The article continues to explain that one can use javascript to create a form that a user mistakenly clicks to use POST attacks instead of simple GET requests. (I have figured out a way to modify this so clicking is not required below)

This method of attack got me thinking. I had never specifically thought of doing this to attack my own sites. It makes sense though and naturally follows from the application of the semantics of the HTTP protocol. It also got me thinking of how one might use it to further exploit a site.

Is it worth it?

I believe it is. If using no more evidence that it exists as chapter 3 in this article of 8 regarding security measures, the practical result is that, unmitigated, your application’s security is based upon the security of other, well-traveled applications. The article continues to explain a simple fix as provided by Rails. The ONLY fix is the protect from forgery secret. I haven’t evaluated in depth exactly how effective it is, but I’m currently cooking up a new attack based on the CSRF vector.

What’s your Vector Victor?

Security is no joke. I believe I’ve come up with an attack using the CSRF vector that could potentially be devastating. There’s really no one to report this to as it applies to every site that is vulnerable to this type of attack. I believe it should be called the CSRF-MPDBF: The Cross-Site Request Forgery Massively Parallel Distributed Brute Force attack.

How does it work?

It uses the POST exploit as discussed in the article using javascript to commandeer the computers of any visitor to try a random password or two and report success to another site using the same CSRF exploit.

The Set-up (Huh? No really, how would that work?)

Let’s presume there are 2 sites: A and B. Site A is some website for which the attacker wants login credentials. The attacker may, or may not know the login name of a user. Having it certainly helps the attacker, but it’s not strictly necessary for this exercise. Site B is a forum with lose security policies that allows javascript code to be inserted into pages by users. Examples aren’t limited to forums, but a multitude of other “public service” sites.

Presume that Site A has a simple log-in form, with or without SSL; encryption is moot. Also presume that this site doesn’t use request forgery protection. It’s just a simple login for such as:

<form action="/security/login" method="POST" />
<p><label for="username">Username:</label><input name="username" type="text" /></p>
<p><label for="password">Password:</label><input name="password" type="password" /></p>
<p><input type="submit" value="Login" />
</form>

Site A will look at the username and password in the POST body and do a look up of the username, then see if the password matches. I’m not talking about SQL injection here. Presume it’s a Rails application and it’s using the sanitize_sql functions correctly. If the login is successful, the URL will change (a redirection will occur), otherwise, presume the application displays the same message (same URL) without redirecting. Most applications redirect after login. But it is possible to confirm login another way. This is just one example. The key point is that an attacker is able to reliably confirm a successful login (and conversely, an unsuccessful one).

Are we there yet?

Sorry, let’s get to some steps of the attack:

The attack code

The first step is for the attacker to create special javascript code as in the above article with one key difference: target the login script and include a username and GUESSED password.

Say what? A GUESSED password?

Yes, guessed. I told you that this was a brute force attack. Javascript includes a random number generator:

Math.random()
So why not use it to generate a random password?

But that will never work!

Oh really? Why not? Every javascript engine is different. No browser and no visit to the page will likely use the same password. If you post some comment to a popular site and get a lot of people to trigger this, the target site A could be compromised in a matter of days, if not hours.

Dude, where’s my code?

FIIINE! This javascript is inserted on site B to attack site A (modified from the article: “On Rails Security”

<a href="http://www.b.com/" onmouseover="
  var f = document.createElement('form');
  function passwordGuess() { /* ... details here ... */ }
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.a.com/account/destroy';
  var x = document.createElement('input'));
  f.appendChild(x);
  x.setAttribute('name','username');
  x.setAttribute('value','TARGETUSER');
  x = document.createElement('input'));
  f.appendChild(x);
  x.setAttribute('name','password');
  x.setAttribute('value',passwordGuess());
  f.submit();
  return false;">To the harmless survey</a>

Here’s how it works. I added a stub function for the password guessing routine as that is not what is at issue here. This code is to be injected into a forum post or SQL injection or whatever vector such that it appears to public users of site B. When the javascript is called, on click, it creates a form in HTML. This is a bit strange. Javascript modifies the DOM to add a form. The form is then submitted as though the user had submitted a form from site B. But server A doesn’t know the form exists on site B and processes the username and password as though the request came from site A. This, my friends, is effectively a login attempt. The user is completely unaware that anything occurred. It’s executed when the hover over the link and when they click, the link works as desired. This is different from the original article as it worked on click. The unwitting user at site B cannot detect that this attack has occurred unless he or she inspects the traffic. What normal user does that?

2010/02/01 – Editor’s Note: I’m trying to get this to work, I cannot seem to get the status of a password guess using Javascript. This might be a hair-brained idea after all.

OK, but the attacker can’t get that random password!

Not true. Use the same attack vector. Presume site B or some other site C is vulnerable to the same type of attack. To deliver the credentials back to the attacker simply modify the javascript to detect if the page was redirected. Once the credentials are verified, post the brute forced credentials to a public location. An attacker could even use the same type of attack to post those results. The attacker simply checks the destination site for updates and snarfs the credentials. Then the real fun begins and I don’t have to explain what can happen at this point.

Impact?

Well, think of it in simple terms. Every browser visiting site B’s compromised page becomes a weapon. Each person that goes to this page on site B with the code contributes to the attack. Assuming site B is very popular, but lax on code insertion filtering, site A will be attacked x times, where x is the number of users that trigger the attack on site B. If the attack runs m times (multiple attempts), then site A will be attacked x*m times. If B is a popular site, then x can easily approach millions of triggers in a period of a few weeks. Assuming a small dictionary can be loaded, a clever attacker could author the script to try word combinations, or insert symbols between, before, and after words, try numbers, etc. If the username is known in advance, it’s only a matter of time before that account on site A is compromised by visitors on site B. If multiple sites with B’s lax security exist, then the attack is: m*(x1 + x2 + …xn) where x is set sites like B to which the malicious code is posted.

It’s sort of like creating a bot network that only exists when users go to a specific page or pages.

But it’s a random password, won’t the same password be tried again and again?

Yes. There will, undoubtedly, be collisions in the the attack space. The key is that, depending on the javascript random engine, there will be significantly greater quantities of untried attacks. Even pseudo-random number generators try to be difficult to predict. In this case, this randomness works in favor of the attack by created more unique generations. A super-sophistocated attack could post failed results to a forum. These results could then be compiled to a random number black list that will skip over previously failed trials. That would significantly reduce the collisions (not eliminate) but is more sophisticated and again, if the PRNG has a decent algorithm, won’t be necessary.

Proof of concept?

Ha, no way. I would not write one in the wild.

Has anyone else thought of this?

I suppose I should have looked this up before I started writing. However, writing down an idea helps solidify the thought. I didn’t find anything with a quick Google Search but that isn’t thorough research. Most of the hits involve the tokens getting brute forced. My example assumes there are no tokens. But you can see how even brute forcing the tokens leads to slowing the progress of an attack.

Why is this so bad?

Because there’s no one IP source to block. It’s distributed and not easily detectable. Couple this with a password dictionary somewhere and you have a serious attack. Couple it further without the need to click something to trigger the attack (below) and it’s even more wide-spread.

No Clicking Required

The article gives the example of javascript code that creates a form, then replaces a link a user would normally click with the form such that it performs the CSRF attack. It is possible to create an exploit using the same vector but not require a click to activate it. Simply create a form as indicated in the article with my modifications, then write a script to periodically execute it asynchronously and without redirecting the user to the login page. This hides the attack and permits multiple guesses; as many as can be squeezed into the time the user is on the B site.

How bad is it?

I would imagine most of the big sites already mitigate this attack using forgery tokens. While no security expert, I believe that Facebook is using them based on their login form. I’ve removed some of the worthless table formatting, added line breaks, and only used the HTML form fields. For some reason, the field is listed as having the id=“lsd”. I have no idea why.

<form method="POST" action="https://login.facebook.com/login.php?login_attempt=1" id="login_form">
<input type="hidden" name="charset_test" value="&euro;,&acute;,€,´,水,Д,Є" />
<input type="hidden" id="locale" name="locale" value="en_US" autocomplete="off" />
<input type="hidden" id="non_com_login" name="non_com_login" autocomplete="off" />
[...]
<input type="checkbox" class="inputcheckbox " id="persistent" name="persistent" value="1" />
[...]
<input type="text" class="inputtext" title="Email" placeholder="Email" id="email" name="email" value="" />
<input type="password" class="inputpassword" id="pass" name="pass" value="" />
<input type="text" class="inputtext hidden_elem DOMControl_placeholder" id="pass_placeholder" name="pass_placeholder" value="" />
<input value="Login" type="submit" class="UIButton_Text" />
<input type="hidden" name="charset_test" value="&euro;,&acute;,€,´,水,Д,Є" />
<input type="hidden" id="lsd" name="lsd" value="KboH0" autocomplete="off" />
</form>

The 5 characters seem to change from browser to browser. It appears to be generated, then persisted using cookies (cookie name: lsd).

The real problem will be with smaller sites that are written with custom code that do not use the forgery prevention tokens. I cannot even begin to guess how many sites are affected.

“And what was the real lesson? Don’t leave things in the fridge.”

Spike may have been onto something there (quote is from Cowboy Bebop, “Toys in the Attic”). Don’t get soft on your security. As you can see, we’re all in this together. Your weakness is someone else’s exploit vector. You won’t just become a statistic, you’ll be actively, albeit unwittingly, contributing to an attack on other sites at the discretion of less scrupled persons.

Read the article! Don’t leave your applications in security cold-storage. It’s important.

Disclaimer

Again, this is an informational article to help site programmers build more secure sites. I am not responsible for people that misuse these ideas or examples.

Ruby Update Breaking old Rails Apps

no comments

Background

Lately, I’ve been updating my Mac and Linux systems (AS I SHOULD!) and I’ve been getting errors for the passed few months from my Rails-based applications. They’ll be 500 errors (server error).

The Problem

It seems that a Ruby1.9 (from Ruby1.8) update has added the function “chars” to the core String class. Unfortunately, Rails prior to version 2 (I have not had this issue with newer, Rails 2 projects, I can only assume it’s not an issue: consider it unconfirmed) uses this method/attribute in a Rails ActiveSupport extension that allows you to slice up strings in an easier way.

Specifically, the ActiveSupport library assumes that the String class does NOT define the “chars” function. However, with Ruby1.9, it does. This leads to a conflict of types. Rails wants to use an attribute (as defined with ActiveSupport), Ruby1.9 is offering a method. The method wins and Ruby thinks the result of “chars” is an enumerator when it’s really expecting a String. Under Ruby 1.8, String included Enumerable as a mixin module. In 1.9, String no longer includes this.

The Rails team saw a need for the method chars and created one in activesupport/lib/active_support/core_ext/string/unicode.rb, then the Ruby core team added their own to the string class. Thus the conflict and thus the error.

The Solution

I picked this up from various websites reporting the same issue. They’re just not easy to find. You need to insert this code into the config/boot.rb file in the affected application. I put it at the top.

# Fix for ./script/../config/../config/../vendor/
#rails/activerecord/lib/../../activesupport/lib/
#active_support/core_ext/string/access.rb:43:in
# `first':NoMethodError: undefined method `[]' for
##<Enumerable::Enumerator:0x103767ec0>
unless '1.9'.respond_to?(:force_encoding) 
String.class_eval do 
    begin 
      remove_method :chars 
    rescue NameError 
      # OK 
    end 
  end 
end 
# /Fix

It effectively removes the function “chars” from the String class that is added in from Ruby1.9. Hopefully, the Rails equivalent is the same and as or more secure.. But only does this if the String class has the method “force_encoding”. Why force_encoding? because it appeared in 1.9 along with “chars.” That’s the only link. So, should they remove “force_encoding” later, this fix won’t work any more.

Suggestions

Upgrade Rails to 2.0+ That should fix you up. Or avoid Ruby 1.9… I don’t recommend that though. They’ve isolated some of the core functionality into libraries. Ruby should have less of a memory footprint. I’d imagine there are security fixes .

It is also unfortunate that this situation occurred. I’m not sure how it can be avoided in the future. Things get updated. A little warning about them would be nice, however I’m actually amazed it hasn’t happened already. Kudos to the Rails/Ruby team.

Array.valN: Working smarter with Rails, with Ruby

no comments

Rails has a powerful form builder mechanism. However, if you want to make a list of things and have the data flow back to you without involving ActiveRecord, you’re in for some hurt; well, you were, unless you decide to use this module. Warning: this article is moderately advanced. I’m assuming you’re familiar with ActionControllers and the ActionView in my examples. If you’re familiar with how to use the basics of Rails, you should probably only read the first 1/2. Read the first code block to see what my code can do. But to see why this code is useful, read on to the examples.

Say I have a set of fox phrases. Now, if a certain fox is to have a list of catch phrases, and you don’t want to store them in a database, you can’t use Rails’ form helpers (text_field, hidden_field, etc.) and the params[] calls without a good deal of ugly, hack code (creating spoof instance variables to display data and creating a fake active record objects to receive that data from a form). To help beautify code, I created an additional way of accessing (setting and getting) elements in an array (valN). Behold the ArrayIterAccessors:

>> @catch_phrases = ['Addiction is like Pokemon!','Hey! There goes my pickup!','Chucky Bacon!']
=> ["Addiction is like Pokemon!", "Hey! There goes my pickup!", "Chucky Bacon!"]
>> @catch_phrases.val2
=> "Chucky Bacon!"
>> @catch_phrases.val2 = 'Chunky Bacon! Chunky Bacon! Chunky Bacon!'
=> "Chunky Bacon! Chunky Bacon! Chunky Bacon!"
>> @catch_phrases
=> ["Addiction is like Pokemon!", "Hey! There goes my pickup!", "Chunky Bacon! Chunky Bacon! Chunky Bacon!"]

“Now,” you say, “why the heck is he not just using the []!?” Well, I say, Try doing that within ActionView (rhtml template) for a text field:

<%= text_field 'catch_phrases', '[0]' %>

That will fail horribly. Depending on what you set @catch_phrases to, you’ll get:

undefined method `[0]' for ['Addiction is like Pokemon!','Hey! There goes my pickup!','Chucky Bacon!']:Array

And that doesn’t get you anywhere. So, supposin’ you say, oh, I dunno:

# My special module (should be a plugin but for demonstration
# purposes, I'm defining it inline
# Skip this code for now
class CatchPhrasesController < ApplicationController
  module ArrayIterAccessors
  alias :old_method_missing :method_missing
  def method_missing( sym, *args, &block )
  sym_s = sym.to_s; t = sym_s[/\d+/]
    if t and sym_s[/\Aval\d+=?\Z/]
      if sym_s[/=\Z/]
        self[t.to_i] = args.first; return self[t.to_i]
      else
        return self[t.to_i]
      end
    else
      return old_method_missing( sym, *args, &block )
    end
  end
end
  # this is an action for your CatchPhrases Controller URL:
  # http://site.example.com/catch_phrases/new
  def new
    Array.send( :include, ArrayIterAccessors )
    @catch_phrases = ['Addiction is like Pokemon!','Hey! There goes my pickup!','Chucky Bacon!']
  end
end
### in your view: new.rhtml ###
<% @catch_phrases.each_with_index do |item,index| %>
<%= text_field 'catch_phrases', "val#{index}" %>
<% end %>

So when your view renders, you’ll see 3 text fields, each with the catch phrase pre-filled (so you’ll have an addiction catch phrase, a pickup catch phrase, and a chunky bacon catch phrase). You can edit them and when you submit the form, you can reconstitute your array by:

@catch_phrases = params[:catch_phrases].keys.collect{|k| k.to_s[/\d+/] }.sort.collect{|k| params[:catch_phrases][('val'+k.to_s).to_sym] }

If you didn’t change the catch phrases, you’ll get the exact array: @catch_phrases, back. And if you DID change the phrases, you’ll get an array, in order, of the altered catch phrases!

Pitfalls

  1. No ActiveRecord (well, I claimed that as a plus above) means: no validation. That’s up to you, unless you include a validatable mixin (there are a few of those). This also means there is no way of reporting errors back automatically. I suppose I could write something later to do this.
  2. That last line is ugly and slow.
  3. The use example is for demonstration purposes and is awful: DO NOT USE IT. That will mix the module in EVERY time you render the “new” action. To do this correctly take the module and the
    Array.send :include, ArrayIterAccessors
    code out of your controller and:
    1. Put it in a plugin OR
    2. Create a new file in lib/array_iter_accessors.rb (in your Rails project) and put the module into that. Then, in your environment.rb file (at the bottom), put the
      Array.send :include, ArrayIterAccessors
      code. That way, the mixin will only be done once.

What I did

I overrided the method_missing method for the Array class (then mixed it in). I scan for any method calls for methods beginning with “val” and ending in a number. I also parse for an ‘=’ in case you’d like to use it for assignment too. Then, I call the array’s [] operator to access the values at the desired position. And because I’m using the array’s built-in operator, you’ll see all the error messages and behavior you’re used to seeing without my module.

Why val?

Because it’s two letters shorter than “value.” What? You expected a profound reason? Sorry if this precludes your ability to keep track of your girlfriends.

You have enough here to write your own plug-in, or just to use it when you need it. Happy riding.

Session of Fear

2 comments

Now, before you think “FUD Time!”, I preface this post with the following warning: Yes, this is a problem with not just Rails but all sessions; no, it’s not unsolvable. So, in light of the bad news, there is much good news.

The Problem

Rails applications are very useful. However, most require sessions in order to be useful. Sure, you can provide an ActionWebService or REST-based service to not use sessions, but for the majority of user services, you’re stuck with sessions.

A Session About Sessions

(Go ahead and skip this part if you’re already familiar with how sessions work)

A session is a way a server remembers who you are (in case you didn’t know). The most common way of allowing a server to ID you, without knowing who you are exactly, is by sharing a secret! Well, it’s supposed to be secret and we’ll come to that in a bit. When a new session is created, your session ID is generated (it’s random, hopefully) and saved in the server. The id can be associated with other data, such as your name, or permissions. When you request a page, the server will give you this ID. You almost never see that ID because it’s sent in the background as a “cookie.” You’ve probably heard of these before. From now on, every time you request a page from the server, your cookie data is automatically included in the request (your web browser takes care of this) so the server can figure out who you are. Done correctly, no personally identifiable information is sent over the Web. Just your (hopefully) random ID.

Sounds good so far. But if you’re not using HTTPS (SSL/TSL encryption), your secret contained in that cookie can be seen when it is first sent, and when you access any page thereafter (because you’re sending it each time remember). So, if someone has access to your traffic (if you’re using a shared WiFi or a hub or have a shady ISP), they can pull out your secret. Here’s an old session I sniffed using a program called snort:

GET /javascripts/prototype.js?1186461045 HTTP/1.1..
Host: christopher.wojno.com..
User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.1.6) Gecko/20070810 Firefox/2.0.0.6..
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5..Accept-Language: en-us,en;q=0.5..
Accept-Encoding: gzip,deflate..
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7..
Keep-Alive: 300..
Connection: keep-alive..
Cookie: _session_id=db392fa5b39125fa7c1e581b9c1ec71d; is_admin=yes....

There it is, the last line. “Cookie.” So when my browser was trying to download some javascript libraries (Prototype is quite good), it sent my session ID. Now, the javascript doesn’t care about my session ID, but it was sent any way. If someone were to see that sent over the Internet (at any point on it’s way to the server), he or she could create a fake cookie with that ID and login as me without my password! (don’t try it, I’ve already logged out and reset the session) They could then, go into the settings and lock me out of my own system (until I change the password in the ruby console… that’ll show ’em?). In the meantime, the damage has been done and it can happen again.

SSL!

I won’t go into SSL, but that little piece of technology has enabled credit card purchases over the Internet for years and will hopefully continue to do so for years to come. Any way, in short, it’s encryption and it will encrypt cookies too!

So, we need to force rails to encrypt cookies. Not as simple as it sounds. First, you need to setup an SSL certificate on your server. Easier said than done, though, some Apache packages come with the tools to automatically generate one. For a confusing, yet interesting read, see X509 on Wikipedia.

I assume you installed your SSL certificate and have configured Apache1 to run with SSL (or lighty, if you’re using that instead (mongrel is not mentioned as it does not have SSL, though there is a way to use SSL and mongrel together)). If not, there are loads of tutorials. I warn you though, it’s fairly technical.

Hold onto your Cookies!

So, you have SSL and a Rails application running (I’m assuming). How do you tell Rails to make sure your cookies are only sent via SSL? Rails lets you specify it.

Tell Rails to use SSL Only

By default, Rails does NOT enforce SSL on cookies or sessions (that would be frustrating for development, wouldn’t it?). So you have to enable that enforcement yourself. If you want your session cookie to be sent over SSL site-wide (and generally, you do!), head into your rails application directory and open (in your favorite editor) app/controllers/application.rb Add the following anywhere in the ApplicationController class:

class ApplicationController < ActionController::Base
  session :session_key => '_session_id', :session_secure => true
end

The ApplicationController class definition should already exist, don’t duplicate it. Also, make sure that session isn’t already specified. If it is, the important part here is “:session_secure => true”. Rails will now tell the browser to only send the session cookie if the browser is using the https (SSL) protocol. This feature is poorly documented but hopefully this will help keep your applications that you write, safe. NOTE: You MUST use SSL if you enable this or your application will become extremely forgetful (Who are you? What are you doing in my kitchen?!).

If you’re interested in storing OTHER data (not overly recommended though due to this and another exploit) in other cookies, Rails offers cookie—manipulation (not really management, the API leaves something to be desired). You can tell individual cookies to only be sent over encrypted connections, just like the session cookie. The other exploit: if the computer is shared and the browser doesn’t clear out the cookies, the next person to sit at the computer can harvest the cookie information, so don’t store passwords, e-mail addresses… really anything in cookies, it’s just a bad idea. DO store worthless information you don’t want to save in your server, such as squirrel preferences.

Admittedly, generally the odds of someone having access to your network traffic (and your cookies, no! MY cookies!) directly is moderate. For the attacker to successfully pick out your cookie data from the slew of traffic wizzing by, he/she would have to be looking and looking for a specific cookie name. So make sure no one has it out for you.

I don’t mean to sound down on the Rails team. Dang-fine-job I say. Cookies aren’t really important and the session is cookie-based, so session security falls by the way-side. It’s up to all developers to keep his or her eyes open for potential pitfalls.

1_000.thank( Rails::DevTeam.members.collect{|m|m.email} )

1 Note: Apache doesn’t know how to run Ruby code as of this writing. You need to use an Apache’s mod_proxy. This will (after it’s been configured) then pass the requests from Apache, to mongrel, lighty, or… WEBrick (if you’re nuts)

One-line Plurals

1 comment

Problems with Plurals

Trevor brought up a good point in my last Rails post concerning plurals.

So I decided to enlighten him/create something new.

Rails offers a mixin for Action View called (oddly enough) pluralize. It’s fairly simple, but unnecessarily limited. Here’s some syntax:

>> pluralize(1, 'person')
=> 1 person
>> pluralize(3, 'person')
=> 3 people

It’s annoying if you don’t want the number or are trying to construct complicated sentences. The other big problem is that it doesn’t work outside of an ActionView. Luckily, there’s pluralize So I complied with Trevor’s request but used the pluralize function:

>> module SmartPlural
>> def plural( count=0 )
>> return ( count != 1 ? self.pluralize : self.dup )
>> end
>> end
=> nil
>> String.send :include, SmartPlural
=> String
>> "Trevor".plural( 3 )
=> "Trevors"
>> "Trevor".plural( 1 )
=> "Trevor"

Sorry for the lack of a ! (bang) version. That requires writing C-code… and that means writing the pluralize function again… and I’m not doing that. Yes, you can call me lazy.

What happened here?

For those who are curious as to what I did: I created a module called SmartPlural, the name doesn’t really matter. After that, I created a new function called “plural” that will pluralize the string if the number is not 1 (0 apples, 1 apple, 2 apples, etc.)1. Finally, I install the new module into the String class as a mixin. Then I tested it on my friend here.

Why didn’t you do: (x.length == 1 ? ‘foo’ : ‘foos’)

Well, what if I don’t like foos (bad Mr. T joke here, I won’t suffer it upon you though)? Say I want to talk about octopus:

>> 'octopus'.pluralize
=> "octopi"

Rails already knows about quite a few irregular words and you don’t want to pluralize every word you’ll be pluralizing yourself; that’s just stupid and not DRY. Use the pluralize function that comes with Rails. Here’s yet another reason to do so. What if I like to juggle baby geese, you know, goslings? As in the previous example, octopus is a special word, it’s plural isn’t simply the original followed by an s. If I have:

>> 'goose'.pluralize
=> "gooses"

Rails is wrong. It doesn’t know about geese or goose. Now, I don’t expect the Rails team to think of every word, and neither did they. You can tell Rails how to plualize those special cases and it will be effective everywhere, now that’s DRY.

>> Inflector.inflections do |inflection|
>> inflection.irregular( 'goose', 'geese' )
>> end
=> [[/(g)eese$/i, "\\1oose"],... <snipped>
>> "goose".plural( 3 ) # make sure you have SmartPlural included in String
=> "geese"

Not all

One more thing: the default value out of String.plural is, well, the plural form:

>> "Trevor".plural
=> "Trevors"

So if you’re just interested in the plural form, and don’t care about numbers, you don’t have to use String.pluralize. Just a little bonus, a very small one.

The One-Liner

To use this code, just type (or preferably, paste):

module SmartPlural; def plural(count=0); return (count != 1 ? self.pluralize : self.dup ); end; end; String.send :include, SmartPlural

1 I like apples…

Easy And/Or in Ruby on Rails

2 comments

This missing feature of rails has really bugged me, but it’s so useful.

If you have a list of words such as: apples, oranges, and bananas as an array:

>> list = ['apples','oranges','bananas']
=> ["apples", "oranges", "bananas"]

You’d like to be able to have a variable length list and still have it look correct in the view. So a smaller list:

>> list = ['oranges','bananas']
=> ["oranges", "bananas"]

Should look like: “oranges and bananas”.

>> list = ['apples','oranges','bananas']
=> ["apples", "oranges", "bananas"]
>> and_or_list 'and', list
=> "apples, oranges, and bananas"
>> list.pop
=> "bananas"
>> and_or_list 'and', list
=> "apples and oranges"
>> list.pop
=> "oranges"
>> and_or_list 'and', list
=> "apples"

The following block of code will do just that:

  def and_or_list( andor, list )
	list = list.dup
	comma = (list.size > 2 ? ',' : '')
	list2 = list.pop if list.size > 1
	s = list.join(', ')
	s << comma+' '+andor+' ' + list2 if list2
	s
  end

Vioa!

Instant and easy listing of various things, in English. Your users will never know it’s generated.

I’ve wrapped it up in a neat little plug-in for you. Just install it in your vendor/plugins directory. It will automatically be available in your views.

AndOrList Module