Convert file indentation to spaces

This commit is contained in:
James Espinosa
2014-07-05 20:17:27 -05:00
parent 68e6a01743
commit 7e4fad462b
88 changed files with 2915 additions and 2999 deletions
-2
View File
@@ -1,5 +1,4 @@
class AdminController < ApplicationController
before_filter :administrative, :if => :admin_param
skip_before_filter :has_info
@@ -67,5 +66,4 @@ class AdminController < ApplicationController
def admin_param
params[:admin_id] != '1'
end
end
@@ -1,5 +1,4 @@
class Api::V1::MobileController < ApplicationController
skip_before_filter :authenticated
before_filter :mobile_request?
@@ -30,5 +29,4 @@ class Api::V1::MobileController < ApplicationController
request.user_agent =~ /ios|android/i
end
end
end
+43 -46
View File
@@ -1,57 +1,54 @@
class Api::V1::UsersController < ApplicationController
skip_before_filter :authenticated
before_filter :valid_api_token
before_filter :extrapolate_user
skip_before_filter :authenticated
before_filter :valid_api_token
before_filter :extrapolate_user
respond_to :json
respond_to :json
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
def show
respond_with @user.as_json
end
respond_with @user.admin ? User.all : @user
end
private
def show
respond_with @user.as_json
end
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
private
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /(.*?)-(.*)/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def check_hash(id, hash)
digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")
hash == digest
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /(.*?)-(.*)/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
def check_hash(id, hash)
digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
@clean_token = CGI::unescape(token)
end
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
@user = User.find_by_id(@clean_token.split("-").first)
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
@clean_token = CGI::unescape(token)
end
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
@user = User.find_by_id(@clean_token.split("-").first)
end
end
@@ -1,5 +1,4 @@
class ApplicationController < ActionController::Base
before_filter :authenticated, :has_info, :create_analytic
helper_method :current_user, :is_admin?, :sanitize_font
@@ -53,5 +52,4 @@ class ApplicationController < ActionController::Base
css
# css if css.match(/\A[0-9]+([\%]|pt)\z/)
end
end
@@ -4,7 +4,6 @@ class BenefitFormsController < ApplicationController
@benefits = Benefits.new
end
def download
begin
path = params[:name]
@@ -26,7 +25,6 @@ class BenefitFormsController < ApplicationController
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
=begin
# More secure version
def download
@@ -47,6 +45,4 @@ class BenefitFormsController < ApplicationController
end
end
=end
end
-2
View File
@@ -1,5 +1,4 @@
class DashboardController < ApplicationController
skip_before_filter :has_info
def home
@@ -10,5 +9,4 @@ class DashboardController < ApplicationController
cookies[:font] = params[:font]
end
end
end
-1
View File
@@ -33,5 +33,4 @@ class MessagesController < ApplicationController
end
end
end
end
@@ -1,7 +1,6 @@
class PasswordResetsController < ApplicationController
skip_before_filter :authenticated
def reset_password
user = Marshal.load(Base64.decode64(params[:user])) unless params[:user].nil?
-1
View File
@@ -39,5 +39,4 @@ class PayController < ApplicationController
format.json {render :json => {:account_num => decrypted || "No Data" }}
end
end
end
@@ -3,5 +3,4 @@ class PerformanceController < ApplicationController
def index
@perf = current_user.performance
end
end
-1
View File
@@ -3,5 +3,4 @@ class RetirementController < ApplicationController
def index
@info = current_user.retirement
end
end
+1 -1
View File
@@ -1,4 +1,5 @@
class ScheduleController < ApplicationController
def create
message = false
@@ -55,5 +56,4 @@ class ScheduleController < ApplicationController
end
return vals
end
end
+19 -21
View File
@@ -1,35 +1,34 @@
class SessionsController < ApplicationController
skip_before_filter :has_info
skip_before_filter :authenticated, :only => [:new, :create]
def new
@url = params[:url]
redirect_to home_dashboard_index_path if current_user
@url = params[:url]
redirect_to home_dashboard_index_path if current_user
end
def create
path = params[:url].present? ? params[:url] : home_dashboard_index_path
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
path = params[:url].present? ? params[:url] : home_dashboard_index_path
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
rescue Exception => e
end
end
if user
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token if User.where(:user_id => user.user_id).exists?
else
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
end
redirect_to path
if user
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token if User.where(:user_id => user.user_id).exists?
else
# Removed this code, just doesn't seem specific enough!
# flash[:error] = "Either your username and password is incorrect"
flash[:error] = e.message
render "new"
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
end
redirect_to path
else
# Removed this code, just doesn't seem specific enough!
# flash[:error] = "Either your username and password is incorrect"
flash[:error] = e.message
render "new"
end
end
def destroy
@@ -37,5 +36,4 @@ class SessionsController < ApplicationController
reset_session
redirect_to root_path
end
end
+19 -21
View File
@@ -1,5 +1,4 @@
class TutorialsController < ApplicationController
skip_before_filter :has_info
skip_before_filter :authenticated
@@ -18,16 +17,16 @@ class TutorialsController < ApplicationController
end
def xss
@code = %{
<li style="color: #FFFFFF">
<!--
I'm going to use HTML safe because we had some weird stuff
going on with funny chars and jquery, plus it says safe so I'm guessing
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
}
@code = %{
<li style="color: #FFFFFF">
<!--
I'm going to use HTML safe because we had some weird stuff
going on with funny chars and jquery, plus it says safe so I'm guessing
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
}
end
def broken_auth
@@ -45,15 +44,15 @@ class TutorialsController < ApplicationController
event.preventDefault();
$.ajax(\{
url: "/example",
data: valuesToSubmit,
type: "POST",
success: function(response) \{
alert('success!');
},
error: function(event) \{
alert('failure!');
\}
\});
data: valuesToSubmit,
type: "POST",
success: function(response) \{
alert('success!');
},
error: function(event) \{
alert('failure!');
\}
\});
\});
\} }
@@ -95,5 +94,4 @@ class TutorialsController < ApplicationController
def metaprogramming
end
end
-3
View File
@@ -1,9 +1,7 @@
class UsersController < ApplicationController
skip_before_filter :has_info
skip_before_filter :authenticated, :only => [:new, :create]
def new
@user = User.new
end
@@ -52,5 +50,4 @@ class UsersController < ApplicationController
redirect_to user_account_settings_path(:user_id => current_user.user_id)
end
end
end
-2
View File
@@ -1,5 +1,4 @@
class WorkInfoController < ApplicationController
def index
@user = User.find_by_user_id(params[:user_id])
if !(@user) || @user.admin
@@ -18,5 +17,4 @@ class WorkInfoController < ApplicationController
end
end
=end
end
+7 -7
View File
@@ -4,16 +4,16 @@ class Analytics < ActiveRecord::Base
scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}
def self.count_by_col(col)
calculate(:count, col)
calculate(:count, col)
end
def self.parse_field(field)
valid_fields = ["ip_address", "referrer", "user_agent"]
valid_fields = ["ip_address", "referrer", "user_agent"]
if valid_fields.include?(field)
field
else
"1"
end
if valid_fields.include?(field)
field
else
"1"
end
end
end
+23 -24
View File
@@ -1,20 +1,20 @@
class Benefits < ActiveRecord::Base
attr_accessor :backup
attr_accessor :backup
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "wb+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "wb+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
def self.make_backup(file, data_path, full_file_name)
def self.make_backup(file, data_path, full_file_name)
if File.exists?(full_file_name)
silence_streams(STDERR) { system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{file.original_filename}") }
end
end
end
=begin
def self.make_backup(file, data_path, full_file_name)
@@ -22,17 +22,16 @@ class Benefits < ActiveRecord::Base
end
=end
def self.silence_streams(*streams)
on_hold = streams.collect { |stream| stream.dup }
streams.each do |stream|
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
stream.sync = true
end
yield
ensure
streams.each_with_index do |stream, i|
stream.reopen(on_hold[i])
end
end
def self.silence_streams(*streams)
on_hold = streams.collect { |stream| stream.dup }
streams.each do |stream|
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
stream.sync = true
end
yield
ensure
streams.each_with_index do |stream, i|
stream.reopen(on_hold[i])
end
end
end
-1
View File
@@ -2,5 +2,4 @@ class KeyManagement < ActiveRecord::Base
attr_accessible :iv, :user_id
belongs_to :work_info
belongs_to :user
end
-1
View File
@@ -14,5 +14,4 @@ class PaidTimeOff < ActiveRecord::Base
def sick_days_taken_percentage
result = self.sick_days_taken.to_f / self.sick_days_earned.to_f * 100.0
end
end
-2
View File
@@ -1,5 +1,4 @@
class Pay < ActiveRecord::Base
# mass-assignable attributes
attr_accessible :bank_account_num, :bank_routing_num, :percent_of_deposit
@@ -21,5 +20,4 @@ class Pay < ActiveRecord::Base
def encrypt_bank_account_num
self.bank_account_num = Encryption.encrypt_sensitive_value(self.bank_account_num)
end
end
+12 -14
View File
@@ -1,7 +1,6 @@
require 'encryption'
class User < ActiveRecord::Base
attr_accessible :email, :admin, :first_name, :last_name, :user_id, :password, :password_confirmation
validates :password, :presence => true,
:confirmation => true,
@@ -49,18 +48,18 @@ class User < ActiveRecord::Base
end
=end
private
private
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
raise "#{email} doesn't exist!" if !(user)
if user.password == Digest::MD5.hexdigest(password)
auth = user
else
raise "Incorrect Password!"
end
return auth
auth = nil
user = find_by_email(email)
raise "#{email} doesn't exist!" if !(user)
if user.password == Digest::MD5.hexdigest(password)
auth = user
else
raise "Incorrect Password!"
end
return auth
end
=begin
@@ -76,11 +75,11 @@ private
=end
def assign_user_id
unless @skip_user_id_assign.present? || self.user_id.present?
unless @skip_user_id_assign.present? || self.user_id.present?
user = User.order("user_id").last
uid = user.user_id.to_i + 1 if user && user.user_id && !(User.exists?(:user_id => "#{user.user_id.to_i + 1}"))
self.user_id = uid.to_s if uid
end
end
end
def hash_password
@@ -96,5 +95,4 @@ private
self[column] = Encryption.encrypt_sensitive_value(self.user_id)
end while User.exists?(column => self[column])
end
end
+12 -14
View File
@@ -4,27 +4,26 @@ class WorkInfo < ActiveRecord::Base
has_one :key_management, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
#before_save :encrypt_ssn
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
"***-**-" << self.decrypt_ssn[-4,4]
end
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def key
@@ -40,5 +39,4 @@ class WorkInfo < ActiveRecord::Base
def cipher_type
'aes-256-cbc'
end
end
+39 -49
View File
@@ -1,70 +1,60 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
User information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
<p>
Something went wrong.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe071;"></span>
Manage Users
</div>
</div>
<div id="userDataTable" class="widget-body">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">Success!</h4>
<p>User information successfully updated.</p>
</div>
</div>
</div>
</div> <!-- End widget-body-->
</div> <!-- End widget header-->
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">Error!</h4>
<p>Something went wrong.</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe071;"></span>Manage Users
</div>
</div>
<div id="userDataTable" class="widget-body">
</div> <!-- End widget-body-->
</div> <!-- End widget header-->
</div>
</div>
</div>
</div>
<%= javascript_include_tag "jquery.dataTables.js"%>
<script type="text/javascript">
function dataTablePagination(){
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
};
function makeActive(){
$('li[id="admin"]').addClass('active');
$('li[id="admin"]').addClass('active');
};
function loadTable(){
$("#userDataTable").load("/admin/"+ <%= params[:admin_id] %> + "/get_all_users")
$("#userDataTable").load("/admin/"+ <%= params[:admin_id] %> + "/get_all_users")
};
$(document).ready(
makeActive,
dataTablePagination(),
loadTable()
makeActive,
dataTablePagination(),
loadTable()
);
</script>
+113 -121
View File
@@ -1,134 +1,126 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="main-container">
<div class="row-fluid">
<div class="row-fluid">
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</div>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</div>
</div>
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Dental Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Dental Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
<div>
<h2>Upload file</h2>
<%= form_for @benefits, :url => upload_path, :html => { :action => "upload", :multipart => true, :id => "fi" } do |f| %>
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
<div>
<div>
<%= hidden_field "benefits", "backup", :value => false %>
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="icon-plus icon-white"></i>
<span>Add file</span>
<%= f.file_field :upload %>
</span>
<button id="start_upload" type="submit" class="btn btn-primary start">
<i class="icon-upload icon-white"></i>
<span><%= t('fileupload.start_upload') %></span>
</button>
<br/><br/><span class="filename">Nothing selected</span>
</div>
<div class="span5">
<!-- The global progress bar -->
<div class="progress progress-success progress-striped active fade">
<div class="bar" style="width:0%;"></div>
</div>
</div>
</div>
<!-- The loading indicator is shown during image processing -->
<div class="fileupload-loading"></div>
<br>
<!-- The table listing the files available for upload/download -->
<table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody>
</table>
<% end %>
<div id="progress">
<div class="bar" style="width: 0%;"></div>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
<div>
<h2>Upload file</h2>
<%= form_for @benefits, :url => upload_path, :html => { :action => "upload", :multipart => true, :id => "fi" } do |f| %>
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
<div>
<div>
<%= hidden_field "benefits", "backup", :value => false %>
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="icon-plus icon-white"></i>
<span>Add file</span>
<%= f.file_field :upload %>
</span>
<button id="start_upload" type="submit" class="btn btn-primary start">
<i class="icon-upload icon-white"></i>
<span><%= t('fileupload.start_upload') %></span>
</button>
<br/><br/><span class="filename">Nothing selected</span>
</div>
<div class="span5">
<!-- The global progress bar -->
<div class="progress progress-success progress-striped active fade">
<div class="bar" style="width:0%;"></div>
</div>
</div>
</div>
<!-- The loading indicator is shown during image processing -->
<div class="fileupload-loading"></div>
<br>
<!-- The table listing the files available for upload/download -->
<table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody>
</table>
<% end %>
<div id="progress">
<div class="bar" style="width: 0%;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(function() {
$("#benefits_upload").change(function (){
var fileName = $(this).val();
$(".filename").html(fileName);
});
});
$(function() {
$("#benefits_upload").change(function (){
var fileName = $(this).val();
$(".filename").html(fileName);
});
});
function makeActive(){
$('li[id="benefit_forms"]').addClass('active');
};
$(document).ready(
makeActive
);
function makeActive(){
$('li[id="benefit_forms"]').addClass('active');
};
$(document).ready(makeActive);
</script>
+143 -148
View File
@@ -1,172 +1,167 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!--begin span12 -->
<% if @user.paid_time_off %>
<%= render :partial => "layouts/dashboard/dashboard_stats"%>
<% end %>
</div> <!-- end span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!--begin span12 -->
<% if @user.paid_time_off %>
<%= render :partial => "layouts/dashboard/dashboard_stats"%>
<% end %>
</div> <!-- end span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="home"]').addClass('active');
$('li[id="home"]').addClass('active');
};
function pieChartHome() {
$(function () {
//create instance
$('.chart1').easyPieChart({
animate: 2000,
barColor: '#e26666',
trackColor: '#dddddd',
scaleColor: '#e26666',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart1').data('easyPieChart').update(50);
}, 5000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(70);
}, 10000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(30);
}, 15000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(90);
}, 19000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(40);
}, 32000);
$(function () {
//create instance
$('.chart1').easyPieChart({
animate: 2000,
barColor: '#e26666',
trackColor: '#dddddd',
scaleColor: '#e26666',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart1').data('easyPieChart').update(50);
}, 5000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(70);
}, 10000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(30);
}, 15000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(90);
}, 19000);
setTimeout(function () {
$('.chart1').data('easyPieChart').update(40);
}, 32000);
});
$(function () {
//create instance
$('.chart2').easyPieChart({
animate: 2000,
barColor: '#b5799e',
trackColor: '#dddddd',
scaleColor: '#b5799e',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart2').data('easyPieChart').update(90);
}, 10000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(40);
}, 18000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(70);
}, 28000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(50);
}, 32000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(80);
}, 40000);
$(function () {
//create instance
$('.chart2').easyPieChart({
animate: 2000,
barColor: '#b5799e',
trackColor: '#dddddd',
scaleColor: '#b5799e',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart2').data('easyPieChart').update(90);
}, 10000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(40);
}, 18000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(70);
}, 28000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(50);
}, 32000);
setTimeout(function () {
$('.chart2').data('easyPieChart').update(80);
}, 40000);
});
$(function () {
//create instance
$('.chart3').easyPieChart({
animate: 2000,
barColor: '#579da9',
trackColor: '#dddddd',
scaleColor: '#579da9',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart3').data('easyPieChart').update(20);
}, 9000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(59);
}, 20000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(38);
}, 35000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(79);
}, 49000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(96);
}, 52000);
$(function () {
//create instance
$('.chart3').easyPieChart({
animate: 2000,
barColor: '#579da9',
trackColor: '#dddddd',
scaleColor: '#579da9',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart3').data('easyPieChart').update(20);
}, 9000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(59);
}, 20000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(38);
}, 35000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(79);
}, 49000);
setTimeout(function () {
$('.chart3').data('easyPieChart').update(96);
}, 52000);
});
$(function () {
//create instance
$('.chart4').easyPieChart({
animate: 2000,
barColor: '#dba26b',
trackColor: '#dddddd',
scaleColor: '#dba26b',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart4').data('easyPieChart').update(40);
}, 6000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(67);
}, 14000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(43);
}, 23000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(80);
}, 36000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(66);
}, 41000);
$(function () {
//create instance
$('.chart4').easyPieChart({
animate: 2000,
barColor: '#dba26b',
trackColor: '#dddddd',
scaleColor: '#dba26b',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart4').data('easyPieChart').update(40);
}, 6000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(67);
}, 14000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(43);
}, 23000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(80);
}, 36000);
setTimeout(function () {
$('.chart4').data('easyPieChart').update(66);
}, 41000);
});
$(function () {
//create instance
$('.chart5').easyPieChart({
animate: 3000,
barColor: '#1e825e',
trackColor: '#dddddd',
scaleColor: '#1e825e',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart5').data('easyPieChart').update(30);
}, 9000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(87);
}, 19000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(28);
}, 27000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(69);
}, 39000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(99);
}, 47000);
$(function () {
//create instance
$('.chart5').easyPieChart({
animate: 3000,
barColor: '#1e825e',
trackColor: '#dddddd',
scaleColor: '#1e825e',
size: 160,
lineWidth: 7,
});
//update instance after 5 sec
setTimeout(function () {
$('.chart5').data('easyPieChart').update(30);
}, 9000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(87);
}, 19000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(28);
}, 27000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(69);
}, 39000);
setTimeout(function () {
$('.chart5').data('easyPieChart').update(99);
}, 47000);
});
}
}
$(document).ready(
makeActive,
pieChartHome()
makeActive,
pieChartHome()
);
</script>
+5 -6
View File
@@ -18,16 +18,16 @@
</thead>
<tbody>
<% @analytics.each do |a|%>
<tr>
<tr>
<% a.attributes.each do |k,v| %>
<td><%= v %></td>
<% end %>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div class="clearfix">
</div>
</div>
@@ -36,11 +36,10 @@
<script type="text/javascript">
function dataTablePagination(){
$('#data-table').dataTable({
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
};
$(document).ready(dataTablePagination());
</script>
+18 -19
View File
@@ -18,25 +18,25 @@
</thead>
<tbody>
<% @users.each do |u|%>
<tr>
<td style="word-wrap:break-word;">
<%= "#{u.first_name} #{u.last_name}"%>
</td>
<td>
<%= u.email%>
</td>
<td>
<%= u.admin ? %{<span class="fs1" aria-hidden="true" data-icon="&#xe0fe;"}.html_safe : nil %>
</td>
<td>
<%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%>
</td>
</tr>
<% end %>
<tr>
<td style="word-wrap:break-word;">
<%= "#{u.first_name} #{u.last_name}"%>
</td>
<td>
<%= u.email%>
</td>
<td>
<%= u.admin ? %{<span class="fs1" aria-hidden="true" data-icon="&#xe0fe;"}.html_safe : nil %>
</td>
<td>
<%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%>
</td>
</tr>
<% end %>
</tbody>
</table>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div class="clearfix">
</div>
</div>
@@ -51,11 +51,10 @@ function openEditModal(id){
};
function dataTablePagination(){
$('#data-table').dataTable({
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
};
$(document).ready(dataTablePagination());
</script>
+47 -50
View File
@@ -1,7 +1,4 @@
<!-- Begin Modal -->
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
@@ -13,34 +10,34 @@
<div class="modal-body">
<div class="row-fluid">
<div class="span8">
<%= form_for @user, :html => {:id => "account_edit"} do |f| %>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
<%= form_for @user, :html => {:id => "account_edit"} do |f| %>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<%= f.label :admin, nil, {:class => "control-label"}%>
<%= f.select(:admin, @admin_select) %>
<%= f.label :admin, nil, {:class => "control-label"}%>
<%= f.select(:admin, @admin_select) %>
</div>
</div>
<div class="row-fluid">
@@ -53,7 +50,7 @@
<%= link_to "Delete", "#", {:id => "delete_button", :class => "btn btn-danger pull-left"} %>
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-primary pull-right"} %>
</div>
<% end %>
<% end %>
<!-- End Modal -->
<%= javascript_include_tag ('validation.js')%>
@@ -61,38 +58,38 @@
<script type="text/javascript">
$('#submit_button').click(function() {
var valuesToSubmit = $("#account_edit").serialize();
$("#editAcct").modal('hide');
var valuesToSubmit = $("#account_edit").serialize();
$("#editAcct").modal('hide');
$.ajax({
$.ajax({
url: "/admin/" + <%= @user.user_id %> + "/update_user.json",
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
$('#delete_button').click(function() {
$("#editAcct").modal('hide');
$("#editAcct").modal('hide');
$.ajax({
$.ajax({
url: "/admin/" + <%= params[:admin_id] %> + "/delete_user.json",
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
+9 -9
View File
@@ -17,19 +17,19 @@ end
</head>
<body>
<% if current_user %>
<%= render "layouts/shared/header" %>
<%= render "layouts/shared/sidebar" %>
<%= render "layouts/shared/header" %>
<%= render "layouts/shared/sidebar" %>
<% else %>
<%= render "layouts/tutorial/header" %>
<%= render "layouts/tutorial/sidebar" %>
<%= render "layouts/tutorial/header" %>
<%= render "layouts/tutorial/sidebar" %>
<% end %>
<div class="container-fluid">
<div class="dashboard-wrapper">
<%= render "layouts/shared/messages" %>
<%= yield %>
</div>
<div class="dashboard-wrapper">
<%= render "layouts/shared/messages" %>
<%= yield %>
</div>
</div>
<%= render "layouts/shared/footer" %>
<%= render "layouts/shared/footer" %>
<script type="text/javascript">
+12 -13
View File
@@ -1,5 +1,4 @@
<header>
<span style="color:#eee;margin-left:10px;">
Font Size:
<a href="<%= home_dashboard_index_path %>?font=8pt" style="font-size:10pt;color:#eee;">A</a>
@@ -13,7 +12,7 @@
<span class="caret"></span>
<ul class="dropdown-menu pull-right">
<li>
<%= link_to "account settings", user_account_settings_path(:user_id => current_user.user_id) %>
<%= link_to "account settings", user_account_settings_path(:user_id => current_user.user_id) %>
</li>
<li>
<%= link_to "logout", logout_path %>
@@ -21,16 +20,16 @@
</ul>
</div>
<ul class="mini-nav">
<li style="color: #FFFFFF">
<!--
I'm going to use HTML safe because we had some weird stuff
going on with funny chars and jquery, plus it says safe so I'm guessing
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
<li>
<%= button_to "RailsGoat Tutorials", tutorials_path, {:class => "btn btn-primary", :method => "get"}%>
</li>
<li style="color: #FFFFFF">
<!--
I'm going to use HTML safe because we had some weird stuff
going on with funny chars and jquery, plus it says safe so I'm guessing
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
<li>
<%= button_to "RailsGoat Tutorials", tutorials_path, {:class => "btn btn-primary", :method => "get"}%>
</li>
</ul>
</header>
+16 -16
View File
@@ -1,18 +1,18 @@
<% flash.each do |name, msg| %>
<% if name == :error %>
<div class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :success %>
<div class="alert alert-success">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :info %>
<div class="alert alert-info">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% end %>
<% if name == :error %>
<div class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :success %>
<div class="alert alert-success">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :info %>
<div class="alert alert-info">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% end %>
<% end %>
+49 -49
View File
@@ -17,9 +17,9 @@
Admin
<% end %>
</li>
<% end %>
<% end %>
<li id="benefit_forms">
<%= link_to user_benefit_forms_path(:user_id => current_user.user_id) do %>
<%= link_to user_benefit_forms_path(:user_id => current_user.user_id) do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe05c;"></span>
</div>
@@ -66,8 +66,8 @@
Messages
<% end %>
</li>
<li id="pay">
<%= link_to user_pay_index_path(:user_id => current_user.user_id) do %>
<li id="pay">
<%= link_to user_pay_index_path(:user_id => current_user.user_id) do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe038;"></span>
</div>
@@ -77,52 +77,52 @@
</ul>
</div>
<script type="text/javascript">
//Main menu navigation
<script type="text/javascript">
//Main menu navigation
$('.submenu > a').click(function(e){
e.preventDefault();
var submenu = $(this).siblings('ul');
var li = $(this).parents('li');
var submenus = $('#mainnav li.submenu ul');
var submenus_parents = $('#mainnav li.submenu');
if(li.hasClass('open'))
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenu.slideUp();
} else {
submenu.fadeOut(250);
}
li.removeClass('open');
} else
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenus.slideUp();
submenu.slideDown();
} else {
submenus.fadeOut(250);
submenu.fadeIn(250);
}
submenus_parents.removeClass('open');
li.addClass('open');
}
});
$('.submenu > a').click(function(e){
e.preventDefault();
var submenu = $(this).siblings('ul');
var li = $(this).parents('li');
var submenus = $('#mainnav li.submenu ul');
var submenus_parents = $('#mainnav li.submenu');
if(li.hasClass('open'))
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenu.slideUp();
} else {
submenu.fadeOut(250);
}
li.removeClass('open');
} else
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenus.slideUp();
submenu.slideDown();
} else {
submenus.fadeOut(250);
submenu.fadeIn(250);
}
submenus_parents.removeClass('open');
li.addClass('open');
}
});
var ul = $('#mainnav > ul');
var ul = $('#mainnav > ul');
$('#mainnav > a').click(function(e)
{
e.preventDefault();
var mainnav = $('#mainnav');
if(mainnav.hasClass('open'))
{
mainnav.removeClass('open');
ul.slideUp(250);
} else
{
mainnav.addClass('open');
ul.slideDown(250);
}
});
$('#mainnav > a').click(function(e)
{
e.preventDefault();
var mainnav = $('#mainnav');
if(mainnav.hasClass('open'))
{
mainnav.removeClass('open');
ul.slideUp(250);
} else
{
mainnav.addClass('open');
ul.slideDown(250);
}
});
</script>
</script>
+24 -24
View File
@@ -1,24 +1,24 @@
<!-- Want to use this template whether auth'd or not so I've got some code to determine how to render below -->
<header>
<% if not current_user %>
<ul class="mini-nav">
<li>
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<ul class="mini-nav">
<li>
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
</li>
</ul>
<% else %>
<ul class="mini-nav">
<li>
<%= button_to "<- back to app", home_dashboard_index_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<% end %>
<ul class="mini-nav">
<% if not current_user %>
<ul class="mini-nav">
<li>
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<ul class="mini-nav">
<li>
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
</li>
</ul>
<% else %>
<ul class="mini-nav">
<li>
<%= button_to "<- back to app", home_dashboard_index_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<% end %>
<ul class="mini-nav">
<li>
<%= button_to "Tutorial Credentials", "#myModalLabel1", {:id => "show_creds_btn", :class => "btn btn-danger", :method => "get"} %>
</li>
@@ -26,14 +26,14 @@
</header>
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
</div>
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
</div>
<script type="text/javascript">
$('#show_creds_btn').click(function(event) {
event.preventDefault();
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
$("#modal_div").modal("show");
event.preventDefault();
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
$("#modal_div").modal("show");
});
</script>
+12 -12
View File
@@ -1,6 +1,6 @@
<div id="mainnav"class="hidden-phone hidden-tablet">
<ul style="display: block;">
<li id="tutorials_home">
<li id="tutorials_home">
<%= link_to tutorials_path, :html => {:method => "get"} do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe001;"></span>
@@ -16,7 +16,7 @@
A1 Injection
<% end %>
</li>
<li id="broken_auth">
<li id="broken_auth">
<%= link_to broken_auth_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
@@ -41,7 +41,7 @@
A4 Insecure DOR
<% end %>
</li>
<li id="misconfig">
<li id="misconfig">
<%= link_to misconfig_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
@@ -65,7 +65,7 @@
A7 Access Control
<% end %>
</li>
<li id="csrf">
<li id="csrf">
<%= link_to csrf_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
@@ -97,21 +97,21 @@
Extras
</a>
<ul>
<li id="gauntlt">
<li id="gauntlt">
<%= link_to "gauntlt", gauntlt_tutorials_path %>
</li>
<li id="guard">
<%= link_to "Guard", guard_tutorials_path %>
</li>
<li id="mass_assignment">
<li id="mass_assignment">
<%= link_to "Mass Assignment", mass_assignment_tutorials_path %>
</li>
<li id="logic_flaws">
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
</li>
<li id="metaprogramming">
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
</li>
<li id="logic_flaws">
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
</li>
<li id="metaprogramming">
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
</li>
</ul>
</li>
</ul>
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseCompOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
<p class="desc">
A timing attack can exist in several forms. This specific case relates to username (email address) enumeration. By leveraging an automated tool, an attacker can review any subtle variation in response times after submitting a login request to determine if the application is performing a computationally intense function. Meaning, if a function is run once a user is discovered, even if the password is incorrect, this information provides the user with valid or invalid usernames.
</p>
</p>
</div>
</div>
</div>
@@ -32,28 +32,28 @@
</div>
<div class="accordion-body collapse" id="collapseCompTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within app/models/user.rb
</p>
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
<span style="background-color: yellow"> user = find_by_email(email)</span>
raise "#{email} doesn't exist!" if !(user)
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
return auth
end
</pre>
<p class="desc">
Ignore for a moment that the application actually tells you whether or not an email address exists :-). Instead, let's look at what would happen if this error message wasn't so specific. Even if the error message vulnerability was mitigated (because it indicates whether or not a user exists), there will be some variations in the application's response between a user that exists and one that does not (however so slight, considering MD5 is in use).
</p>
<p class="desc">
To understand why, let's follow the flow of this code example. Firstly, the application look for a user by email. If not found, nothing else really happens. No further processing, password comparison, etc. If a user <i>is</i> found, we will perform a password comparison and process as normal.
</p>
<p>
Within app/models/user.rb
</p>
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
<span style="background-color: yellow"> user = find_by_email(email)</span>
raise "#{email} doesn't exist!" if !(user)
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
return auth
end
</pre>
<p class="desc">
Ignore for a moment that the application actually tells you whether or not an email address exists :-). Instead, let's look at what would happen if this error message wasn't so specific. Even if the error message vulnerability was mitigated (because it indicates whether or not a user exists), there will be some variations in the application's response between a user that exists and one that does not (however so slight, considering MD5 is in use).
</p>
<p class="desc">
To understand why, let's follow the flow of this code example. Firstly, the application look for a user by email. If not found, nothing else really happens. No further processing, password comparison, etc. If a user <i>is</i> found, we will perform a password comparison and process as normal.
</p>
</div>
</div>
</div>
@@ -68,29 +68,29 @@
<div class="accordion-body collapse" id="collapseCompThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Lack of Password Complexity - SOLUTION</b></p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
def self.authenticate(email, password)
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
return user
else
raise "Incorrect username or password"
end
end
</pre>
<p class="desc">
To mitigate this attack and shore up our weakness, we do two things. The first is to find a user by email, if they don't exist, create a new user object in memory (not in the database) and assign it a blank password value. This means, regardless of whether or not a user exists, we will have a user to perform some processing on. The next is, we take the input from the user and match it against the user object's password leveraging secure_compare. This is a function (secure_compare) used to ensure that when a comparison happens, it will always take the same amount of time.
</p>
<p class="desc">
In summary, we have ensured that regardless of whether or not a user exists, a password comparison will always occur and it will take the same amount of time to complete.
</p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
def self.authenticate(email, password)
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
return user
else
raise "Incorrect username or password"
end
end
</pre>
<p class="desc">
To mitigate this attack and shore up our weakness, we do two things. The first is to find a user by email, if they don't exist, create a new user object in memory (not in the database) and assign it a blank password value. This means, regardless of whether or not a user exists, we will have a user to perform some processing on. The next is, we take the input from the user and match it against the user object's password leveraging secure_compare. This is a function (secure_compare) used to ensure that when a comparison happens, it will always take the same amount of time.
</p>
<p class="desc">
In summary, we have ensured that regardless of whether or not a user exists, a password comparison will always occur and it will take the same amount of time to complete.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseCompFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -100,9 +100,9 @@
</div>
<div class="accordion-body collapse" id="collapseCompFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Timing is everything. Authenticating is important too.
</p>
<p class="desc">
Timing is everything. Authenticating is important too.
</p>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapsePwdOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
Password complexity is incredibly important and highly debated subject. Other factors play a part in the stringency of the enforcement policy applied. If a username can be enumerated, a CAPTCHA on the login form is not present or other methods to deter a brute-force password guessing campaign are not in place, at least password complexity enforcement policy can make it a that much more difficult for an attacker to guess users passwords.
</p>
Password complexity is incredibly important and highly debated subject. Other factors play a part in the stringency of the enforcement policy applied. If a username can be enumerated, a CAPTCHA on the login form is not present or other methods to deter a brute-force password guessing campaign are not in place, at least password complexity enforcement policy can make it a that much more difficult for an attacker to guess users passwords.
</p>
</div>
</div>
</div>
@@ -32,18 +32,18 @@
</div>
<div class="accordion-body collapse" id="collapsePwdTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Within app/models/User.rb
</p>
<pre class="ruby">
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create
</pre>
<p class="desc">
The application validates only the password length and nothing else. Developers can leverage the format option to apply a regular expression that checks the password has sufficient complexity.
</p>
<pre class="ruby">
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create
</pre>
<p class="desc">
The application validates only the password length and nothing else. Developers can leverage the format option to apply a regular expression that checks the password has sufficient complexity.
</p>
</div>
</div>
</div>
@@ -58,28 +58,28 @@
<div class="accordion-body collapse" id="collapsePwdThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Lack of Password Complexity - ATTACK</b></p>
<p class="desc">
Leverage a tool such as BurpSuite's intruder to brute-force the passwords of the users. The highest privileged account that you an attacker can compromise is the admin. The password is very simple ("admin1234"), username is ("admin@metacorp.com").
</p>
<p><b>Lack of Password Complexity - SOLUTION</b></p>
<p class="desc">
This regular expression validates the password has the following requirements:
<li>1 digit</li>
<li>1 lowercase alphabet</li>
<li>1 uppercase alphabet</li>
<li>1 special character</li>
</p>
<pre class="ruby">
<p class="desc">
Leverage a tool such as BurpSuite's intruder to brute-force the passwords of the users. The highest privileged account that you an attacker can compromise is the admin. The password is very simple ("admin1234"), username is ("admin@metacorp.com").
</p>
<p><b>Lack of Password Complexity - SOLUTION</b></p>
<p class="desc">
This regular expression validates the password has the following requirements:
<li>1 digit</li>
<li>1 lowercase alphabet</li>
<li>1 uppercase alphabet</li>
<li>1 special character</li>
</p>
<pre class="ruby">
validates :password, :presence => true,
:confirmation => true,
:if => :password,
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
</pre>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapsePwdFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -89,9 +89,9 @@ validates :password, :presence => true,
</div>
<div class="accordion-body collapse" id="collapsePwdFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
I wonder how strong the administrator's password is?
</p>
<p class="desc">
I wonder how strong the administrator's password is?
</p>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
Overly verbose error messages that indicate whether or not a user exists can assist an attacker with brute-forcing accounts. In attempting to harvest valid usernames for a password-guessing campaign, these messages can prove very useful.
</p>
Overly verbose error messages that indicate whether or not a user exists can assist an attacker with brute-forcing accounts. In attempting to harvest valid usernames for a password-guessing campaign, these messages can prove very useful.
</p>
</div>
</div>
</div>
@@ -33,50 +33,50 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p><b>Username and Password Enumeration</b></p>
<p><b>Within /app/models/user.rb:</b><p>
<p><b>Within /app/models/user.rb:</b><p>
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
if user
if user.password == password
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
</pre>
<p> On lines 9 and 12 you'll notice that the application generates two error messages. </p>
<p><b>Within /app/controllers/sessions_controller.rb:</b><p>
<pre class="ruby">
def create
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
if user
if user.password == password
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
</pre>
<p> On lines 9 and 12 you'll notice that the application generates two error messages. </p>
<p><b>Within /app/controllers/sessions_controller.rb:</b><p>
<pre class="ruby">
def create
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = e.message
render "new"
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = e.message
render "new"
end
end
</pre>
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
<p class="desc">
One of these messages indicates the email address (username) doesn't exist on the system. The other indicates that the password is incorrect. Although the application will render both error messages, either one of the error messages would be harmful by itself. This type of information can be used by an attacker to harvest email addresses or usernames. Once that list is gathered, passwords can be guessed for each account. If the username being enumerated is actually an email address, a phishing campaign could ensue with emails made to look like they are originating from the vulnerable site.
</p>
end
</pre>
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
<p class="desc">
One of these messages indicates the email address (username) doesn't exist on the system. The other indicates that the password is incorrect. Although the application will render both error messages, either one of the error messages would be harmful by itself. This type of information can be used by an attacker to harvest email addresses or usernames. Once that list is gathered, passwords can be guessed for each account. If the username being enumerated is actually an email address, a phishing campaign could ensue with emails made to look like they are originating from the vulnerable site.
</p>
</div>
</div>
</div>
@@ -91,34 +91,34 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p>
<b> Username and Password Enumeration - SOLUTION</b>
</p>
<p> Within /app/controllers/sessions_controller.rb</p>
<b> Username and Password Enumeration - SOLUTION</b>
</p>
<p> Within /app/controllers/sessions_controller.rb</p>
<pre class="ruby">
def create
def create
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = "Either your username and password is incorrect" #e.message
render "new"
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = "Either your username and password is incorrect" #e.message
render "new"
end
end
</pre>
<p class="desc">
Although this fix is neither systemic nor does it address the problematic code at its core (within the user model), it does provide a quick solution. On line 12, we comment out the "e.message code" and instead provide a very generic error message that lacks specificity on what credential was incorrectly entered.
</p>
end
</pre>
<p class="desc">
Although this fix is neither systemic nor does it address the problematic code at its core (within the user model), it does provide a quick solution. On line 12, we comment out the "e.message code" and instead provide a very generic error message that lacks specificity on what credential was incorrectly entered.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -128,10 +128,10 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Enter an email address that wouldn't likely exist into the login form. Analyze the result.<br/><br/>
Can you leverage this to gain unauthorized access?
</p>
<p class="desc">
Enter an email address that wouldn't likely exist into the login form. Analyze the result.<br/><br/>
Can you leverage this to gain unauthorized access?
</p>
</div>
</div>
</div>
@@ -11,83 +11,83 @@
<div class="modal-body">
<div class="row">
<div class="span8">
<p>Warning, this is a spoiler</p>
<p>Are you sure you want to see the credentials?</p>
<div id="creds_hidden" style="display:none">
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
<thead>
<tr>
<th>
Email
</th>
<th>
Password
</th>
<th>
API Key
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="word-wrap:break-word;">
admin@metacorp.com
</td>
<td>
admin1234
</td>
<td>
1-01de24d75cffaa66db205278d1cf900bf087a737
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jack@metacorp.com
</td>
<td>
yankeessuck
</td>
<td>
2-050ddd40584978fe9e82840b8b95abb98e4786dc
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jim@metacorp.com
</td>
<td>
alohaowasp
</td>
<td>
3-eaa9b4d748d6a8c6a38e24ac1cc2204ebc3541c1
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
mike@metacorp.com
</td>
<td>
motocross1445
</td>
<td>
4-4c809b3d11d272cff8cab1da9e4cdf61137f29d2
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
ken@metacorp.com
</td>
<td>
citrusblend
</td>
<td>
5-4af604a848ca212cfa3935352aabe9522cf89fdc
</td>
</tr>
<p>Warning, this is a spoiler</p>
<p>Are you sure you want to see the credentials?</p>
<div id="creds_hidden" style="display:none">
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
<thead>
<tr>
<th>
Email
</th>
<th>
Password
</th>
<th>
API Key
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="word-wrap:break-word;">
admin@metacorp.com
</td>
<td>
admin1234
</td>
<td>
1-01de24d75cffaa66db205278d1cf900bf087a737
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jack@metacorp.com
</td>
<td>
yankeessuck
</td>
<td>
2-050ddd40584978fe9e82840b8b95abb98e4786dc
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jim@metacorp.com
</td>
<td>
alohaowasp
</td>
<td>
3-eaa9b4d748d6a8c6a38e24ac1cc2204ebc3541c1
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
mike@metacorp.com
</td>
<td>
motocross1445
</td>
<td>
4-4c809b3d11d272cff8cab1da9e4cdf61137f29d2
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
ken@metacorp.com
</td>
<td>
citrusblend
</td>
<td>
5-4af604a848ca212cfa3935352aabe9522cf89fdc
</td>
</tr>
</tbody>
</table>
</div>
</tbody>
</table>
</div>
</div>
</div>
<div class="row-fluid">
@@ -97,7 +97,7 @@
<button class="btn" data-dismiss="modal" aria-hidden="true">
Close
</button>
<button id="understood" class="btn btn-primary" aria-hidden="true">
<button id="understood" class="btn btn-primary" aria-hidden="true">
I understand
</button>
@@ -108,7 +108,7 @@
<script type="text/javascript">
$('#understood').click(function() {
$("#creds_hidden").show();
$("#creds_hidden").show();
});
@@ -31,20 +31,20 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p><b>Cross-Site Request Forgery (CSRF) - The following code was taken from: /app/controllers/application_controller.rb and /app/views/layouts/application.html.erb</b></p>
<p>application_controller.rb<p>
<p>
<pre class="ruby">
# Our security guy keep talking about sea-surfing, cool story bro.
# protect_from_forgery
</pre>
<p>application_controller.rb<p>
<p>
<pre class="ruby">
# Our security guy keep talking about sea-surfing, cool story bro.
# protect_from_forgery
</pre>
</p>
<p> application.html.erb </p>
<p>
<pre class="ruby">
<%= @meta_code_bad %>
</pre>
</p>
</p>
<p> application.html.erb </p>
<p>
<pre class="ruby">
<%= @meta_code_bad %>
</pre>
</p>
</div>
</div>
</div>
@@ -59,57 +59,57 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Cross-Site Request Forgery ATTACK:</b></p>
<p class="desc">
The application allows users to update their calendar and schedule PTO events (PTO section). Due to the fact CSRF protections are disabled, the AJAX request will send the authenticity token but the application will <b>not</b> validate either its presence or validity. Create an html page using the code shown below, authenticate as another user, click on it, review the new calendar (change the dates under date_range1). You should see this HTML code will work, even if you hadn't navigated to the PTO section prior to sending it.
</p>
<p>
<pre class="ruby">
<%=
<p class="desc">
The application allows users to update their calendar and schedule PTO events (PTO section). Due to the fact CSRF protections are disabled, the AJAX request will send the authenticity token but the application will <b>not</b> validate either its presence or validity. Create an html page using the code shown below, authenticate as another user, click on it, review the new calendar (change the dates under date_range1). You should see this HTML code will work, even if you hadn't navigated to the PTO section prior to sending it.
</p>
<p>
<pre class="ruby">
<%=
%{
<html>
<body>
<form action="http://railsgoat.dev/schedule.json" method="POST">
<input type="hidden" name="schedule&#91;event&#95;name&#93;" value="Bad&#32;Guy" />
<input type="hidden" name="schedule&#91;event&#95;type&#93;" value="pto" />
<input type="hidden" name="schedule&#91;event&#95;desc&#93;" value="Fun&#32;Fun" />
<input type="hidden" name="date&#95;range1" value="06&#47;08&#47;2013&#32;&#45;&#32;06&#47;09&#47;2013" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
<html>
<body>
<form action="http://railsgoat.dev/schedule.json" method="POST">
<input type="hidden" name="schedule&#91;event&#95;name&#93;" value="Bad&#32;Guy" />
<input type="hidden" name="schedule&#91;event&#95;type&#93;" value="pto" />
<input type="hidden" name="schedule&#91;event&#95;desc&#93;" value="Fun&#32;Fun" />
<input type="hidden" name="date&#95;range1" value="06&#47;08&#47;2013&#32;&#45;&#32;06&#47;09&#47;2013" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
}
%>
</pre>
</p>
%>
</pre>
</p>
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
<p>
By default, the protect_from_forgery directive is added under the application_controller.rb at project creation. However, occasionally developers turn it off (comment out) because of issues with JS. There are two separate solutions around the JS problem.
</p>
<p>
Once protect_from_forgery is added back...
<li>Add the following code within the header section of the application.html.erb file (or any other application layout file).</li>
</p>
<p>
<pre class="ruby">
<%= @meta_code_good %>
</pre>
</p>
<p>
That will allow you to parse the meta tag with JS. However, keep in mind that any form generated by Rails is populated with an authenticity token so, if you leverage something like JQuery to make an Ajax request, you can include all values within the form by using the technique shown next.
</p>
<p>
<li>Leverage the serialize() method, shown on line 3. This grabs all the values from the form, including the authenticity token.</li>
</p>
<p>
<pre class="ruby">
<%= @ajax_code_good %>
</pre>
</p>
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
<p>
By default, the protect_from_forgery directive is added under the application_controller.rb at project creation. However, occasionally developers turn it off (comment out) because of issues with JS. There are two separate solutions around the JS problem.
</p>
<p>
Once protect_from_forgery is added back...
<li>Add the following code within the header section of the application.html.erb file (or any other application layout file).</li>
</p>
<p>
<pre class="ruby">
<%= @meta_code_good %>
</pre>
</p>
<p>
That will allow you to parse the meta tag with JS. However, keep in mind that any form generated by Rails is populated with an authenticity token so, if you leverage something like JQuery to make an Ajax request, you can include all values within the form by using the technique shown next.
</p>
<p>
<li>Leverage the serialize() method, shown on line 3. This grabs all the values from the form, including the authenticity token.</li>
</p>
<p>
<pre class="ruby">
<%= @ajax_code_good %>
</pre>
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -123,6 +123,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseModelOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
</p>
<p class="desc">
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
</p>
</div>
</div>
</div>
@@ -32,51 +32,51 @@
</div>
<div class="accordion-body collapse" id="collapseModelTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
<p class="desc">
Within app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
respond_with @user.admin ? User.all : @user
end
def show
respond_with @user.as_json
end
</pre>
<p class="desc">
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
</p>
<pre class="ruby">
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
</pre>
<p class="desc">
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
X-Runtime: 0.051734
Connection: close
def show
respond_with @user.as_json
end
</pre>
<p class="desc">
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
</p>
<pre class="ruby">
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
</pre>
<p class="desc">
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
X-Runtime: 0.051734
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
Note that all attributes associated with this user are returned via the API.
</p>
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
Note that all attributes associated with this user are returned via the API.
</p>
</div>
</div>
</div>
@@ -91,41 +91,41 @@
<div class="accordion-body collapse" id="collapseModelThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Model Attributes Exposure - ATTACK</b></p>
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
<p><b>Model Attributes Exposure - SOLUTION</b></p>
<p class="desc">
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
</p>
<pre class="ruby">
respond_with @user.admin ? User.all.as_json : @user.as_json
</pre>
<p class="desc">
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
</p>
<pre class="ruby">
def as_json
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
end
</pre>
<p class="desc">
The response from the API should look like:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "2333488e856669ac637e37cb4cf09cb6"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: baa6a1c90004838793614e4c61633767
X-Runtime: 0.092768
Connection: close
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
<p><b>Model Attributes Exposure - SOLUTION</b></p>
<p class="desc">
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
</p>
<pre class="ruby">
respond_with @user.admin ? User.all.as_json : @user.as_json
</pre>
<p class="desc">
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
</p>
<pre class="ruby">
def as_json
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
end
</pre>
<p class="desc">
The response from the API should look like:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "2333488e856669ac637e37cb4cf09cb6"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: baa6a1c90004838793614e4c61633767
X-Runtime: 0.092768
Connection: close
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
</pre>
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseModelFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -16,15 +16,15 @@
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
<p class="desc">
The OWASP description - Many web applications do not properly protect sensitive data, such as credit cards, SSNs, and authentication credentials, with appropriate encryption or hashing. Attackers may steal or modify such weakly protected data to conduct identity theft, credit card fraud, or other crimes.
</p>
<p class="desc">
Railsgoat does hash user passwords. Unfortunately, it does so using an extremely weak algorithm (MD5). Generally speaking, a strong algorithm and per-user salt can greatly improve the security of a hashed value. Also important to note, hashing and encryption are not the same. Encryption is meant to be reversible using some secret information, hashing is not, hashing is a one-way function not meant to be reversible.
</p>
<p class="desc">
All that being said, there are groups within security organizations that devote themselves to threat models built around this topic so clearly, this description does not encompass all scenarios. However, our recommendation is better than hashing using MD5 <i class="icon-wink-2"></i>.
</p>
</p>
<p class="desc">
Railsgoat does hash user passwords. Unfortunately, it does so using an extremely weak algorithm (MD5). Generally speaking, a strong algorithm and per-user salt can greatly improve the security of a hashed value. Also important to note, hashing and encryption are not the same. Encryption is meant to be reversible using some secret information, hashing is not, hashing is a one-way function not meant to be reversible.
</p>
<p class="desc">
All that being said, there are groups within security organizations that devote themselves to threat models built around this topic so clearly, this description does not encompass all scenarios. However, our recommendation is better than hashing using MD5 <i class="icon-wink-2"></i>.
</p>
</div>
</div>
</div>
@@ -38,34 +38,34 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
before_save <span style="background-color:yellow">:hash_password</span>
</p>
<pre class="ruby">
before_save <span style="background-color:yellow">:hash_password</span>
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
if user
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
if user
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
def hash_password
if self.password.present?
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
end
end
def hash_password
if self.password.present?
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
end
end
</pre>
</pre>
</div>
</div>
@@ -81,34 +81,34 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Password Storage - ATTACK</b></p>
<p class="desc">
Using the passwords stored within db/seeds.rb file, create a wordlist and leverage a password cracking tool such as John The Ripper to crack those passwords.
</p>
<p><b>Password Storage - SOLUTION</b></p>
<p class="desc">
A simple solution here would be to enforce a per-user salt in creating a BCrypt hash. You would need to alter the db schema to add a password_salt and password_hash columns to the table.
</p>
<pre class="ruby">
def self.authenticate(email, password)
user = find_by_email(email)
if user and user.password_hash == <span style="background-color:yellow">BCrypt::Engine.hash_secret(password, user.password_salt)</span>
user
else
"Invalid Credentials Supplied"
end
end
<p class="desc">
Using the passwords stored within db/seeds.rb file, create a wordlist and leverage a password cracking tool such as John The Ripper to crack those passwords.
</p>
<p><b>Password Storage - SOLUTION</b></p>
<p class="desc">
A simple solution here would be to enforce a per-user salt in creating a BCrypt hash. You would need to alter the db schema to add a password_salt and password_hash columns to the table.
</p>
<pre class="ruby">
def self.authenticate(email, password)
user = find_by_email(email)
if user and user.password_hash == <span style="background-color:yellow">BCrypt::Engine.hash_secret(password, user.password_salt)</span>
user
else
"Invalid Credentials Supplied"
end
end
def hash_password
if self.password.present?
<span style="background-color:yellow">self.password_salt = BCrypt::Engine.generate_salt</span>
<span style="background-color:yellow">self.password_hash = BCrypt::Engine.hash_secret(self.password, self.password_salt)</span>
end
end
</pre>
def hash_password
if self.password.present?
<span style="background-color:yellow">self.password_salt = BCrypt::Engine.generate_salt</span>
<span style="background-color:yellow">self.password_hash = BCrypt::Engine.hash_secret(self.password, self.password_salt)</span>
end
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
+100 -100
View File
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseSSNOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores and transmits Social Security Numbers insecurely.
</p>
<p class="desc">
The Railsgoat application stores and transmits Social Security Numbers insecurely.
</p>
</div>
</div>
</div>
@@ -32,18 +32,18 @@
</div>
<div class="accordion-body collapse" id="collapseSSNTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
</p>
<p class="desc">
<p class="desc">
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
</p>
<p class="desc">
The WorkInfo model (app/models/work_info.rb) is missing code to encrypt this data prior to storage. Additionally, while code exists to render only the last 4 numbers of an SSN (shown below), at no time is it used.
</p>
<pre class="ruby">
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
end
</pre>
</p>
<pre class="ruby">
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
end
</pre>
</div>
</div>
@@ -58,100 +58,100 @@
</div>
<div class="accordion-body collapse" id="collapseSSNThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>SSN Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
</p>
<p><b>SSN Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
</p>
<p class="desc">
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
</p>
<pre class="ruby">
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
<p class="desc">
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
</p>
<pre class="ruby">
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def key
raise "Key Missing" if !(KEY)
KEY
end
def key
raise "Key Missing" if !(KEY)
KEY
end
def iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def cipher_type
'aes-256-cbc'
end
</pre>
<p class="desc">
Also within the WorkInfo model, we add the following line of code...
</p>
<pre class="ruby">
before_save :encrypt_ssn
</pre>
<p class="desc">
The remaining pieces are:
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
<li> For new user's who are registering, we create an initialization specific to their account</li>
</p>
<pre class="ruby">
# SEED DATA
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
</pre>
<pre class="ruby">
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
elsif Rails.env.development?
KEY = "123456789101112123456789101112123456789101112"
end
</pre>
<pre class="ruby">
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
<%= CGI.unescapeHTML("&lt;td class=&quot;ssn&quot;&gt;&lt;%= @user.work_info.last_four %&gt;&lt;/td&gt;") %>
</pre>
<pre class="ruby">
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
</pre>
def cipher_type
'aes-256-cbc'
end
</pre>
<p class="desc">
Also within the WorkInfo model, we add the following line of code...
</p>
<pre class="ruby">
before_save :encrypt_ssn
</pre>
<p class="desc">
The remaining pieces are:
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
<li> For new user's who are registering, we create an initialization specific to their account</li>
</p>
<pre class="ruby">
# SEED DATA
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
</pre>
<pre class="ruby">
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
elsif Rails.env.development?
KEY = "123456789101112123456789101112123456789101112"
end
</pre>
<pre class="ruby">
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
<%= CGI.unescapeHTML("&lt;td class=&quot;ssn&quot;&gt;&lt;%= @user.work_info.last_four %&gt;&lt;/td&gt;") %>
</pre>
<pre class="ruby">
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseSSNFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseNine" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
An OS command injection attack occurs when an attacker attempts to execute system level commands through a vulnerable application. Applications are considered vulnerable to the OS command injection attack if they utilize user input in a system level command.
</p>
An OS command injection attack occurs when an attacker attempts to execute system level commands through a vulnerable application. Applications are considered vulnerable to the OS command injection attack if they utilize user input in a system level command.
</p>
</div>
</div>
</div>
@@ -32,50 +32,50 @@
</div>
<div class="accordion-body collapse" id="collapseTen" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
This manifestation of the bug occurs within the Benefits model. A system command is used to make a copy of the file the user has chosen to upload. User-supplied input is leveraged in creating this system command.
</p>
<p>
Within app/controllers/benefits_controller.rb:
</p>
<pre class="ruby">
def upload
<span style="background:yellow">file = params[:benefits][:upload]</span>
if file
flash[:success] = "File Successfully Uploaded!"
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
else
flash[:error] = "Something went wrong"
end
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
</pre>
<p>
Within app/models/benefits.rb:
</p>
<pre class="ruby">
class Benefits &lt; ActiveRecord::Base
attr_accessor :backup
<p class="desc">
This manifestation of the bug occurs within the Benefits model. A system command is used to make a copy of the file the user has chosen to upload. User-supplied input is leveraged in creating this system command.
</p>
<p>
Within app/controllers/benefits_controller.rb:
</p>
<pre class="ruby">
def upload
<span style="background:yellow">file = params[:benefits][:upload]</span>
if file
flash[:success] = "File Successfully Uploaded!"
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
else
flash[:error] = "Something went wrong"
end
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
</pre>
<p>
Within app/models/benefits.rb:
</p>
<pre class="ruby">
class Benefits &lt; ActiveRecord::Base
attr_accessor :backup
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "w+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "w+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
def self.make_backup(file, data_path, full_file_name)
system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{<span style="background:yellow">file.original_filename</span>}")
end
def self.make_backup(file, data_path, full_file_name)
system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{<span style="background:yellow">file.original_filename</span>}")
end
end
end
</pre>
<p class="desc">
The command injection vulnerability is introduced when the user-supplied input (name of file) is interpolated or mixed in with a system command.
</p>
</pre>
<p class="desc">
The command injection vulnerability is introduced when the user-supplied input (name of file) is interpolated or mixed in with a system command.
</p>
</div>
</div>
</div>
@@ -90,54 +90,54 @@
<div class="accordion-body collapse" id="collapseEleven" style="height: 0px;">
<div class="accordion-inner">
<p><b>Command Injection - ATTACK</b></p>
<p class="desc">
The filename portion of the benefits[upload] parameter is vulnerable to command injection. Navigate to the benefits section of the application, and choose a file to upload. Once the file is chosen, turn your intercepting proxy on, click start upload, and intercept the request. you will want to change the backup option to true (highlighted below) and inject your commands within the filename parameter (highlighted). Note: forward slashes ('/') are escaped by the original_filename method (used to extract the file name ).
</p>
<pre class='ruby'>
POST /upload HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/users/5/benefit_forms
Cookie: _railsgoat_session=[redacted for brevity]
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------54316025
Content-Length: 1731
<p class="desc">
The filename portion of the benefits[upload] parameter is vulnerable to command injection. Navigate to the benefits section of the application, and choose a file to upload. Once the file is chosen, turn your intercepting proxy on, click start upload, and intercept the request. you will want to change the backup option to true (highlighted below) and inject your commands within the filename parameter (highlighted). Note: forward slashes ('/') are escaped by the original_filename method (used to extract the file name ).
</p>
<pre class='ruby'>
POST /upload HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/users/5/benefit_forms
Cookie: _railsgoat_session=[redacted for brevity]
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------54316025
Content-Length: 1731
----------54316025
Content-Disposition: form-data; name="utf8"
----------54316025
Content-Disposition: form-data; name="utf8"
✓
----------54316025
Content-Disposition: form-data; name="authenticity_token"
✓
----------54316025
Content-Disposition: form-data; name="authenticity_token"
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
----------54316025
Content-Disposition: form-data; name="benefits[backup]"
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
----------54316025
Content-Disposition: form-data; name="benefits[backup]"
<span style="background:yellow">true</span>
----------54316025
Content-Disposition: form-data; name="benefits[upload]"; <span style="background:yellow">filename="test.rb;+mkdir+thisisatest "</span>
Content-Type: text/x-ruby-script
</pre>
<p><b>Command Injection - SOLUTION</b></p>
<p class="desc">
The solution is fairly simple and because this is so poorly done there are numerous ways to fix the vulnerability. One option, is to abstract a file creation method and pass it options such as the path and filename, then call it twice, once for the initial upload and another for the backup. Another option is to make a copy through the use of the FileUtils.
</p>
<p>
As an example:
</p>
<pre class="ruby">
def self.make_backup(file, data_path, full_file_name)
FileUtils.cp "#{full_file_name}", "#{data_path}/bak#{Time.now.to_i}_#{file.original_filename}"
end
</pre>
<span style="background:yellow">true</span>
----------54316025
Content-Disposition: form-data; name="benefits[upload]"; <span style="background:yellow">filename="test.rb;+mkdir+thisisatest "</span>
Content-Type: text/x-ruby-script
</pre>
<p><b>Command Injection - SOLUTION</b></p>
<p class="desc">
The solution is fairly simple and because this is so poorly done there are numerous ways to fix the vulnerability. One option, is to abstract a file creation method and pass it options such as the path and filename, then call it twice, once for the initial upload and another for the backup. Another option is to make a copy through the use of the FileUtils.
</p>
<p>
As an example:
</p>
<pre class="ruby">
def self.make_backup(file, data_path, full_file_name)
FileUtils.cp "#{full_file_name}", "#{data_path}/bak#{Time.now.to_i}_#{file.original_filename}"
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseTwelve" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -151,6 +151,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p>
Injection flaws, such as SQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attackers hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
</p>
Injection flaws, such as SQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attackers hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
</p>
</div>
</div>
</div>
@@ -32,30 +32,30 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
<p class="desc">
This example of SQL Injection also happens to be a form of <%= link_to "Insecure Direct Object Reference", insecure_dor_tutorials_path, {:target => "_blank", :style => "color: rgb(181, 121, 158)"} %> since it uses user-supplied input to determine the user's profile to update. However, we will discuss the SQL query being used and why it is vulnerable.
</p>
<p>
Within app/controllers/users_controller.rb
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow"> user = User.find(:first, :conditions => "user_id = '#{params[:user][:user_id]}'")</span>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<p class="desc">
The injection vulnerability is introduced when user-supplied input is placed within the SQL string that will be executed as a query. The application will not be able to determine which portion of this query is data and which portion is a query as the user input is interpolated or co-mingled with the query string.
</p>
</p>
<p>
Within app/controllers/users_controller.rb
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow"> user = User.find(:first, :conditions => "user_id = '#{params[:user][:user_id]}'")</span>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<p class="desc">
The injection vulnerability is introduced when user-supplied input is placed within the SQL string that will be executed as a query. The application will not be able to determine which portion of this query is data and which portion is a query as the user input is interpolated or co-mingled with the query string.
</p>
</div>
</div>
</div>
@@ -70,77 +70,77 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>SQL Injection - ATTACK</b></p>
<p class="desc">
You will need to use an intercepting proxy or otherwise modify the request prior to it being received by the application. Browse to account_settings (top right, drop-down). Once at the account settings page, type in passwords, and click submit. Now modify the request from:
<p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 294
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
utf8=✓&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background-color: yellow"> user[user_id]=5</span>&user[email]=ken@metacorp.com&user[first_name]=Ken&user[last_name]=Johnson&user[password]=testtest&user[password_confirmation]=testtest
</pre>
<p class="desc">
Now we will inject some SQL Query syntax that will return the first result of a query that looks for users that have an admin attribute that is true. So essentially, instead of looking up the user whose data we will change by our user ID, we tell the database to return the first admin and update their data. In this instance, we are changing admin@metacorp.com's password to testtest. We can later login as that user. Granted, we could just change the user_id to 1 and do the same thing, and there are other ways to exploit this weakness but this is a clear-cut example of SQL Injection. <b> It is important to note that we have omitted the email, first, and last name parameters as a duplicate email address will cause errors. Additionally, we do not wish to change the admin's first and last name as this would alert the admin to the "hack".</b>
</p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 208
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
<p class="desc">
You will need to use an intercepting proxy or otherwise modify the request prior to it being received by the application. Browse to account_settings (top right, drop-down). Once at the account settings page, type in passwords, and click submit. Now modify the request from:
<p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 294
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
utf8=✓&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background-color: yellow"> user[user_id]=5</span>&user[email]=ken@metacorp.com&user[first_name]=Ken&user[last_name]=Johnson&user[password]=testtest&user[password_confirmation]=testtest
</pre>
<p class="desc">
Now we will inject some SQL Query syntax that will return the first result of a query that looks for users that have an admin attribute that is true. So essentially, instead of looking up the user whose data we will change by our user ID, we tell the database to return the first admin and update their data. In this instance, we are changing admin@metacorp.com's password to testtest. We can later login as that user. Granted, we could just change the user_id to 1 and do the same thing, and there are other ways to exploit this weakness but this is a clear-cut example of SQL Injection. <b> It is important to note that we have omitted the email, first, and last name parameters as a duplicate email address will cause errors. Additionally, we do not wish to change the admin's first and last name as this would alert the admin to the "hack".</b>
</p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 208
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
utf8=✓&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background:yellow">user[user_id]=5') OR admin = 't' --'")</span>&user[password]=testtest1&user[password_confirmation]=testtest1
</pre>
<p><b>SQL Injection - SOLUTION</b></p>
<p class="desc">
In this instance, the more secure route would be to reference the current_user object versus pulling from the database manually, using POST parameters provided by the user.<br/><br/>
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow">user = current_user</span>
utf8=✓&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background:yellow">user[user_id]=5') OR admin = 't' --'")</span>&user[password]=testtest1&user[password_confirmation]=testtest1
</pre>
<p><b>SQL Injection - SOLUTION</b></p>
<p class="desc">
In this instance, the more secure route would be to reference the current_user object versus pulling from the database manually, using POST parameters provided by the user.<br/><br/>
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow">user = current_user</span>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<p class="desc">
...However, since we are discussing fixing vulnerable SQL queries, let's discuss parameterized queries. Parameterized queries separate the SQL Query from the dynamic and often untrusted data. You could replace the string interpolated value with the following query and effectively separate the query from untrusted data:
</p>
<pre class="ruby">
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
</pre>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<p class="desc">
...However, since we are discussing fixing vulnerable SQL queries, let's discuss parameterized queries. Parameterized queries separate the SQL Query from the dynamic and often untrusted data. You could replace the string interpolated value with the following query and effectively separate the query from untrusted data:
</p>
<pre class="ruby">
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -154,6 +154,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,14 +17,14 @@
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
ActiveRecord provides a useful tool for it's Models called a <i>scope</i>. In the words of the documentation:
</p>
<pre><i>
ActiveRecord provides a useful tool for it's Models called a <i>scope</i>. In the words of the documentation:
</p>
<pre><i>
"Scoping allows you to specify commonly-used queries which can be referenced as <br/>method calls on the association objects or models."
</i></pre>
<p class="desc">
This means that we can call a scope as a method and that the scope can be used for common queries such as <i>where</i> and <i>join</i>. Developers must be careful not to interpolate or concatenate user input into these scope calls as this can lead to SQL Injection. This is a common mistake made and can have serious consequences.
</p>
</i></pre>
<p class="desc">
This means that we can call a scope as a method and that the scope can be used for common queries such as <i>where</i> and <i>join</i>. Developers must be careful not to interpolate or concatenate user input into these scope calls as this can lead to SQL Injection. This is a common mistake made and can have serious consequences.
</p>
</div>
</div>
</div>
@@ -38,41 +38,41 @@
</div>
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within app/models/analytics.rb:
</p>
<pre class="ruby">
class Analytics < ActiveRecord::Base
attr_accessible :ip_address, :referrer, :user_agent
<p class="desc">
Within app/models/analytics.rb:
</p>
<pre class="ruby">
class Analytics < ActiveRecord::Base
attr_accessible :ip_address, :referrer, :user_agent
<span style="background-color:yellow">scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}</span>
<span style="background-color:yellow">scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}</span>
def self.count_by_col(col)
calculate(:count, col)
end
</pre>
<p class="desc">
Additionally, within app/controllers/admin_controller.rb:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
<span style="background-color:yellow">fields = params[:field].map {|k,v| k }.join(",")</span>
end
def self.count_by_col(col)
calculate(:count, col)
end
</pre>
<p class="desc">
Additionally, within app/controllers/admin_controller.rb:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
<span style="background-color:yellow">fields = params[:field].map {|k,v| k }.join(",")</span>
end
if params[:ip]
<span style="background-color:yellow">@analytics = Analytics.hits_by_ip(params[:ip], fields)</span>
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<p class="desc">
Within the controller we call the method <i>hits_by_ip</i>. This method is actually a scope as highlighted (above) in the Analytics model. The field object, defined within the controller, represents user-input that is intended to control the column returned by the SQL query. The field object represents the HTTP Request's parameter key. So this means we can control at least a portion of the query. Due to the fact that this input is used as an interpolated value within the query string, we have control over a larger portion of the query.
</p>
if params[:ip]
<span style="background-color:yellow">@analytics = Analytics.hits_by_ip(params[:ip], fields)</span>
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<p class="desc">
Within the controller we call the method <i>hits_by_ip</i>. This method is actually a scope as highlighted (above) in the Analytics model. The field object, defined within the controller, represents user-input that is intended to control the column returned by the SQL query. The field object represents the HTTP Request's parameter key. So this means we can control at least a portion of the query. Due to the fact that this input is used as an interpolated value within the query string, we have control over a larger portion of the query.
</p>
</div>
</div>
</div>
@@ -87,72 +87,72 @@
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
<div class="accordion-inner">
<p><b>SQL Injection - ATTACK</b></p>
<p class="desc">
Navigate to the admin analytics panel. Send a request to search by an IP. Modify the request to change the parameter key to a partial SQL statement that returns all users and their information from the database:
</p>
<pre>
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie:[redacted]
Connection: keep-alive
</pre>
<p class="desc">
Essentially we are changing the intended SQL query from:
</p>
<pre>
SELECT <span style="background-color:yellow">UserInput</span> FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p class="desc">
to:
</p>
<pre>
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p><b>SQL Injection - SOLUTION</b></p>
<p class="desc">
To resolve this issue, do not interpolate user-provided input into SQL queries. However, it is always a good idea to create a whitelist of acceptable values when writing any code that is intended to be powerful and very flexible but that also leverages user-input to make these potentially security-impacting decisions. Within the Analytics model, we have a method called <i>parse_field</i>:
</p>
<pre class="ruby">
def self.parse_field(field)
valid_fields = ["ip_address", "referrer", "user_agent"]
<p class="desc">
Navigate to the admin analytics panel. Send a request to search by an IP. Modify the request to change the parameter key to a partial SQL statement that returns all users and their information from the database:
</p>
<pre>
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie:[redacted]
Connection: keep-alive
</pre>
<p class="desc">
Essentially we are changing the intended SQL query from:
</p>
<pre>
SELECT <span style="background-color:yellow">UserInput</span> FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p class="desc">
to:
</p>
<pre>
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p><b>SQL Injection - SOLUTION</b></p>
<p class="desc">
To resolve this issue, do not interpolate user-provided input into SQL queries. However, it is always a good idea to create a whitelist of acceptable values when writing any code that is intended to be powerful and very flexible but that also leverages user-input to make these potentially security-impacting decisions. Within the Analytics model, we have a method called <i>parse_field</i>:
</p>
<pre class="ruby">
def self.parse_field(field)
valid_fields = ["ip_address", "referrer", "user_agent"]
if valid_fields.include?(field)
field
else
"1"
end
end
</pre>
<p class="desc">
When used properly, this method prevents anything that does not match those items in the <i>valid_fields</i> array from reaching the SQL query. For example, within the admin controller we can prevent anything that does not match that white-list from inclusion into the query by doing the following:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
fields = params[:field].map {|k,v| <span style="background-color:yellow">Analytics.parse_field(k)</span> }.join(",")
end
if valid_fields.include?(field)
field
else
"1"
end
end
</pre>
<p class="desc">
When used properly, this method prevents anything that does not match those items in the <i>valid_fields</i> array from reaching the SQL query. For example, within the admin controller we can prevent anything that does not match that white-list from inclusion into the query by doing the following:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
fields = params[:field].map {|k,v| <span style="background-color:yellow">Analytics.parse_field(k)</span> }.join(",")
end
if params[:ip]
@analytics = Analytics.hits_by_ip(params[:ip], fields)
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<p class="desc">
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
</p>
if params[:ip]
@analytics = Analytics.hits_by_ip(params[:ip], fields)
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<p class="desc">
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -166,6 +166,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -18,7 +18,7 @@
<div class="accordion-inner">
<p class="desc">
Virtually every application has these issues because most development teams dont focus on ensuring their components/libraries are up to date. In many cases, the developers dont even know all the components they are using, never mind their versions. Component dependencies make things even worse.
</p>
</p>
</div>
</div>
</div>
@@ -33,16 +33,16 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the Gemfile the following gem versions are set. These versions of Rails and Rack are both vulnerable to multiple attacks.
</p>
<pre class="ruby">
<%= %q{
Within the Gemfile the following gem versions are set. These versions of Rails and Rack are both vulnerable to multiple attacks.
</p>
<pre class="ruby">
<%= %q{
gem 'rails', '3.2.11'
gem 'rack', '1.4.3'
} %>
</pre>
<p class="desc">
</p>
} %>
</pre>
<p class="desc">
</p>
</div>
</div>
</div>
@@ -56,13 +56,13 @@
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
To fix this issue, simply update your gems after unpinning the gem versions. You should always run the most up to date version possible and run Bundler-Audit Regularly.
</p>
<p class="desc">
To fix this issue, simply update your gems after unpinning the gem versions. You should always run the most up to date version possible and run Bundler-Audit Regularly.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -76,6 +76,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,7 +16,7 @@
</div>
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
<div class="accordion-inner">
JQuery Snippet contains at least one DOM-Based XSS vulnerability that can be confirmed in IE11. Unknowingly, the Railsgoat development team used this library. Credit for vulnerability discovery as well as submission to <%= link_to "@raesene", "http://github.com/raesene", {:style => "color: rgb(181, 121, 158)", :target => "_blank"}%>. This was unintentional but goes to show how easily vulnerabilities can creep in when using third-party libraries.
JQuery Snippet contains at least one DOM-Based XSS vulnerability that can be confirmed in IE11. Unknowingly, the Railsgoat development team used this library. Credit for vulnerability discovery as well as submission to <%= link_to "@raesene", "http://github.com/raesene", {:style => "color: rgb(181, 121, 158)", :target => "_blank"}%>. This was unintentional but goes to show how easily vulnerabilities can creep in when using third-party libraries.
</div>
</div>
</div>
@@ -30,33 +30,33 @@
</div>
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the file app/assets/javascripts/jquery.snippet.js:
</p>
<pre>
<%= %{
<p class="desc">
Within the file app/assets/javascripts/jquery.snippet.js:
</p>
<pre>
<%= %{
// snippet new window popup function
function snippetPopup(content) \{
top.consoleRef=window.open('','myconsole',
'width=600,height=300'
+',left=50,top=50'
+',menubar=0'
+',toolbar=0'
+',location=0'
+',status=0'
+',scrollbars=1'
+',resizable=1');
top.consoleRef.document.writeln(
'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">location.href</span><%= %{+'</title></head>'
+'<body bgcolor=white onLoad="self.focus()">'
+'<pre>'+content+'</pre>'
+'</body></html>'
);
top.consoleRef.document.close();
top.consoleRef=window.open('','myconsole',
'width=600,height=300'
+',left=50,top=50'
+',menubar=0'
+',toolbar=0'
+',location=0'
+',status=0'
+',scrollbars=1'
+',resizable=1');
top.consoleRef.document.writeln(
'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">location.href</span><%= %{+'</title></head>'
+'<body bgcolor=white onLoad="self.focus()">'
+'<pre>'+content+'</pre>'
+'</body></html>'
);
top.consoleRef.document.close();
\}}%></pre>
<p class="desc">
We can see that the <i>location.href</i> DOM property is used to dynamically generate a title for the text box pop-up. This value is string concatenated directly from the DOM without first performing some escaping routine or HTML encoding.
</p>
<p class="desc">
We can see that the <i>location.href</i> DOM property is used to dynamically generate a title for the text box pop-up. This value is string concatenated directly from the DOM without first performing some escaping routine or HTML encoding.
</p>
</div>
</div>
</div>
@@ -70,27 +70,27 @@ function snippetPopup(content) \{
</div>
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
<div class="accordion-inner">
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - ATTACK</b></p>
<p class="desc">
In order to demonstrate that you can indeed perform DOM XSS through this coding error, we will use a simple alert box. This does not appear to work in Chrome, Safari, or Firefox as they first URL encoded the script portion of the url before rendering which complicates browser interpretation. IE on the other hand, true to form, is totally vulnerable. The following example assumes you are running Railsgoat on localhost, port 3000. If this is the case, open IE, paste the URL (below) into IE.
</p>
<pre>
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - ATTACK</b></p>
<p class="desc">
In order to demonstrate that you can indeed perform DOM XSS through this coding error, we will use a simple alert box. This does not appear to work in Chrome, Safari, or Firefox as they first URL encoded the script portion of the url before rendering which complicates browser interpretation. IE on the other hand, true to form, is totally vulnerable. The following example assumes you are running Railsgoat on localhost, port 3000. If this is the case, open IE, paste the URL (below) into IE.
</p>
<pre>
<%= "http://localhost:3000/tutorials/injection#</title></head><script>alert(1)</script>" %>
</pre>
<p class="desc">
The portion after the pound (#) symbol will close off the title and head portions of the HTML and then allow for properly generated JavaScript to be rendered and executed. After browsing to this URL, navigate to the tutorial where code snippets are shown and click on the "pop-up" link that appears after hovering over the code snippet. This should be all that is required to demonstrate DOM-XSS.
</p>
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - SOLUTION</b></p>
<p class="desc">
Use the hoganEscape() function defined in application.js to solve this problem. For instance:
</p>
<pre class="javascript">
</pre>
<p class="desc">
The portion after the pound (#) symbol will close off the title and head portions of the HTML and then allow for properly generated JavaScript to be rendered and executed. After browsing to this URL, navigate to the tutorial where code snippets are shown and click on the "pop-up" link that appears after hovering over the code snippet. This should be all that is required to demonstrate DOM-XSS.
</p>
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - SOLUTION</b></p>
<p class="desc">
Use the hoganEscape() function defined in application.js to solve this problem. For instance:
</p>
<pre class="javascript">
<%=%{'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">hoganEscape(location.href)</span> <%=%{+'</title></head>' }%>
</pre>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -104,6 +104,6 @@ function snippetPopup(content) \{
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
Applications frequently use the actual name or key of an object when generating web pages. Applications dont always verify the user is authorized for the target object. This results in an insecure direct object reference flaw. Testers can easily manipulate parameter values to detect such flaws. Code analysis quickly shows whether authorization is properly verified.
</p>
Applications frequently use the actual name or key of an object when generating web pages. Applications dont always verify the user is authorized for the target object. This results in an insecure direct object reference flaw. Testers can easily manipulate parameter values to detect such flaws. Code analysis quickly shows whether authorization is properly verified.
</p>
</div>
</div>
</div>
@@ -33,26 +33,26 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the app/controllers/work_info_controller.rb file the follow code can be found:
</p>
<pre class="ruby">
<%= %q{
class WorkInfoController < ApplicationController
Within the app/controllers/work_info_controller.rb file the follow code can be found:
</p>
<pre class="ruby">
<%= %q{
class WorkInfoController < ApplicationController
def index
@user = User.find_by_user_id(params[:user_id])
if !(@user)
flash[:error] = "Sorry, no user with that user id exists"
redirect_to home_dashboard_index_path
end
end
def index
@user = User.find_by_user_id(params[:user_id])
if !(@user)
flash[:error] = "Sorry, no user with that user id exists"
redirect_to home_dashboard_index_path
end
end
end
} %>
</pre>
<p class="desc">
Instead of using the current_user object which, takes the user ID value from the user's session and is normally resilient against tampering, the user ID is pulled from the request parameter (user id in the RESTful URL). Additionally, even in the session, User IDs should be sufficiently random and the sessions stored in a persistent manner (ActiveRcord) versus using the Base64 encoded / HMAC validation session schema.
</p>
end
} %>
</pre>
<p class="desc">
Instead of using the current_user object which, takes the user ID value from the user's session and is normally resilient against tampering, the user ID is pulled from the request parameter (user id in the RESTful URL). Additionally, even in the session, User IDs should be sufficiently random and the sessions stored in a persistent manner (ActiveRcord) versus using the Base64 encoded / HMAC validation session schema.
</p>
</div>
</div>
</div>
@@ -67,27 +67,27 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Insecure Direct Object Reference - ATTACK</b></p>
<p class="desc">
Navigate to the work info page, observe your user ID in the URL /users/<%= "<:user id>"%>/work_info.
Now change it to someone else's user ID.<br/><br/> Example - /users/2/work_info
</p>
<p><b>Insecure Direct Object Reference - SOLUTION</b></p>
<p class="desc">
The easiest way to fix this is to reference the current_user object. Also, it might make sense to not disclose any more sensitive information than necessary (re: error message).
</p>
<pre class="ruby">
def index
<span style="background-color:yellow">@user = current_user</span>
if !(@user) || @user.admin
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
redirect_to home_dashboard_index_path
end
end
</pre>
<p class="desc">
Navigate to the work info page, observe your user ID in the URL /users/<%= "<:user id>"%>/work_info.
Now change it to someone else's user ID.<br/><br/> Example - /users/2/work_info
</p>
<p><b>Insecure Direct Object Reference - SOLUTION</b></p>
<p class="desc">
The easiest way to fix this is to reference the current_user object. Also, it might make sense to not disclose any more sensitive information than necessary (re: error message).
</p>
<pre class="ruby">
def index
<span style="background-color:yellow">@user = current_user</span>
if !(@user) || @user.admin
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
redirect_to home_dashboard_index_path
end
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -101,6 +101,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
Regular expressions are a common way to extract the data you want from the data you do <b><u>not</u></b> want. It is common for Ruby developers to forget that in Ruby regexp anchors are \A and \z. This allows strict enforcement so that potentially dangerous characters such as the newline character aren't able to bypass security-based regular expression checks.
</p>
Regular expressions are a common way to extract the data you want from the data you do <b><u>not</u></b> want. It is common for Ruby developers to forget that in Ruby regexp anchors are \A and \z. This allows strict enforcement so that potentially dangerous characters such as the newline character aren't able to bypass security-based regular expression checks.
</p>
</div>
</div>
</div>
@@ -33,69 +33,69 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within the file app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
before_filter :valid_api_token
before_filter :extrapolate_user
</pre>
<p class="desc">
The above two lines specify that we will run these validations prior to allowing a user to interact with the API endpoints.
</p>
<pre class="ruby">
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
Within the file app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
before_filter :valid_api_token
before_filter :extrapolate_user
</pre>
<p class="desc">
The above two lines specify that we will run these validations prior to allowing a user to interact with the API endpoints.
</p>
<pre class="ruby">
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
<span style="background-color:yellow">id = $1</span>
hash = $2
(id && hash) ? true : false
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
<span style="background-color:yellow">id = $1</span>
hash = $2
(id && hash) ? true : false
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
end
def check_hash(id, hash)
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
hash == digest
end
def check_hash(id, hash)
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
end
</pre>
<p class="desc">
This first validation, <i>valid_api_token</i>, extracts the user's access token. Within the token there is a user ID and a hash. The application extracts both values, hashes the user ID and the application's secret salt together. If the digest hash matches with the user provided hash, the entire token is valid. <br/><br/>Meaning, if the hash (<i>check_hash</i>) doesn't match the hash provided by the user, the token is invalid and therefore unauthorized. Alternatively, the hash provided is valid but the user ID is invalid. <br/><br/>The next validation, built after this check, extrapolates the user from that hash. In theory, because we have already validated both the user ID and hash are valid, we can just extract the user ID from what has been provided and determine user access.
</p>
<pre class="ruby">
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
end
</pre>
<p class="desc">
Unfortunately, we've made a mistake. The regular expression can be bypassed by entering a newline character (url encoded: <i>%0a</i>).We meant or expected for a user to enter a token such as:
</p>
<pre>
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
However, the user actually enters:
</p>
<pre>
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
This means that our token will pass the initial hash check. Additionally, when we perform the split by the hyphen (<i>"-"</i>) character, and retrieve the first value from the newly created array (what should be a valid user ID), it will be <i>"2\n1"</i>. When performing a <i>find_by_*</i>, ActiveRecord will ignore everything from the newline character on and return the result of the first character. This means, we can become another user!
</p>
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
end
</pre>
<p class="desc">
This first validation, <i>valid_api_token</i>, extracts the user's access token. Within the token there is a user ID and a hash. The application extracts both values, hashes the user ID and the application's secret salt together. If the digest hash matches with the user provided hash, the entire token is valid. <br/><br/>Meaning, if the hash (<i>check_hash</i>) doesn't match the hash provided by the user, the token is invalid and therefore unauthorized. Alternatively, the hash provided is valid but the user ID is invalid. <br/><br/>The next validation, built after this check, extrapolates the user from that hash. In theory, because we have already validated both the user ID and hash are valid, we can just extract the user ID from what has been provided and determine user access.
</p>
<pre class="ruby">
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
end
</pre>
<p class="desc">
Unfortunately, we've made a mistake. The regular expression can be bypassed by entering a newline character (url encoded: <i>%0a</i>).We meant or expected for a user to enter a token such as:
</p>
<pre>
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
However, the user actually enters:
</p>
<pre>
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
This means that our token will pass the initial hash check. Additionally, when we perform the split by the hyphen (<i>"-"</i>) character, and retrieve the first value from the newly created array (what should be a valid user ID), it will be <i>"2\n1"</i>. When performing a <i>find_by_*</i>, ActiveRecord will ignore everything from the newline character on and return the result of the first character. This means, we can become another user!
</p>
</div>
</div>
</div>
@@ -110,97 +110,97 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Broken Regular Expression ATTACK:</b></p>
<p class="desc">
As discussed in the Bug Section (above), you can prepend the user ID of the person whose information you would like to retrieve followed by a newline character and your user's valid API token. The following is an example of what our request <i>should</i> look like:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
Content-Length: 4
</pre>
<p class="desc">
This is the response:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
X-Runtime: 0.008342
Connection: close
<p class="desc">
As discussed in the Bug Section (above), you can prepend the user ID of the person whose information you would like to retrieve followed by a newline character and your user's valid API token. The following is an example of what our request <i>should</i> look like:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
Content-Length: 4
</pre>
<p class="desc">
This is the response:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
X-Runtime: 0.008342
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
We want to access this endpoint as an admin (user ID of 1). We will change our request so that we can emulate <b>being</b> and admin by prepending 1%0a:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
Content-Length: 4
</pre>
<p class="desc">
The following is a response from the application (note - we get bonus points because as an admin we can retrieve <b> EVERYONE's</b> data):
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
X-Runtime: 0.009111
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
We want to access this endpoint as an admin (user ID of 1). We will change our request so that we can emulate <b>being</b> and admin by prepending 1%0a:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
Content-Length: 4
</pre>
<p class="desc">
The following is a response from the application (note - we get bonus points because as an admin we can retrieve <b> EVERYONE's</b> data):
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
X-Runtime: 0.009111
Connection: close
[{"admin":true,"created_at":"2014-01-23T16:17:10Z","email":"admin@metacorp.com","first_name":
"Admin","id":1,"last_name":"","password":"c93ccd78b2076528346216b3b2f701e6","updated_at":"2014-01-23T16:17:10Z","user_id":1},
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino",
"password":"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"jim@metacorp.com","first_name":"Jim","id":3,"last_name":"Manico","password":
"e1eb29f815193265b57d31bb4d9de140","updated_at":"2014-01-23T16:17:10Z","user_id":3},{"admin":false,
"created_at":"2014-01-23T16:17:10Z","email":"mike@metacorp.com","first_name":"Mike","id":4,"last_name":"McCabe",
"password":"df5d9020fa0f31adc4fd279020f587c8","updated_at":"2014-01-23T16:17:10Z","user_id":4},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"ken@metacorp.com","first_name":"Ken","id":5,"last_name":"Johnson","password":
"67a2faf94e8e71113617d4b72f851bf0","updated_at":"2014-01-23T16:17:10Z","user_id":5},{"admin":null,"created_at":
"2014-03-09T13:58:28Z","email":"test1@test.com","first_name":"test","id":6,"last_name":"test","password":
"05a671c66aefea124cc08b76ea6d30bb","updated_at":"2014-03-09T13:58:28Z","user_id":6},{"admin":null,"created_at":
"2014-03-10T00:13:12Z","email":"test2@test.com","first_name":"test","id":7,"last_name":"test","password":
"91482305bacc71bd52612cce07135b77","updated_at":"2014-03-10T00:13:12Z","user_id":7}]
</pre>
<p><b> Broken Regular Expression SOLUTION:</b></p>
<p class="desc">
There are many things wrong with how we are going about doing this but, for a simple fix, you can anchor the regular expression to reject/ignore newline characters.
</p>
<pre class="ruby">
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
</pre>
[{"admin":true,"created_at":"2014-01-23T16:17:10Z","email":"admin@metacorp.com","first_name":
"Admin","id":1,"last_name":"","password":"c93ccd78b2076528346216b3b2f701e6","updated_at":"2014-01-23T16:17:10Z","user_id":1},
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino",
"password":"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"jim@metacorp.com","first_name":"Jim","id":3,"last_name":"Manico","password":
"e1eb29f815193265b57d31bb4d9de140","updated_at":"2014-01-23T16:17:10Z","user_id":3},{"admin":false,
"created_at":"2014-01-23T16:17:10Z","email":"mike@metacorp.com","first_name":"Mike","id":4,"last_name":"McCabe",
"password":"df5d9020fa0f31adc4fd279020f587c8","updated_at":"2014-01-23T16:17:10Z","user_id":4},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"ken@metacorp.com","first_name":"Ken","id":5,"last_name":"Johnson","password":
"67a2faf94e8e71113617d4b72f851bf0","updated_at":"2014-01-23T16:17:10Z","user_id":5},{"admin":null,"created_at":
"2014-03-09T13:58:28Z","email":"test1@test.com","first_name":"test","id":6,"last_name":"test","password":
"05a671c66aefea124cc08b76ea6d30bb","updated_at":"2014-03-09T13:58:28Z","user_id":6},{"admin":null,"created_at":
"2014-03-10T00:13:12Z","email":"test2@test.com","first_name":"test","id":7,"last_name":"test","password":
"91482305bacc71bd52612cce07135b77","updated_at":"2014-03-10T00:13:12Z","user_id":7}]
</pre>
<p><b> Broken Regular Expression SOLUTION:</b></p>
<p class="desc">
There are many things wrong with how we are going about doing this but, for a simple fix, you can anchor the regular expression to reject/ignore newline characters.
</p>
<pre class="ruby">
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -214,6 +214,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseCryptoOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
<p class="desc">
The Railsgoat application allows employees of Metacorp to choose the <b><i>Remember Me</b></i> option at login, which creates a cookie named <b><i>auth-token</i></b>. The encryption routine used to generate the <b><i>auth-token</i></b> allows the application to extract a user ID. When decrypted, a user ID is extracted and the user is authorized appropriately. This same encryption routine is used elsewhere in the application in a manner such that a clever attacker can generate an auth_token cookie with whatever user ID they prefer and authorize to the application as a different user.
</p>
</p>
</div>
</div>
</div>
@@ -32,63 +32,63 @@
</div>
<div class="accordion-body collapse" id="collapseCryptoTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
</p>
<pre class="ruby">
# Added a re-usable encryption routine, shouldn't be an issue!
def self.encrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
new_val = aes.update("#{val}") + aes.final
Base64.strict_encode64(new_val).encode('utf-8')
end
<p class="desc">
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
</p>
<pre class="ruby">
# Added a re-usable encryption routine, shouldn't be an issue!
def self.encrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
new_val = aes.update("#{val}") + aes.final
Base64.strict_encode64(new_val).encode('utf-8')
end
def self.decrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
decoded = Base64.strict_decode64("#{val}")
aes.update("#{decoded}") + aes.final
end
</pre>
<p class="desc">
We have placed this code under the lib directory so that we have a re-usable encryption routine. This code is used to generate a user's <b><i>auth_token</b></i> cookie responsible for authorization and access. However, we've also used this same code when encrypting a user's bank account number. This means, a user can enter in any value they would like and will receive it's encrypted equivalent back from the application. Essentially, a user has the ability to generate the auth_token cookie for any user ID and authorize as that user.<br/><br/>
Within the app/models/pay.rb file we have a before hook that will save a user's bank account number as an encrypted value:
</p>
<pre class="ruby">
# callbacks
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
def self.decrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
decoded = Base64.strict_decode64("#{val}")
aes.update("#{decoded}") + aes.final
end
</pre>
<p class="desc">
We have placed this code under the lib directory so that we have a re-usable encryption routine. This code is used to generate a user's <b><i>auth_token</b></i> cookie responsible for authorization and access. However, we've also used this same code when encrypting a user's bank account number. This means, a user can enter in any value they would like and will receive it's encrypted equivalent back from the application. Essentially, a user has the ability to generate the auth_token cookie for any user ID and authorize as that user.<br/><br/>
Within the app/models/pay.rb file we have a before hook that will save a user's bank account number as an encrypted value:
</p>
<pre class="ruby">
# callbacks
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
def encrypt_bank_account_num
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
end
</pre>
<p class="desc">
Additionally, we render that encrypted value (purposefully) when the <b><i>show</i></b> action is created within the app/controllers/pay_controller.rb file:
</p>
<pre class="ruby">
def show
respond_to do |format|
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
end
end
</pre>
<p class="desc">
Lastly, we re-use this same routine within the following code is used to create a user's <b><i>auth_token</b></i> cookie upon sign-up or creation (app/models/user.rb):
</p>
<pre class="ruby">
before_create { generate_token(:auth_token) }
def encrypt_bank_account_num
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
end
</pre>
<p class="desc">
Additionally, we render that encrypted value (purposefully) when the <b><i>show</i></b> action is created within the app/controllers/pay_controller.rb file:
</p>
<pre class="ruby">
def show
respond_to do |format|
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
end
end
</pre>
<p class="desc">
Lastly, we re-use this same routine within the following code is used to create a user's <b><i>auth_token</b></i> cookie upon sign-up or creation (app/models/user.rb):
</p>
<pre class="ruby">
before_create { generate_token(:auth_token) }
def generate_token(column)
begin
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
end while User.exists?(column => self[column])
end
</pre>
def generate_token(column)
begin
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
end while User.exists?(column => self[column])
end
</pre>
</div>
</div>
</div>
@@ -103,17 +103,17 @@
<div class="accordion-body collapse" id="collapseCryptoThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Insecure Encryption Re-use ATTACK:</b></p>
<p class="desc">
Navigate to the <b><i>Pay</b></i> section of the application. Enter your bank account number but use the number <b><i>1</i></b> as your bank account number. Once the information is entered and submitted, you'll see the encrypted value of your bank account number (1) returned. URL encode the special characters (+ and ==) and use this value as your <b><i>auth_token</i></b> cookie. Navigate to your dashboard and you'll have the ability to access administrative functionality.
</p>
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
<p class="desc">
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
</p>
<p class="desc">
Navigate to the <b><i>Pay</b></i> section of the application. Enter your bank account number but use the number <b><i>1</i></b> as your bank account number. Once the information is entered and submitted, you'll see the encrypted value of your bank account number (1) returned. URL encode the special characters (+ and ==) and use this value as your <b><i>auth_token</i></b> cookie. Navigate to your dashboard and you'll have the ability to access administrative functionality.
</p>
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
<p class="desc">
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseCryptoFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -127,6 +127,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The application allows the admin attribute of a User model to be set through a mass assignment call. This vulnerability exists because a developer has indicated it is acceptable to set or change the admin value through the use of the attr_accessible setting. Any action that uses mass assignment to create a user or modify a user's settings is susceptible to this attack which would allow vertical privilege escalation.
</p>
The application allows the admin attribute of a User model to be set through a mass assignment call. This vulnerability exists because a developer has indicated it is acceptable to set or change the admin value through the use of the attr_accessible setting. Any action that uses mass assignment to create a user or modify a user's settings is susceptible to this attack which would allow vertical privilege escalation.
</p>
</div>
</div>
</div>
@@ -33,29 +33,29 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
The bug is introduced within app/models/user.rb, seen on line 3 (:admin):
</p>
<p>
<pre class="ruby">
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :admin, :password_confirmation, :first_name, :last_name
} %>
</pre>
</p>
<p class="desc">
Any attribute added to the attr_accessible setting can be used during a mass assignment call. What this means is that conceptually, the following is allowed:
</p>
<pre class="ruby">
# Note the string "true"/"false" or 1/0, etc. can be added to specify the boolean attribute...
# is true or false thanks to ActiveRecord
User.new(:email => "email@email.com",
:admin => "true",
:password => "h4xx0r",
:first_name => "Captain",
:last_name => "Crunch"
)
</pre>
The bug is introduced within app/models/user.rb, seen on line 3 (:admin):
</p>
<p>
<pre class="ruby">
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :admin, :password_confirmation, :first_name, :last_name
} %>
</pre>
</p>
<p class="desc">
Any attribute added to the attr_accessible setting can be used during a mass assignment call. What this means is that conceptually, the following is allowed:
</p>
<pre class="ruby">
# Note the string "true"/"false" or 1/0, etc. can be added to specify the boolean attribute...
# is true or false thanks to ActiveRecord
User.new(:email => "email@email.com",
:admin => "true",
:password => "h4xx0r",
:first_name => "Captain",
:last_name => "Crunch"
)
</pre>
</div>
</div>
</div>
@@ -69,67 +69,67 @@
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Mass Assignment ATTACK:</b></p>
<p class="desc">
Through the use of an intercepting proxy, we are able to capture our form submission after entering our information on the sign up page. The request looks like this...
</p>
<pre class="ruby">
POST /users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/signup
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 248
<p><b> Mass Assignment ATTACK:</b></p>
<p class="desc">
Through the use of an intercepting proxy, we are able to capture our form submission after entering our information on the sign up page. The request looks like this...
</p>
<pre class="ruby">
POST /users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/signup
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 248
utf8=✓&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit
</pre>
<p>
...and the attack is quite simple. Append a parameter to the body of this POST request that specifies the admin value is true.
</p>
<pre class="ruby"> utf8=✓&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit&<span style="background-color: yellow">user[admin]=true</span>
</pre>
<p class="desc">
So when the request is received by the create method within the user controller (code shown below), the admin attribute is set to true upon user creation.
</p>
<pre class="ruby">
def create
<span style="background-color: yellow">user = User.new(params[:user])</span>
user.build_retirement(POPULATE_RETIREMENTS.shuffle.first)
user.build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
user.build_work_info(POPULATE_WORK_INFO.shuffle.first)
user.performance.build(POPULATE_PERFORMANCE.shuffle.first)
if user.save
session[:user_id] = user.user_id
redirect_to home_dashboard_index_path
else
@user = user
render :new
end
end
</pre>
<p class="desc">
The last thing to mention here is that this can be done either through the signup page or when you edit your account settings.
</p>
<p><b> Mass Assignment SOLUTION:</b></p>
<p class="desc">
The solution is fairly simple, remove the admin attribute from the attr_accessible method. The following code shows what we mean:
</p>
<pre class="ruby">
<span style="background-color:yellow"># Note that the admin attr has been removed </span>
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
} %>
</pre>
utf8=✓&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit
</pre>
<p>
...and the attack is quite simple. Append a parameter to the body of this POST request that specifies the admin value is true.
</p>
<pre class="ruby"> utf8=✓&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit&<span style="background-color: yellow">user[admin]=true</span>
</pre>
<p class="desc">
So when the request is received by the create method within the user controller (code shown below), the admin attribute is set to true upon user creation.
</p>
<pre class="ruby">
def create
<span style="background-color: yellow">user = User.new(params[:user])</span>
user.build_retirement(POPULATE_RETIREMENTS.shuffle.first)
user.build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
user.build_work_info(POPULATE_WORK_INFO.shuffle.first)
user.performance.build(POPULATE_PERFORMANCE.shuffle.first)
if user.save
session[:user_id] = user.user_id
redirect_to home_dashboard_index_path
else
@user = user
render :new
end
end
</pre>
<p class="desc">
The last thing to mention here is that this can be done either through the signup page or when you edit your account settings.
</p>
<p><b> Mass Assignment SOLUTION:</b></p>
<p class="desc">
The solution is fairly simple, remove the admin attribute from the attr_accessible method. The following code shows what we mean:
</p>
<pre class="ruby">
<span style="background-color:yellow"># Note that the admin attr has been removed </span>
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
} %>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -139,12 +139,12 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Did you register your account correctly? How about when you updated your settings?
</p>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The constantize method is a Rails MetaProgramming method designed to dynamically find a constant that matches the string specified. This is often used to dynamically instantiate a class or module. When user-supplied input is a part of that equation, great precautions must be taken to ensure security holes are not introduced.
</p>
The constantize method is a Rails MetaProgramming method designed to dynamically find a constant that matches the string specified. This is often used to dynamically instantiate a class or module. When user-supplied input is a part of that equation, great precautions must be taken to ensure security holes are not introduced.
</p>
</div>
</div>
</div>
@@ -33,22 +33,22 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within the file app/controllers/benefit_forms_controller.rb:
</p>
<pre class="ruby">
def download
begin
<span style="background-color:yellow">path = Rails.root.join('public', 'docs', params[:name])</span>
<span style="background-color:yellow">file = params[:type].constantize.new(path)</span>
send_file file, :disposition => 'attachment'
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<p class="desc">
The location of the file to render is dynamically generated based on user input (params[:name]). This means the user controls the location of the file to be retrieved. Additionally, the params[:type] (File) is not validated to make sure it matches up with expected values.
</p>
Within the file app/controllers/benefit_forms_controller.rb:
</p>
<pre class="ruby">
def download
begin
<span style="background-color:yellow">path = Rails.root.join('public', 'docs', params[:name])</span>
<span style="background-color:yellow">file = params[:type].constantize.new(path)</span>
send_file file, :disposition => 'attachment'
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<p class="desc">
The location of the file to render is dynamically generated based on user input (params[:name]). This means the user controls the location of the file to be retrieved. Additionally, the params[:type] (File) is not validated to make sure it matches up with expected values.
</p>
</div>
</div>
</div>
@@ -63,55 +63,55 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Constantize ATTACK:</b></p>
<p class="desc">
In order to attack this weakness, navigate to the benefit forms page and observe the link to download either the health or dental documents.
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
</pre>
<p>
Change the name parameter to something a little more fun like:
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=../../config/initializers/secret_token.rb&type=File
</pre>
<p class="desc">
This second request string specifies to navigate back two directories and then look for config/intiializers/secret_token.rb. It is important to note, even when Rails.root.join is used, leveraging path traversal (ex: ../../) allows the attacker to retrieve any file that the application's user has permissions to.<br/><br/> Example:
</p>
<pre class="ruby">
../../../../../../../etc/passwd&type=File
</pre>
<p><b> Constantize SOLUTION:</b></p>
<p class="desc">
In this instance and as always, there are multiple ways to fix this. A simple method to secure this function by validating user input is as follows:
</p>
<pre class="ruby">
# More secure version
def download
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
begin
<span style="background-color:yellow">if file_assoc.has_key?(params[:name].to_s)</span>
path = Rails.root.join('public', 'docs', file_assoc[params[:name].to_s])
<span style="background-color:yellow">if params[:type] == "File"</span>
file = params[:type].constantize.new(path)
send_file file, :disposition => 'attachment'
end
else
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
send_file file, :disposition => 'attachment'
end
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<p class="desc">
The fix ultimately boils down to leveraging a hash, if the hash has the key provided by the user, the value associated with that key is the name of the file to be returned.
</p>
<p class="desc">
In order to attack this weakness, navigate to the benefit forms page and observe the link to download either the health or dental documents.
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
</pre>
<p>
Change the name parameter to something a little more fun like:
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=../../config/initializers/secret_token.rb&type=File
</pre>
<p class="desc">
This second request string specifies to navigate back two directories and then look for config/intiializers/secret_token.rb. It is important to note, even when Rails.root.join is used, leveraging path traversal (ex: ../../) allows the attacker to retrieve any file that the application's user has permissions to.<br/><br/> Example:
</p>
<pre class="ruby">
../../../../../../../etc/passwd&type=File
</pre>
<p><b> Constantize SOLUTION:</b></p>
<p class="desc">
In this instance and as always, there are multiple ways to fix this. A simple method to secure this function by validating user input is as follows:
</p>
<pre class="ruby">
# More secure version
def download
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
begin
<span style="background-color:yellow">if file_assoc.has_key?(params[:name].to_s)</span>
path = Rails.root.join('public', 'docs', file_assoc[params[:name].to_s])
<span style="background-color:yellow">if params[:type] == "File"</span>
file = params[:type].constantize.new(path)
send_file file, :disposition => 'attachment'
end
else
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
send_file file, :disposition => 'attachment'
end
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<p class="desc">
The fix ultimately boils down to leveraging a hash, if the hash has the key provided by the user, the value associated with that key is the name of the file to be returned.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -125,6 +125,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -63,7 +63,7 @@
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -77,6 +77,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -61,7 +61,7 @@
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -75,6 +75,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,13 +16,13 @@
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
<p class="desc">
Applications frequently redirect users to other pages, or use internal forwards in a similar manner. Sometimes the target page is specified in an unvalidated parameter, allowing attackers to choose the destination page.
Detecting unchecked redirects is easy. Look for redirects where you can set the full URL. Unchecked forwards are harder, because they target internal pages.
</p>
<p class="desc">
</p>
<p class="desc">
Railsgoat allows the redirection to the paths previously requested but for which the user did not have access. Following authentication, the user is redirected.
</p>
</p>
</div>
</div>
</div>
@@ -36,30 +36,30 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
The application performs zero validation of the path for which they will redirect users, following authentication. The URL parameter is used to determine where to redirect the user, if the url parameter is not present, the user will be redirect to their home page.
</p>
<pre class="ruby">
def create
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
rescue Exception => e
end
<p class="desc">
The application performs zero validation of the path for which they will redirect users, following authentication. The URL parameter is used to determine where to redirect the user, if the url parameter is not present, the user will be redirect to their home page.
</p>
<pre class="ruby">
def create
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
rescue Exception => e
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to path
else
# Removed this code, just doesn't seem specific enough!
# flash[:error] = "Either your username and password is incorrect"
flash[:error] = e.message
render "new"
end
end
</pre>
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to path
else
# Removed this code, just doesn't seem specific enough!
# flash[:error] = "Either your username and password is incorrect"
flash[:error] = e.message
render "new"
end
end
</pre>
</div>
</div>
@@ -75,29 +75,29 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Unvalidated Redirects and Forwards - ATTACK</b></p>
<p class="desc">
Ensure you are logged out of the application. When requesting the login page, ensure you append a url=<your test url here>. Then, authenticate to the application. Once authenticated, you should be redirected to your test url.
</p>
<p><b>Unvalidated Redirects and Forwards - SOLUTION</b></p>
<p class="desc">
To fix this vulnerability, validate the path. In our case, we really only want to redirect users to our site so the TLD is not important. In this case, leveraging URI.parse() can be incredibly helpful. We can change the code to something like:
</p>
<pre class="ruby">
path = home_dashboard_index_path
begin
if params[:url].present?
path = URI.parse(params[:url]).path
end
rescue
end
</pre>
<p class="desc">
Further validation can occur with regular expression. If you must redirect to another application, remember to use URI.parse() and the host, path, and scheme (ssl or not) options FIRST, prior to performing regular expression validation. Additionally, always open and close your validation regexp using Ruby anchor tags \A and \z.
</p>
<p class="desc">
Ensure you are logged out of the application. When requesting the login page, ensure you append a url=<your test url here>. Then, authenticate to the application. Once authenticated, you should be redirected to your test url.
</p>
<p><b>Unvalidated Redirects and Forwards - SOLUTION</b></p>
<p class="desc">
To fix this vulnerability, validate the path. In our case, we really only want to redirect users to our site so the TLD is not important. In this case, leveraging URI.parse() can be incredibly helpful. We can change the code to something like:
</p>
<pre class="ruby">
path = home_dashboard_index_path
begin
if params[:url].present?
path = URI.parse(params[:url]).path
end
rescue
end
</pre>
<p class="desc">
Further validation can occur with regular expression. If you must redirect to another application, remember to use URI.parse() and the host, path, and scheme (ssl or not) options FIRST, prior to performing regular expression validation. Additionally, always open and close your validation regexp using Ruby anchor tags \A and \z.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -107,12 +107,12 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Read the description section, fairly big hint there.
</p>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
Applications frequently fail to authenticate, encrypt, and protect the confidentiality and integrity of sensitive network traffic. When they do, they sometimes support weak algorithms, use expired or invalid certificates, or do not use them correctly.
</p>
Applications frequently fail to authenticate, encrypt, and protect the confidentiality and integrity of sensitive network traffic. When they do, they sometimes support weak algorithms, use expired or invalid certificates, or do not use them correctly.
</p>
</div>
</div>
</div>
@@ -33,8 +33,8 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
The application currently does not use SSL (this is not the bug). Once it does, we will show the bug. For now, check out the solution section.
</p>
The application currently does not use SSL (this is not the bug). Once it does, we will show the bug. For now, check out the solution section.
</p>
</div>
</div>
</div>
@@ -49,21 +49,21 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
In order to enforce transport layer security and ensure all requests are made over SSL, navigate to the environment file that matches the environment you would like to apply this to and add:
</p>
<pre class="ruby">
config.force_ssl = true
</pre>
<p class="desc">
To protect sessions from being sent over non-encrypted channels, mark your cookies with the secure flag. Under config/initializers/session_store.rb added the following option (highlighted):
</p>
<pre class="ruby">
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
</pre>
In order to enforce transport layer security and ensure all requests are made over SSL, navigate to the environment file that matches the environment you would like to apply this to and add:
</p>
<pre class="ruby">
config.force_ssl = true
</pre>
<p class="desc">
To protect sessions from being sent over non-encrypted channels, mark your cookies with the secure flag. Under config/initializers/session_store.rb added the following option (highlighted):
</p>
<pre class="ruby">
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -31,15 +31,15 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Rails provides the ability to apply before_filter(s) which run prior to rendering content to the user. This is helpful when restricting access to content based on the user's role. Currently, the methods to apply a before_filter already exist in the application controller but were forgotten when creating the administrative functionality. Notice an asbsence of the before_filter within app/controllers/admin_controller.rb
</p>
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
Rails provides the ability to apply before_filter(s) which run prior to rendering content to the user. This is helpful when restricting access to content based on the user's role. Currently, the methods to apply a before_filter already exist in the application controller but were forgotten when creating the administrative functionality. Notice an asbsence of the before_filter within app/controllers/admin_controller.rb
</p>
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
skip_before_filter :has_info
} %>
</pre>
skip_before_filter :has_info
} %>
</pre>
</div>
</div>
@@ -55,40 +55,40 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Failure to Restrict URL Access - ATTACK</b></p>
<p class="desc">
Request the following URL /admin/1/dashboard and have fun :-)
</p>
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
<p class="desc">
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb:
</p>
<pre class="ruby">
helper_method :current_user, <span style="background-color:yellow">:is_admin?</span>
<p class="desc">
Request the following URL /admin/1/dashboard and have fun :-)
</p>
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
<p class="desc">
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb:
</p>
<pre class="ruby">
helper_method :current_user, <span style="background-color:yellow">:is_admin?</span>
def is_admin?
current_user.admin if current_user
end
def is_admin?
current_user.admin if current_user
end
def administrative
if not is_admin?
reset_session
redirect_to root_url
end
end
</pre>
<p>
Then add the following line within app/controllers/admin_controller.rb
</p>
<pre class="ruby">
class AdminController < ApplicationController
def administrative
if not is_admin?
reset_session
redirect_to root_url
end
end
</pre>
<p>
Then add the following line within app/controllers/admin_controller.rb
</p>
<pre class="ruby">
class AdminController < ApplicationController
<span style="background-color:yellow">before_filter :administrative</span>
skip_before_filter :has_info
</pre>
<span style="background-color:yellow">before_filter :administrative</span>
skip_before_filter :has_info
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -102,6 +102,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -11,7 +11,7 @@
<a href="#collapseFive" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
@@ -33,30 +33,30 @@
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
<div class="accordion-inner">
<p>
The following code was taken from app/views/sessions/new.html.erb:
</p>
<pre class="javascript">
<%=
%{
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" +} %> <span style="background-color:yellow"> paramValue</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
The following code was taken from app/views/sessions/new.html.erb:
</p>
<pre class="javascript">
<%=
%{
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" +} %> <span style="background-color:yellow"> paramValue</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
</script>
}
%>
</pre>
<p class="desc">
The code (above) takes user input (params), and renders it back on the page without any output encoding or escaping.
</p>
}
%>
</pre>
<p class="desc">
The code (above) takes user input (params), and renders it back on the page without any output encoding or escaping.
</p>
</div>
</div>
</div>
@@ -71,40 +71,40 @@
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
<div class="accordion-inner">
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
<p class="desc">
Ensure you are signed out of the application first. Make sure you are using something like Firefox as Safari/Chrome won't work for this exercise. Then, use the following link (substitute hostname for your actual hostname) to execute an alert box:
</p>
<pre>
<%= %{http://127.0.0.1:3000/#test=<script>alert(1)</script>} %>
</pre>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
<p>
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
</p>
<pre class="javascript">
<%= %{
<!-- support for multiple languages coming soon! -->
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" + } %> <span style="background-color:yellow"> hoganEscape(paramValue)</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
</script>
}
<p class="desc">
Ensure you are signed out of the application first. Make sure you are using something like Firefox as Safari/Chrome won't work for this exercise. Then, use the following link (substitute hostname for your actual hostname) to execute an alert box:
</p>
<pre>
<%= %{http://127.0.0.1:3000/#test=<script>alert(1)</script>} %>
</pre>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
<p>
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
</p>
<pre class="javascript">
<%= %{
<!-- support for multiple languages coming soon! -->
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" + } %> <span style="background-color:yellow"> hoganEscape(paramValue)</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
</script>
}
%>
</pre>
%>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -115,11 +115,11 @@
<div class="accordion-body collapse" id="collapseEight" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
You should view the source of the login page, might be something interesting there.
</p>
You should view the source of the login page, might be something interesting there.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -32,21 +32,21 @@
<div class="accordion-inner">
<p><b>Stored Cross-Site Scripting - The following code was taken from app/views/layouts/shared/_header.html.erb</b></p>
<p>
<pre class="ruby">
<%= @code %>
</pre>
<p>
<pre class="ruby">
<%= @code %>
</pre>
</p>
<p class="desc">
Coincidentally, HTML safe is not safe from HTML Injection or "XSS" attacks. The name is deceiving. Some folks believe the raw() helper to be different than the html_safe String method. raw() is actually a wrapper for html_safe and essentially ensures exceptions are handled when the expected value is nil.
<pre class="ruby">
# Psuedo-code to help conceptualize
def raw(dirty_string)
dirty_string.to_s.html_safe
end
</pre>
<p class="desc">
Coincidentally, HTML safe is not safe from HTML Injection or "XSS" attacks. The name is deceiving. Some folks believe the raw() helper to be different than the html_safe String method. raw() is actually a wrapper for html_safe and essentially ensures exceptions are handled when the expected value is nil.
<pre class="ruby">
# Psuedo-code to help conceptualize
def raw(dirty_string)
dirty_string.to_s.html_safe
end
</pre>
</p>
</p>
</div>
</div>
@@ -63,16 +63,16 @@
<div class="accordion-inner">
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
<p> When registering, enter your JavaScript tag such as <%= %{<script>alert("ohai")</script>} %> in the First Name field. Upon login the header navigation bar will echo "Welcome" + your JS code. You can have your XSS code point the victim to a <b><%= link_to "BeEF server", "http://beefproject.com", {:style => "color: rgb(69, 126, 136)" } %></b> and have some fun as well.
</p>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
<p>
Often developers error on the side of using "html_safe" versus "raw" with the idea being one is safer than the other. In this example, simply removing the .html_safe call would both eliminate the attack (by default, Rails 3.x html encodes these dangerous chars). Rails 2.x would require that any potentially malicious content is wrapped within an h() tag. Potentially malicious content should be thought of anything that is dynamically generated. Also, it is important to note that if for some reason you wanted to render HTML code in literal form, you can use things like sanitize() or strip_tags().
</p>
<p> When registering, enter your JavaScript tag such as <%= %{<script>alert("ohai")</script>} %> in the First Name field. Upon login the header navigation bar will echo "Welcome" + your JS code. You can have your XSS code point the victim to a <b><%= link_to "BeEF server", "http://beefproject.com", {:style => "color: rgb(69, 126, 136)" } %></b> and have some fun as well.
</p>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
<p>
Often developers error on the side of using "html_safe" versus "raw" with the idea being one is safer than the other. In this example, simply removing the .html_safe call would both eliminate the attack (by default, Rails 3.x html encodes these dangerous chars). Rails 2.x would require that any potentially malicious content is wrapped within an h() tag. Potentially malicious content should be thought of anything that is dynamically generated. Also, it is important to note that if for some reason you wanted to render HTML code in literal form, you can use things like sanitize() or strip_tags().
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -83,12 +83,12 @@
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Apparently we had some issues rendering people's names with weird formatting or something, I dunno, I think I fixed it by safely encoding html and rendering the necessary content.<br/><br/>
You're <b>Welcome</b>!
</p>
Apparently we had some issues rendering people's names with weird formatting or something, I dunno, I think I fixed it by safely encoding html and rendering the necessary content.<br/><br/>
You're <b>Welcome</b>!
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
+4 -5
View File
@@ -10,7 +10,6 @@
</script>
<![endif]-->
</head>
<body>
@@ -18,10 +17,10 @@
<%= render "layouts/tutorial/sidebar" %>
<div class="container-fluid">
<div class="dashboard-wrapper">
<%= yield %>
</div>
<div class="dashboard-wrapper">
<%= yield %>
</div>
</div>
<%= render "layouts/shared/footer" %>
<%= render "layouts/shared/footer" %>
</body>
</html>
+52 -52
View File
@@ -1,8 +1,8 @@
<div class="dashboard-wrapper">
<div class="main-container">
<!-- Begin Row -->
<!-- Begin Row -->
<div class="row-fluid">
<!-- Begin Span12 -->
<!-- Begin Span12 -->
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -39,12 +39,12 @@
</div>
</div>
</div>
<!-- End Span12 -->
<!-- End Span12 -->
</div>
<!-- End Row -->
<!-- Begin Row -->
<!-- Begin Row -->
<div class="row-fluid">
<!-- Begin Span12 -->
<!-- Begin Span12 -->
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -53,15 +53,15 @@
</div>
</div>
<div class="widget-body">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Message successfully sent.
</p>
</div>
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Message successfully sent.
</p>
</div>
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -69,35 +69,35 @@
Failed to send message.
</p>
</div>
<div class="row-fluid">
<div class="span8">
<!-- Begin Message Draft Content-->
<%= form_for @message, :url => user_messages_path, :method => :post, :html => {:id => "send_message"} do |f|%>
<%= f.hidden_field :creator_id, :value => current_user.id %>
<%= f.hidden_field :read, :value => '0' %>
<div class="control-group">
<%= f.label "To:", nil, {:class => "control-label"}%>
<%= f.select(:receiver_id, options_from_collection_for_select(User.all, :id, :full_name)) %>
</div>
<div class="row-fluid">
<div class="span8">
<!-- Begin Message Draft Content-->
<%= form_for @message, :url => user_messages_path, :method => :post, :html => {:id => "send_message"} do |f|%>
<%= f.hidden_field :creator_id, :value => current_user.id %>
<%= f.hidden_field :read, :value => '0' %>
<div class="control-group">
<%= f.label "To:", nil, {:class => "control-label"}%>
<%= f.select(:receiver_id, options_from_collection_for_select(User.all, :id, :full_name)) %>
</div>
<div class="control-group">
<%= f.label :message, nil, {:class => "control-label"}%>
<%= f.text_area :message, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :message, nil, {:class => "control-label"}%>
<%= f.text_area :message, {:class => "span12"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="clearfix"></div>
<% end %>
<!-- End Message Draft Content-->
</div>
</div>
<div class="clearfix"></div>
<% end %>
<!-- End Message Draft Content-->
</div>
</div>
</div>
</div>
</div>
<!-- End Span12 -->
<!-- End Span12 -->
</div>
<!-- End Row -->
</div>
@@ -108,27 +108,27 @@
<script type="text/javascript">
$("#submit_button").click(function(event) {
var valuesToSubmit = $("#send_message").serialize();
var valuesToSubmit = $("#send_message").serialize();
event.preventDefault();
$.ajax({
url: <%= "/users/#{current_user.user_id}/messages.json".inspect.html_safe %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
function makeActive(){
$('li[id="messages"]').addClass('active');
$('li[id="messages"]').addClass('active');
};
$(document).ready(function () {
+1 -1
View File
@@ -35,7 +35,7 @@
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="messages"]').addClass('active');
$('li[id="messages"]').addClass('active');
};
$(document).ready(function () {
+88 -88
View File
@@ -1,20 +1,20 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -22,25 +22,25 @@
Failed to update.
</p>
</div>
</div>
</div>
<!-- Begin DP-->
<div class="row-fluid">
<!-- begin cal -->
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe097;"></span> PTO Calendar
</div>
</div>
<div id="calendarDiv" class="widget-body">
<div id='calendar'></div>
</div>
</div>
</div>
<!-- End cal-->
<div class="span6">
</div>
</div>
<!-- Begin DP-->
<div class="row-fluid">
<!-- begin cal -->
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe097;"></span> PTO Calendar
</div>
</div>
<div id="calendarDiv" class="widget-body">
<div id='calendar'></div>
</div>
</div>
</div>
<!-- End cal-->
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
@@ -48,46 +48,46 @@
</div>
</div>
<!-- Begin WB-->
<div id="scheduleDiv" class="widget-body">
<%= form_for @schedule, :url => "#",:html => {:id => "cal_update"} do |s|%>
<div class="control-group">
<%= s.label :event_name, "Event Name", {:class => "control-label"}%>
<%= s.text_field :event_name, {:placeholder => "My PTO", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.text_field :event_type, {:type => "hidden", :value => "pto", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.label :event_desc, "Event Description", {:class => "control-label"}%>
<%= s.text_field :event_desc, {:placeholder => "Travel to Europe", :class => "span6"}%>
</div>
<div class="control-group">
<label class="control-label" for="date_range1">
Event Dates
</label>
<div class="controls">
<div class="input-append">
<input type="text" name="date_range1" id="date_range1" class="span8 date_picker" placeholder="Select Date"/>
<span class="add-on">
<i class="icon-calendar"></i>
</span>
</div>
</div>
</div>
<%= s.submit "Submit", {:id => 'cal_update_submit', :class => "btn btn-primary pull-left"} %>
<% end %>
<div class="clearfix">
<div id="scheduleDiv" class="widget-body">
<%= form_for @schedule, :url => "#",:html => {:id => "cal_update"} do |s|%>
<div class="control-group">
<%= s.label :event_name, "Event Name", {:class => "control-label"}%>
<%= s.text_field :event_name, {:placeholder => "My PTO", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.text_field :event_type, {:type => "hidden", :value => "pto", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.label :event_desc, "Event Description", {:class => "control-label"}%>
<%= s.text_field :event_desc, {:placeholder => "Travel to Europe", :class => "span6"}%>
</div>
<div class="control-group">
<label class="control-label" for="date_range1">
Event Dates
</label>
<div class="controls">
<div class="input-append">
<input type="text" name="date_range1" id="date_range1" class="span8 date_picker" placeholder="Select Date"/>
<span class="add-on">
<i class="icon-calendar"></i>
</span>
</div>
</div>
</div>
<%= s.submit "Submit", {:id => 'cal_update_submit', :class => "btn btn-primary pull-left"} %>
<% end %>
<div class="clearfix">
</div>
</div>
<!-- End WB-->
</div>
</div>
</div>
</div>
<!-- End DP-->
<div class="row-fluid">
</div>
<!-- End DP-->
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -101,7 +101,7 @@
</div>
</div>
</div>
<div class="row-fluid">
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -115,7 +115,7 @@
</div>
</div>
</div>
</div>
</div>
</div>
<%= javascript_include_tag "fullcalendar.js" %>
@@ -125,7 +125,7 @@
function makeActive(){
$('li[id="pto"]').addClass('active');
$('li[id="pto"]').addClass('active');
};
@@ -147,7 +147,7 @@ $('.date_picker').daterangepicker({
makeActive()
});
google.load("visualization", "1", {
google.load("visualization", "1", {
packages: ["corechart"]
});
@@ -220,29 +220,29 @@ function drawChart2() {
}
function resizeTopWidgets(){
var calHeight = $("#calendarDiv").height();
var calHeight = $("#calendarDiv").height();
$("#scheduleDiv").css({'height':calHeight});
};
$("#cal_update_submit").click(function(event) {
var valuesToSubmit = $("#cal_update").serialize();
var valuesToSubmit = $("#cal_update").serialize();
event.preventDefault();
$.ajax({
url: "/schedule.json",
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
$('#calendar').fullCalendar('refetchEvents')
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
$('#calendar').fullCalendar('refetchEvents')
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
</script>
+183 -183
View File
@@ -1,20 +1,20 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -22,11 +22,11 @@
Failed to update.
</p>
</div>
</div>
</div>
<!-- Begin Row-Fluid for Inputs -->
<div class="row-fluid">
<div class="span9">
</div>
</div>
<!-- Begin Row-Fluid for Inputs -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
@@ -34,90 +34,90 @@
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
<!-- Begin inputs-->
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
<span class="add-on">%</span>
</div>
<div class="input-append">
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
<span class="add-on">%</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- End Row-Fluid for Inputs-->
</div>
<!-- End Row-Fluid for Inputs-->
<!-- ###################-->
<!-- Begin Dynamic Table ColSpan Table -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<!-- ###################-->
<!-- Begin Dynamic Table ColSpan Table -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<!-- Begin Widget Header-->
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Accounts
</div>
</div>
<!-- End Widget Header-->
<div class="widget-body">
<div id="dt_example" class="example_alt_pagination">
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
<thead>
<tr>
<th style="width:30%">
Encrypted Bank Account Number
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
<span class="box1">
<span aria-hidden="true" class="icon-question"></span>
</span>
<% end %>
</th>
<th style="width:25%">
Bank Routing Number
</th>
<th style="width:20%">
Percentage of Deposit
</th>
<th style="width:25%">
Action
</th>
</tr>
</thead>
<tbody>
<!-- Begin Widget Header-->
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Accounts
</div>
</div>
<!-- End Widget Header-->
<div class="widget-body">
<div id="dt_example" class="example_alt_pagination">
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
<thead>
<tr>
<th style="width:30%">
Encrypted Bank Account Number
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
<span class="box1">
<span aria-hidden="true" class="icon-question"></span>
</span>
<% end %>
</th>
<th style="width:25%">
Bank Routing Number
</th>
<th style="width:20%">
Percentage of Deposit
</th>
<th style="width:25%">
Action
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="clearfix">
</div>
</div>
</div> <!-- end of widget body-->
</div>
</div>
</div
<!-- End Dynamic Table ColSpan Table -->
<!-- ###################-->
</tbody>
</table>
<div class="clearfix">
</div>
</div>
</div> <!-- end of widget body-->
</div>
</div>
</div
<!-- End Dynamic Table ColSpan Table -->
<!-- ###################-->
<!-- Begin Row-Fluid for Decryption Input -->
<div class="row-fluid">
<div class="span9">
<!-- Begin Row-Fluid for Decryption Input -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
@@ -125,25 +125,25 @@
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
<!-- Begin inputs-->
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- Row-Fluid for Decryption Input -->
</div>
</div>
<!-- Row-Fluid for Decryption Input -->
</div>
</div>
<%= javascript_include_tag "jquery.dataTables.js" %>
@@ -155,60 +155,60 @@
a user to delete that direct deposit entry
*/
function buildDeleteLink(dd_id){
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
'<i class="icon-trash">'+
'</i></a>'
return link
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
'<i class="icon-trash">'+
'</i></a>'
return link
};
/*
parseDirectDepositInfo accepts the response object and parses the JSON response, then
populates the direct deposit data table.
parseDirectDepositInfo accepts the response object and parses the JSON response, then
populates the direct deposit data table.
*/
function parseDirectDepostInfo(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
$.each(msg.user, function(index, val){
$('#data_table').dataTable().fnAddData( [
val.bank_account_num,
val.bank_routing_num,
val.percent_of_deposit,
buildDeleteLink(val.id)
] );
});
var msg = jQuery.parseJSON(JSON.stringify(response));
$.each(msg.user, function(index, val){
$('#data_table').dataTable().fnAddData( [
val.bank_account_num,
val.bank_routing_num,
val.percent_of_deposit,
buildDeleteLink(val.id)
] );
});
};
/*
populateTable will first clear the existing dd table, then call the appropriate
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
with the response from the endpoint in order to populate the data table.
populateTable will first clear the existing dd table, then call the appropriate
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
with the response from the endpoint in order to populate the data table.
*/
function populateTable() {
$('#data_table').dataTable().fnClearTable();
$.ajax({
$('#data_table').dataTable().fnClearTable();
$.ajax({
url: <%= sanitize(user_pay_path(:format => "json", :user_id => current_user.user_id, :id => current_user.user_id).inspect) %>,
type: "GET",
success: function(response) {
parseDirectDepostInfo(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
type: "GET",
success: function(response) {
parseDirectDepostInfo(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
};
/*
createDataTable initializes the dd table as a datatable
createDataTable initializes the dd table as a datatable
*/
function createDataTable(){
$('#data_table').dataTable({
$('#data_table').dataTable({
"sPaginationType": "full_numbers"
});
};
/*
This function doesn't really work right now but is supposed to offer the user a
"delete confirmation" message
This function doesn't really work right now but is supposed to offer the user a
"delete confirmation" message
*/
$('.delete-row').click(function () {
var conf = confirm('Continue delete?');
@@ -219,78 +219,78 @@ $('.delete-row').click(function () {
});
/*
decryptShow parses the json response from the application and then renders
a decrypted version of the user's account number
decryptShow parses the json response from the application and then renders
a decrypted version of the user's account number
*/
function decryptShow(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
alert("Decrypted Account Number: " + msg.account_num);
var msg = jQuery.parseJSON(JSON.stringify(response));
alert("Decrypted Account Number: " + msg.account_num);
};
/*
This function overrides the decrypt buttons (submit button's) native behavior,
allowing an ajax call to be made with the decrypt_form's inputs which is decrypted
server side with a JSON response containing the decrypted value. The decrypted value is
then passed to decryptShow();
This function overrides the decrypt buttons (submit button's) native behavior,
allowing an ajax call to be made with the decrypt_form's inputs which is decrypted
server side with a JSON response containing the decrypted value. The decrypted value is
then passed to decryptShow();
*/
$("#decrypt_btn").click(function(event){
var valuesToSubmit = $("#decrypt_form").serialize();
event.preventDefault();
$.ajax({
var valuesToSubmit = $("#decrypt_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(decrypted_bank_acct_num_user_pay_index_path(:format => "json", :user_id => current_user.user_id).inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
decryptShow(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
decryptShow(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
/*
This function overrides the dd_form_btn's native behavior in order to submit an ajax request
that updates the user's direct deposit information. Upon success, the populateTable() function
is called in order to update the dataTable on the page to reflect the latest entry.
This function overrides the dd_form_btn's native behavior in order to submit an ajax request
that updates the user's direct deposit information. Upon success, the populateTable() function
is called in order to update the dataTable on the page to reflect the latest entry.
*/
$("#dd_form_btn").click(function(event) {
var valuesToSubmit = $("#bank_info_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(update_dd_info_user_pay_index_path(:format => "json").inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
populateTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
populateTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
$("#encrypted_acct_question").click(function(event) {
event.preventDefault();
alert("For your safety your account number is stored encrypted as well as presented to you \nin an encrypted form.\n\n" +
"For your convenience, you can decrypt your bank account number at any time using our\n" +
"conveniently located decryption function."
)
event.preventDefault();
alert("For your safety your account number is stored encrypted as well as presented to you \nin an encrypted form.\n\n" +
"For your convenience, you can decrypt your bank account number at any time using our\n" +
"conveniently located decryption function."
)
});
/*
Make the sidebar element "Pay" active.
Make the sidebar element "Pay" active.
*/
function makeActive(){
$('li[id="pay"]').addClass('active');
$('li[id="pay"]').addClass('active');
};
/*
1) makeActive - Adds the active class to the sidebar element
2) createDataTable - Initializes the dataTable as such
3) populateTable - populates the newly initialized dataTable
1) makeActive - Adds the active class to the sidebar element
2) createDataTable - Initializes the dataTable as such
3) populateTable - populates the newly initialized dataTable
*/
$(document).ready(
makeActive,
+37 -38
View File
@@ -1,6 +1,6 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -13,45 +13,44 @@
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe004;"></span> Performance History
</div>
</div>
<div class="widget-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width:16%">Reviewer</th>
<th style="width:16%">Date</th>
<th style="width:16%">Score</th>
<th style="width:16%">Comments</th>
</tr>
</thead>
<tbody>
<% @perf.each do |p| %>
<tr>
<td><%= p.reviewer_name %></td>
<td><%= p.date_submitted %></td>
<td><%= p.score %></td>
<td><%= p.comments %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe004;"></span> Performance History
</div>
</div>
<div class="widget-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width:16%">Reviewer</th>
<th style="width:16%">Date</th>
<th style="width:16%">Score</th>
<th style="width:16%">Comments</th>
</tr>
</thead>
<tbody>
<% @perf.each do |p| %>
<tr>
<td><%= p.reviewer_name %></td>
<td><%= p.date_submitted %></td>
<td><%= p.score %></td>
<td><%= p.comments %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
google.load("visualization", "1", {
packages: ["corechart"]
});
@@ -116,7 +115,7 @@ function drawChart2() {
}
function makeActive(){
$('li[id="performance"]').addClass('active');
$('li[id="performance"]').addClass('active');
};
$(document).ready(function () {
+75 -75
View File
@@ -1,83 +1,83 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe071;"></span> Employee Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="expenses">
<h3><%= @info.employee_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe075;"></span> Employer Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="signups">
<h3><%= @info.employer_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Total Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="income">
<h3><%= @info.total %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
</div>
<div class="row-fluid">
<div class="span6"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0d4;"></span> Employee Services
</div>
</div>
<div class="widget-body">
<p>
Saving for retirement can be difficult. Choosing the plan that is right for you is incredibly important. MetaCorp understands this and and offers free one-on-one interaction with a savings counselor. This service is available weekly, Monday thru Wednesday. Sign up through your departments designated finance lead.
</p>
<hr/>
<p>
MetaCorp is dedicated to its employees and this service is just one way of showing it!
</p>
</div>
</div>
</div> <!-- End of span-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe071;"></span> Employee Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="expenses">
<h3><%= @info.employee_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe075;"></span> Employer Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="signups">
<h3><%= @info.employer_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Total Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="income">
<h3><%= @info.total %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
</div>
<div class="row-fluid">
<div class="span6"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0d4;"></span> Employee Services
</div>
</div>
<div class="widget-body">
<p>
Saving for retirement can be difficult. Choosing the plan that is right for you is incredibly important. MetaCorp understands this and and offers free one-on-one interaction with a savings counselor. This service is available weekly, Monday thru Wednesday. Sign up through your departments designated finance lead.
</p>
<hr/>
<p>
MetaCorp is dedicated to its employees and this service is just one way of showing it!
</p>
</div>
</div>
</div> <!-- End of span-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="retirement"]').addClass('active');
};
function makeActive(){
$('li[id="retirement"]').addClass('active');
};
$(document).ready(makeActive)
+6 -8
View File
@@ -37,15 +37,13 @@
<div class="actions">
<%= link_to "Forgot Password", forgot_password_path, {:class=>"pull-left"}%><br/>
<%= submit_tag "Login", {:class => "btn btn-info btn-large pull-right"} %>
<span class="checkbox-wrapper">
<%= check_box_tag :remember_me, 1, params[:remember_me], {:id => "form-terms", :class => "checkbox", :type => "checkbox"} %>
<label class="checkbox-label" for="form-terms"></label> <span class="label-text">Remember</span>
</span>
<%= link_to "Forgot Password", forgot_password_path, {:class=>"pull-left"}%><br/>
<%= submit_tag "Login", {:class => "btn btn-info btn-large pull-right"} %>
<span class="checkbox-wrapper">
<%= check_box_tag :remember_me, 1, params[:remember_me], {:id => "form-terms", :class => "checkbox", :type => "checkbox"} %>
<label class="checkbox-label" for="form-terms"></label> <span class="label-text">Remember</span>
</span>
<div class="clearfix"></div>
<% end %>
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="access_control"]').addClass('active');
$('li[id="access_control"]').addClass('active');
};
$(document).ready(makeActive);
+18 -18
View File
@@ -1,26 +1,26 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/user_pass_enum")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/user_pass_enum")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="broken_auth"]').addClass('active');
$('li[id="broken_auth"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/csrf/csrf_first"%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/csrf/csrf_first"%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="csrf"]').addClass('active');
$('li[id="csrf"]').addClass('active');
};
$(document).ready(makeActive);
+25 -27
View File
@@ -1,36 +1,34 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Gauntlet
</div>
</div>
<div class="widget-body">
<p class="desc">
Gauntlt is a tool used for unit testing leveraging third-party tools. We've baked this into Railsgoat so that you can play with it. <br/><br/> To learn more about this tool, please visit their site at: <%= link_to "Gauntlet Github Repository", "https://github.com/gauntlt/gauntlt", {:style =>"color: rgb(181, 121, 158);"} %></p>
<p class="desc">
All *.attack files are contained under the gauntlt_scripts directory. We have provided a simple.attack file that demonstrates the tool works. If errors occur, please submit a bug through our github powered issue tracking system.
</p>
<p class="desc">
To run this tool type this via the command line: <br/><br/>$ gauntlt
</p>
</div>
</div>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Gauntlet
</div>
</div>
<div class="widget-body">
<p class="desc">
Gauntlt is a tool used for unit testing leveraging third-party tools. We've baked this into Railsgoat so that you can play with it. <br/><br/> To learn more about this tool, please visit their site at: <%= link_to "Gauntlet Github Repository", "https://github.com/gauntlt/gauntlt", {:style =>"color: rgb(181, 121, 158);"} %></p>
<p class="desc">
All *.attack files are contained under the gauntlt_scripts directory. We have provided a simple.attack file that demonstrates the tool works. If errors occur, please submit a bug through our github powered issue tracking system.
</p>
<p class="desc">
To run this tool type this via the command line: <br/><br/>$ gauntlt
</p>
</div>
</div>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function openSub(){
$('li[id="gauntlt"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="gauntlt"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(openSub);
+17 -19
View File
@@ -1,28 +1,26 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe012;"></span> Using Guard with Brakeman and Bundle-Audit
</div>
</div>
<div class="widget-body">
<iframe src="http://player.vimeo.com/video/63901340" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</div>
</div>
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe012;"></span> Using Guard with Brakeman and Bundle-Audit
</div>
</div>
<div class="widget-body">
<iframe src="http://player.vimeo.com/video/63901340" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function openSub(){
$('li[id="guard"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="guard"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(openSub);
+75 -76
View File
@@ -1,88 +1,87 @@
<div class="dashboard-wrapper">
<div class="main-container">
<h1> Welcome to RailsGoat </h1>
<h3> Tutorial Guide </h3>
<div class="main-container">
<h1> Welcome to RailsGoat </h1>
<h3> Tutorial Guide </h3>
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Step 1 - Choose your path
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Get Started
</h3>
<p class="desc">
Choose your path! Go directly into reading each tutorial or begin attacking the application to practice identifying weaknesses. <br/>You can then cross reference your findings with the findings listed under the tutorials section.
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe091;"></span> Step 2 - Using the application
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Using the application
</h3>
<p class="desc">
Each tutorial offers 4 parts:
<li style="margin-left: 20px">Description</li>
<li style="margin-left: 20px">Bug</li>
<li style="margin-left: 20px">Solution</li>
<li style="margin-left: 20px">Hint</li><br/>
These titles are fairly self-explanatory but <b>BEWARE</b>, the solution section gives away how to solve or attack that particular vulnerability instance!
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0e1;"></span> Step 3 - Attack
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h1 align="center">
HACK!
</h1>
<p class="desc">
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Step 1 - Choose your path
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Get Started
</h3>
<p class="desc">
Choose your path! Go directly into reading each tutorial or begin attacking the application to practice identifying weaknesses. <br/>You can then cross reference your findings with the findings listed under the tutorials section.
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe091;"></span> Step 2 - Using the application
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Using the application
</h3>
<p class="desc">
Each tutorial offers 4 parts:
<li style="margin-left: 20px">Description</li>
<li style="margin-left: 20px">Bug</li>
<li style="margin-left: 20px">Solution</li>
<li style="margin-left: 20px">Hint</li><br/>
These titles are fairly self-explanatory but <b>BEWARE</b>, the solution section gives away how to solve or attack that particular vulnerability instance!
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0e1;"></span> Step 3 - Attack
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h1 align="center">
HACK!
</h1>
<p class="desc">
</p>
</div>
<hr/>
</p>
</div>
<hr/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="tutorials_home"]').addClass('active');
$('li[id="tutorials_home"]').addClass('active');
};
$(document).ready(makeActive);
+18 -18
View File
@@ -1,26 +1,26 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/sqli_scope"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_command"%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/sqli_scope"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_command"%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="injection"]').addClass('active');
$('li[id="injection"]').addClass('active');
};
$(document).ready(makeActive);
@@ -1,21 +1,21 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_second" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_second" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="insecure_components"]').addClass('active');
$('li[id="insecure_components"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_dor/insecure_dor_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_dor/insecure_dor_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="insecure_dor"]').addClass('active');
$('li[id="insecure_dor"]').addClass('active');
};
$(document).ready(makeActive);
+14 -14
View File
@@ -1,23 +1,23 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="logic_flaws"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="logic_flaws"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(makeActive);
+9 -9
View File
@@ -1,17 +1,17 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/mass_assignment/admin_mass_assign")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/mass_assignment/admin_mass_assign")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="mass_assignment"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="mass_assignment"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(makeActive);
+9 -9
View File
@@ -1,17 +1,17 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/metaprogramming/benefit_forms_constantize")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/metaprogramming/benefit_forms_constantize")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="metaprogramming"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="metaprogramming"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(makeActive);
+13 -13
View File
@@ -1,21 +1,21 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="misconfig"]').addClass('active');
$('li[id="misconfig"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/redirects/redirects_first"%>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/redirects/redirects_first"%>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="redirects"]').addClass('active');
$('li[id="redirects"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/ssl_tls/ssl_tls_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/ssl_tls/ssl_tls_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="ssl_tls"]').addClass('active');
$('li[id="ssl_tls"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/url_access/url_access_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/url_access/url_access_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="url_access"]').addClass('active');
$('li[id="url_access"]').addClass('active');
};
$(document).ready(makeActive);
+13 -13
View File
@@ -1,21 +1,21 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/xss_first"%>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/dom_xss"%>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/xss_first"%>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/dom_xss"%>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="xss"]').addClass('active');
$('li[id="xss"]').addClass('active');
};
$(document).ready(makeActive);
+75 -79
View File
@@ -1,20 +1,20 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -22,58 +22,58 @@
Failed to update.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe090;"></span> Profile Settings
<span class="mini-title">
Edit your account details
</span>
</div>
</div>
<div class="widget-body">
<%= form_for @user, :html => {:id => "account_edit"} do |f|%>
<%= f.hidden_field :user_id%>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe090;"></span> Profile Settings
<span class="mini-title">
Edit your account details
</span>
</div>
</div>
<div class="widget-body">
<%= form_for @user, :html => {:id => "account_edit"} do |f|%>
<%= f.hidden_field :user_id%>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div>
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<div>
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<div class="control-group">
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="clearfix">
</div>
<% end %>
<div class="clearfix">
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<%= javascript_include_tag ('validation.js')%>
@@ -81,27 +81,23 @@
<script type="text/javascript">
$("#submit_button").click(function(event) {
var valuesToSubmit = $("#account_edit").serialize();
var valuesToSubmit = $("#account_edit").serialize();
event.preventDefault();
$.ajax({
url: <%= "/users/#{current_user.user_id}.json".inspect.html_safe %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
</script>
+21 -23
View File
@@ -1,43 +1,41 @@
<div class="row-fluid">
<div class="span12">
<div class="row-fluid">
<div class="span4 offset4">
<div class="signup">
<%= form_for @user, :html => {:id => "account_edit", :class=> "signup-wrapper"} do |f| %>
<%= form_for @user, :html => {:id => "account_edit", :class=> "signup-wrapper"} do |f| %>
<div class="header">
<h2>Sign Up</h2>
<p>Fill out the form below to login</p>
</div>
<div class="header">
<h2>Sign Up</h2>
<p>Fill out the form below to login</p>
</div>
<div class="content">
<%= f.text_field :email, {:class => "input input-block-level", :placeholder => "Email"} %>
<div class="content">
<%= f.text_field :email, {:class => "input input-block-level", :placeholder => "Email"} %>
<%= f.text_field :first_name, {:class => "input input-block-level", :placeholder => "First Name"} %>
<%= f.text_field :first_name, {:class => "input input-block-level", :placeholder => "First Name"} %>
<%= f.text_field :last_name, {:class => "input input-block-level", :placeholder => "Last Name"} %>
<%= f.text_field :last_name, {:class => "input input-block-level", :placeholder => "Last Name"} %>
<div class="control-group">
<%= f.password_field :password, {:class => "input input-block-level", :placeholder => "Password"}%>
</div>
<div class="control-group">
<%= f.password_field :password_confirmation, {:class => "input input-block-level", :placeholder => "Confirm Password"}%>
</div>
</div>
<div class="control-group">
<%= f.password_field :password, {:class => "input input-block-level", :placeholder => "Password"}%>
</div>
<div class="control-group">
<%= f.password_field :password_confirmation, {:class => "input input-block-level", :placeholder => "Confirm Password"}%>
</div>
</div>
<div class="actions">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info btn-large pull-right"} %>
</div>
<div class="clearfix"></div>
<% end %>
<div class="actions">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info btn-large pull-right"} %>
</div>
<div class="clearfix"></div>
<% end %>
</div>
</div>
</div>
</div>
</div>
+43 -45
View File
@@ -1,63 +1,61 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe088;"></span> Employee Information
</div>
</div>
<div class="widget-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width:16%">Full Name</th>
<th style="width:16%">Income</th>
<th style="width:16%">Bonuses</th>
<th style="width:16%">Years w/ MetaCorp</th>
<th style="width:16%">SSN</th>
<th style="width:16%">DoB</th>
</tr>
</thead>
<tbody>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe088;"></span> Employee Information
</div>
</div>
<div class="widget-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width:16%">Full Name</th>
<th style="width:16%">Income</th>
<th style="width:16%">Bonuses</th>
<th style="width:16%">Years w/ MetaCorp</th>
<th style="width:16%">SSN</th>
<th style="width:16%">DoB</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= "#{@user.first_name} #{@user.last_name}" %></td>
<td><%= @user.work_info.income %></td>
<td><%= @user.work_info.bonuses %></td>
<td><%= @user.work_info.years_worked %></td>
<td class="ssn"><%= @user.work_info.SSN %></td>
<!-- Begin Secure Version>-->
<!--<td class="ssn"><%#= @user.work_info.last_four %></td>-->
<!-- End Secure Version -->
<td><%= @user.work_info.DoB %></td>
</tr>
<tr>
<td><%= "#{@user.first_name} #{@user.last_name}" %></td>
<td><%= @user.work_info.income %></td>
<td><%= @user.work_info.bonuses %></td>
<td><%= @user.work_info.years_worked %></td>
<td class="ssn"><%= @user.work_info.SSN %></td>
<!-- Begin Secure Version>-->
<!--<td class="ssn"><%#= @user.work_info.last_four %></td>-->
<!-- End Secure Version -->
<td><%= @user.work_info.DoB %></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
// This should be an EXCELLENT feature!!!
function maskSSN(){
var fullSSN = $("td.ssn").html().replace(/\d{3}.*?\d{2}/, "*****");
$("td.ssn").html(fullSSN);
var fullSSN = $("td.ssn").html().replace(/\d{3}.*?\d{2}/, "*****");
$("td.ssn").html(fullSSN);
}
function makeActive(){
$('li[id="employee_info"]').addClass('active');
$('li[id="employee_info"]').addClass('active');
};
$(document).ready(function () {
maskSSN(),
makeActive()