Cross Site Scripting in Rails 5

Steps

Step 1

Add has_many :comments to Task model.

class Task < ApplicationRecord
  belongs_to :project
  has_many :comments  
end

The comment belongs to a task.

class Comment < ApplicationRecord
  belongs_to :task
end

Step 2

Generate the comment model with content field for text area.

rails g model comment content:text 

Step 3

Add the foreign key t.integer :task_id to the generated migration file.

class CreateComments < ActiveRecord::Migration[5.0]
  def change
    create_table :comments do |t|
      t.text :content
      t.integer :task_id

      t.timestamps
    end
  end
end

Migrate the database.

rails db:migrate

Note: You should always define a database index for all foreign keys. I am skipping it here.

Step 4

The comment model has content column for displaying text area. Add comments section to the task show page.

<h2>Comments</h2>
<% for comment in @comments %>
<p><%= comment.content %></p>
<% end %>

Add the form to comment on a task displayed on the show page.

<%= form_for(Comment.new, url: task_comments_path(@task)) do |f| %>
<p> <%= f.text_area :content, rows: 7 %> </p>
<p> <%= submit_tag 'Add Comment' %> </p>
<% end %>

The app/views/tasks/show.html.erb looks like this:

<p>
  <strong>Name:</strong>
  <%= @task.name %>
</p>
<p>
  <strong>Complete:</strong>
  <%= @task.complete %>
</p>

<h2>Comments</h2>
<% for comment in @comments %>
<p><%= comment.content %></p>
<% end %>

<%= form_for(Comment.new, url: task_comments_path(@task)) do |f| %>
<p> <%= f.text_area :content, rows: 7 %> </p>
<p> <%= submit_tag 'Add Comment' %> </p>
<% end %>

<%= link_to 'Edit', edit_task_path(@task) %> |
<%= link_to 'Back', tasks_path %>

Task show page now displays all comments and has a text area for adding a form.

Step 5

Generate the commments controller with create action.

rails g controller comments create

The comments controller create action looks like this:

class CommentsController < ApplicationController
  def create
    task = Task.find(params[:task_id])
    task.comments.create(params.require(:comment).permit(:content))

    redirect_to task
  end
end

We create comment for the task model by using the has_many :comments association. Rails automatically populates the task_id foreign key in the new comment record. This is also secure way of creating a nested model.

Step 6

Tasks controller show method looks like this:

def show
  task = Task.find(params[:id])
  @comments = task.comments
end

Step 7

Define the nested routes for comments in routes.rb.

resources :tasks do
  resources :comments, only: [:create]
end

You can now add comments.

Step 8

In the task show page, add:

Testing <script> alert('test') </script>

to the comments form. This is now automatically sanitized by Rails 5. There is no need to use the h view helper anymore.

Step 9

We can escape the user input in the rails console.

 > CGI::escapeHTML("<script> alert(document.cookie) </script>")
 => "&lt;script&gt; alert(document.cookie) &lt;/script&gt;" 

We see the escaped output.

Summary

In this article, you learned how Cross Site Scripting is handled in Rails 5.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.