├── log └── .gitkeep ├── public ├── favicon.ico ├── docs │ ├── Dental_n_Stuff.pdf │ └── Health_n_Stuff.pdf ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── .ruby-version ├── lib ├── assets │ └── .gitkeep ├── tasks │ ├── traning.rake │ └── server.rake └── encryption.rb ├── .ruby-gemset ├── app ├── models │ ├── concerns │ │ └── .keep │ ├── retirement.rb │ ├── application_record.rb │ ├── key_management.rb │ ├── schedule.rb │ ├── performance.rb │ ├── message.rb │ ├── analytics.rb │ ├── paid_time_off.rb │ ├── pay.rb │ ├── benefits.rb │ ├── work_info.rb │ └── user.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── retirement_controller.rb │ ├── performance_controller.rb │ ├── paid_time_off_controller.rb │ ├── tutorials_controller.rb │ ├── work_info_controller.rb │ ├── dashboard_controller.rb │ ├── benefit_forms_controller.rb │ ├── api │ │ └── v1 │ │ │ ├── mobile_controller.rb │ │ │ └── users_controller.rb │ ├── sessions_controller.rb │ ├── messages_controller.rb │ ├── pay_controller.rb │ ├── users_controller.rb │ ├── schedule_controller.rb │ ├── application_controller.rb │ ├── admin_controller.rb │ └── password_resets_controller.rb ├── helpers │ ├── pay_helper.rb │ ├── admin_helper.rb │ ├── users_helper.rb │ ├── messages_helper.rb │ ├── schedule_helper.rb │ ├── sessions_helper.rb │ ├── dashboard_helper.rb │ ├── retirement_helper.rb │ ├── tutorials_helper.rb │ ├── work_info_helper.rb │ ├── api │ │ └── v1 │ │ │ └── users_helper.rb │ ├── application_helper.rb │ ├── benefit_forms_helper.rb │ ├── paid_time_off_helper.rb │ ├── performance_helper.rb │ └── password_resets_helper.rb ├── assets │ ├── images │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ ├── logo.psd │ │ ├── loading.gif │ │ ├── profile.jpg │ │ ├── html-screen.jpg │ │ ├── loading-blue.gif │ │ ├── loading-red.gif │ │ ├── fonts │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.ttf │ │ │ └── icomoon.woff │ │ ├── loading-black.gif │ │ ├── loading-green.gif │ │ ├── loading-orange.gif │ │ └── profile_color.jpg │ ├── stylesheets │ │ ├── password_resets.css.scss │ │ ├── charts-graphs.css │ │ ├── application.scss │ │ └── timepicker.css │ └── javascripts │ │ ├── password_resets.js.coffee │ │ ├── validation.js │ │ ├── load-image.min.js │ │ ├── html5.js │ │ ├── application.js │ │ ├── jquery.scrollUp.js │ │ ├── tiny-scrollbar.js │ │ └── bootstrap-image-gallery-main.js ├── mailers │ └── user_mailer.rb └── views │ ├── user_mailer │ ├── forgot_password.text.erb │ └── forgot_password.html.erb │ ├── layouts │ ├── shared │ │ ├── _messages.html.erb │ │ ├── _footer.html.erb │ │ ├── _header.html.erb │ │ └── _sidebar.html.erb │ └── application.html.erb │ ├── password_resets │ ├── forgot_password.html.erb │ └── reset_password.html.erb │ ├── dashboard │ ├── bar_graph.html.erb │ └── home.html.erb │ ├── users │ ├── new.html.erb │ └── account_settings.html.erb │ ├── admin │ ├── analytics.html.erb │ ├── dashboard.html.erb │ ├── get_all_users.html.erb │ └── get_user.html.erb │ ├── messages │ ├── show.html.erb │ └── index.html.erb │ ├── sessions │ └── new.html.erb │ ├── work_info │ └── index.html.erb │ ├── tutorials │ └── credentials.html.erb │ ├── retirement │ └── index.html.erb │ ├── performance │ └── index.html.erb │ └── benefit_forms │ └── index.html.erb ├── .rspec ├── vendor └── assets │ ├── javascripts │ └── .gitkeep │ └── stylesheets │ └── .gitkeep ├── Procfile ├── versions.tf ├── 1-Build-Docker-Image.sh ├── script ├── start └── rails ├── spec ├── models │ ├── message_spec.rb │ ├── key_management_spec.rb │ ├── pay_spec.rb │ ├── benefits_spec.rb │ └── user_spec.rb ├── mailers │ └── user_mailer_spec.rb ├── helpers │ ├── messages_helper_spec.rb │ ├── pay_helper_spec.rb │ ├── api │ │ └── v1 │ │ │ └── users_helper_spec.rb │ └── password_resets_helper_spec.rb ├── controllers │ ├── messages_controller_spec.rb │ ├── password_resets_controller_spec.rb │ ├── pay_controller_spec.rb │ └── api │ │ └── v1 │ │ └── users_controller_spec.rb ├── views │ └── password_resets │ │ └── new.html.erb_spec.rb ├── vulnerabilities │ ├── url_access_spec.rb │ ├── password_hashing_spec.rb │ ├── unvalidated_redirects_spec.rb │ ├── sensitive_data_exposure.rb │ ├── password_complexity_spec.rb │ ├── command_injection_spec.rb │ ├── insecure_dor_spec.rb │ ├── sql_injection_spec.rb │ ├── broken_auth_spec.rb │ ├── xss_spec.rb │ ├── mass_assignment_spec.rb │ └── csrf_spec.rb ├── support │ ├── user_fixture.rb │ └── capybara_shared.rb └── spec_helper.rb ├── config ├── initializers │ ├── html_entities.rb │ ├── strong_parameters.rb │ ├── constants.rb │ ├── assets.rb │ ├── key.rb │ ├── session_store.rb │ ├── filter_parameter_logging.rb │ ├── mime_types.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── secret_token.rb │ ├── inflections.rb │ └── populate_user_data.rb ├── environment.rb ├── boot.rb ├── locales │ └── en.yml ├── database.yml ├── environments │ ├── test.rb │ ├── mysql.rb │ ├── development.rb │ └── production.rb ├── routes.rb └── application.rb ├── parseyaml.py ├── entrypoint.sh ├── config.ru ├── .rubocop.yml ├── db ├── migrate │ ├── 20171007010129_remove_users_user_id.rb │ ├── 20140312002642_add_auth_token_to_users.rb │ ├── 20131112235256_add_encrypted_ssn_to_work_infos.rb │ ├── 20130708202859_create_benefits.rb │ ├── 20131113200708_create_key_managements.rb │ ├── 20140408185601_create_analytics.rb │ ├── 20131011180207_create_messages.rb │ ├── 20140315002730_create_pays.rb │ ├── 20130524222129_create_retirements.rb │ ├── 20130531182058_create_performances.rb │ ├── 20130424220355_create_users.rb │ ├── 20130531143853_create_work_infos.rb │ ├── 20130525001150_create_paid_time_offs.rb │ └── 20130527165832_create_schedules.rb └── schema.rb ├── doc └── README_FOR_APP ├── 2-Deploy-Docker-Image-To-Docker-Hub.sh ├── .gitignore ├── Rakefile ├── .powrc ├── outputs.tf ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── variables.tf ├── .overcommit.yml ├── Gemfile ├── main.tf ├── Guardfile ├── Dockerfile ├── CONTRIBUTING.md ├── README.md └── CODE_OF_CONDUCT.md /log/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.2 2 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | railsgoat 2 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --backtrace -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: rvmsudo bundle exec unicorn -p 9000 2 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /1-Build-Docker-Image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build . -t railsgoat:1.0 --no-cache -------------------------------------------------------------------------------- /script/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rails db:setup 5 | rails server 6 | -------------------------------------------------------------------------------- /app/helpers/pay_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module PayHelper 3 | end 4 | -------------------------------------------------------------------------------- /spec/models/message_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | -------------------------------------------------------------------------------- /app/helpers/admin_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module AdminHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module UsersHelper 3 | end 4 | -------------------------------------------------------------------------------- /spec/mailers/user_mailer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | -------------------------------------------------------------------------------- /app/helpers/messages_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module MessagesHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/schedule_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module ScheduleHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module SessionsHelper 3 | end 4 | -------------------------------------------------------------------------------- /spec/helpers/messages_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | -------------------------------------------------------------------------------- /spec/models/key_management_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | -------------------------------------------------------------------------------- /app/helpers/dashboard_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module DashboardHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/retirement_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module RetirementHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/tutorials_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module TutorialsHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/work_info_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module WorkInfoHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/api/v1/users_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Api::V1::UsersHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module ApplicationHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/benefit_forms_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module BenefitFormsHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/paid_time_off_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module PaidTimeOffHelper 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/performance_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module PerformanceHelper 3 | end 4 | -------------------------------------------------------------------------------- /spec/controllers/messages_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | -------------------------------------------------------------------------------- /app/helpers/password_resets_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module PasswordResetsHelper 3 | end 4 | -------------------------------------------------------------------------------- /spec/controllers/password_resets_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | -------------------------------------------------------------------------------- /app/assets/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/1.jpg -------------------------------------------------------------------------------- /app/assets/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/2.jpg -------------------------------------------------------------------------------- /app/assets/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/3.jpg -------------------------------------------------------------------------------- /app/assets/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/4.jpg -------------------------------------------------------------------------------- /app/assets/images/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/logo.psd -------------------------------------------------------------------------------- /app/assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/loading.gif -------------------------------------------------------------------------------- /app/assets/images/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/profile.jpg -------------------------------------------------------------------------------- /app/models/retirement.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Retirement < ApplicationRecord 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /public/docs/Dental_n_Stuff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/public/docs/Dental_n_Stuff.pdf -------------------------------------------------------------------------------- /public/docs/Health_n_Stuff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/public/docs/Health_n_Stuff.pdf -------------------------------------------------------------------------------- /app/assets/images/html-screen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/html-screen.jpg -------------------------------------------------------------------------------- /app/assets/images/loading-blue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/loading-blue.gif -------------------------------------------------------------------------------- /app/assets/images/loading-red.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/loading-red.gif -------------------------------------------------------------------------------- /config/initializers/html_entities.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | ActiveSupport::JSON::Encoding::escape_html_entities_in_json = false 3 | -------------------------------------------------------------------------------- /app/assets/images/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/fonts/icomoon.eot -------------------------------------------------------------------------------- /app/assets/images/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/fonts/icomoon.ttf -------------------------------------------------------------------------------- /app/assets/images/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/fonts/icomoon.woff -------------------------------------------------------------------------------- /app/assets/images/loading-black.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/loading-black.gif -------------------------------------------------------------------------------- /app/assets/images/loading-green.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/loading-green.gif -------------------------------------------------------------------------------- /app/assets/images/loading-orange.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/loading-orange.gif -------------------------------------------------------------------------------- /app/assets/images/profile_color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/demo-railsgoat/master/app/assets/images/profile_color.jpg -------------------------------------------------------------------------------- /parseyaml.py: -------------------------------------------------------------------------------- 1 | import yaml, json 2 | with open('./contrast_security.yaml') as f: 3 | config = yaml.load(f) 4 | print(json.dumps(config['api'])) -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class ApplicationRecord < ActiveRecord::Base 3 | self.abstract_class = true 4 | end 5 | -------------------------------------------------------------------------------- /config/initializers/strong_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection) 3 | -------------------------------------------------------------------------------- /spec/controllers/pay_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # require 'spec_helper' 3 | # 4 | # describe PayController do 5 | # 6 | # end 7 | -------------------------------------------------------------------------------- /app/models/key_management.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class KeyManagement < ApplicationRecord 3 | belongs_to :work_info 4 | belongs_to :user 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/constants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | ACCESS_TOKEN_SALT = "S4828341189aefiasd#ASDF" 3 | 4 | RG_IV = "PPKLKAJDKGHALDJL482823458028" 5 | -------------------------------------------------------------------------------- /lib/tasks/traning.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | desc "run training tests" 3 | task :training do 4 | Rake::Task["spec:vulnerabilities"].invoke 5 | end 6 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | rm -f tmp/pids/server.pid 2 | if [[ $TEST = true ]] 3 | then 4 | bundle exec rails training 5 | else 6 | bundle exec rails s -p 3000 -b '0.0.0.0' 7 | fi -------------------------------------------------------------------------------- /spec/controllers/api/v1/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # require 'spec_helper' 3 | # 4 | # describe Api::V1::UsersController do 5 | # 6 | # end 7 | -------------------------------------------------------------------------------- /spec/models/pay_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # require 'spec_helper' 3 | # 4 | # describe Pay do 5 | # pending "add some examples to (or delete) #{__FILE__}" 6 | # end 7 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Rails.application.config.assets.precompile += %w( validation.js jquery.dataTables.min.js fullcalendar.min.js moment.min.js ) 3 | -------------------------------------------------------------------------------- /app/controllers/retirement_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class RetirementController < ApplicationController 3 | 4 | def index 5 | @info = current_user.retirement 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/performance_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class PerformanceController < ApplicationController 3 | 4 | def index 5 | @perf = current_user.performance 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # This file is used by Rack-based servers to start the application. 3 | 4 | require ::File.expand_path("../config/environment", __FILE__) 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-github: 3 | - config/default.yml 4 | - config/rails.yml 5 | 6 | 7 | Rails/OutputSafety: 8 | Exclude: 9 | - 'app/controllers/password_resets_controller.rb' 10 | -------------------------------------------------------------------------------- /db/migrate/20171007010129_remove_users_user_id.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class RemoveUsersUserId < ActiveRecord::Migration[5.1] 3 | def change 4 | remove_column :users, :user_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/models/schedule.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Schedule < ApplicationRecord 3 | belongs_to :paid_time_off 4 | 5 | validates_presence_of :date_begin, :date_end, :event_desc, :event_name, :event_type 6 | end 7 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Load the Rails application. 3 | require File.expand_path("../application", __FILE__) 4 | 5 | # Initialize the Rails application. 6 | Railsgoat::Application.initialize! 7 | -------------------------------------------------------------------------------- /config/initializers/key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | if Rails.env.production? 3 | # Specify env variable/location/etc. to retrieve key from 4 | else 5 | KEY = "123456789101112123456789101112123456789101112" 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140312002642_add_auth_token_to_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class AddAuthTokenToUsers < ActiveRecord::Migration[4.2] 3 | def change 4 | add_column :users, :auth_token, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Set up gems listed in the Gemfile. 3 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__) 4 | 5 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 6 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rails doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /spec/views/password_resets/new.html.erb_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # require 'spec_helper' 3 | # 4 | # describe "password_resets/new.html.erb" do 5 | # pending "add some examples to (or delete) #{__FILE__}" 6 | # end 7 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/password_resets.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the password_resets controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/controllers/paid_time_off_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class PaidTimeOffController < ApplicationController 3 | 4 | def index 5 | @pto = current_user.paid_time_off 6 | @schedule = Schedule.new 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | Railsgoat::Application.config.session_store :cookie_store, key: "_railsgoat_session", httponly: false 5 | -------------------------------------------------------------------------------- /db/migrate/20131112235256_add_encrypted_ssn_to_work_infos.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class AddEncryptedSsnToWorkInfos < ActiveRecord::Migration[4.2] 3 | def change 4 | add_column :work_infos, :encrypted_ssn, :binary 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/tutorials_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class TutorialsController < ApplicationController 3 | skip_before_action :has_info 4 | skip_before_action :authenticated 5 | 6 | layout false, only: [:credentials] 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130708202859_create_benefits.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateBenefits < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :benefits do |t| 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/charts-graphs.css: -------------------------------------------------------------------------------- 1 | /* Easy Pie Chart CSS */ 2 | 3 | .easyPieChart { 4 | position: relative; 5 | text-align: center; 6 | } 7 | 8 | .easyPieChart canvas { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /app/models/performance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Performance < ApplicationRecord 3 | belongs_to :user 4 | 5 | def reviewer_name 6 | u = User.find_by_id(self.reviewer) 7 | u.full_name if u.respond_to?("fullname") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /2-Deploy-Docker-Image-To-Docker-Hub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Please log in using your Docker Hub credentials to update the container image" 4 | docker login 5 | docker tag railsgoat:1.0 contrastsecuritydemo/railsgoat:1.0 6 | docker push contrastsecuritydemo/railsgoat:1.0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle 2 | /bin 3 | /db/*.sqlite3 4 | /log/*.log 5 | /tmp 6 | .elasticbeanstalk/ 7 | .DS_Store 8 | /public/data 9 | /public/assets 10 | *.png 11 | coverage 12 | .tags 13 | /.vagrant 14 | /vendor/ruby 15 | run.sh 16 | test.sh 17 | contrast_security.yaml 18 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require_relative "config/application" 6 | 7 | Rails.application.load_tasks 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/password_resets.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Configure sensitive parameters which will be filtered from the log file. 5 | Rails.application.config.filter_parameters += [:password] 6 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Add new mime types for use in respond_to blocks: 5 | # Mime::Type.register "text/richtext", :rtf 6 | # Mime::Type.register_alias "text/html", :iphone 7 | -------------------------------------------------------------------------------- /.powrc: -------------------------------------------------------------------------------- 1 | if [ -f "${rvm_path}/scripts/rvm" ]; then 2 | source "${rvm_path}/scripts/rvm" 3 | 4 | if [ -f ".rvmrc" ]; then 5 | source ".rvmrc" 6 | elif [ -f ".ruby-version" ] && [ -f ".ruby-gemset" ]; then 7 | rvm use `cat .ruby-version`@`cat .ruby-gemset` 8 | fi 9 | fi 10 | -------------------------------------------------------------------------------- /db/migrate/20131113200708_create_key_managements.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateKeyManagements < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :key_managements do |t| 5 | t.string :iv 6 | t.integer :user_id 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20140408185601_create_analytics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateAnalytics < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :analytics do |t| 5 | t.string :ip_address 6 | t.string :referrer 7 | t.string :user_agent 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 4 | 5 | APP_PATH = File.expand_path("../../config/application", __FILE__) 6 | require File.expand_path("../../config/boot", __FILE__) 7 | require "rails/commands" 8 | -------------------------------------------------------------------------------- /db/migrate/20131011180207_create_messages.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateMessages < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :messages do |t| 5 | t.integer :creator_id 6 | t.integer :receiver_id 7 | t.text :message 8 | t.boolean :read 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/message.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Message < ApplicationRecord 3 | belongs_to :user 4 | validates_presence_of :creator_id, :receiver_id, :message 5 | 6 | def creator_name 7 | if creator = User.where(id: self.creator_id).first 8 | creator.full_name 9 | else 10 | "Name unavailable" 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/work_info_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class WorkInfoController < ApplicationController 3 | def index 4 | @user = User.find_by(id: params[:user_id]) 5 | if !(@user) || @user.admin 6 | flash[:error] = "Sorry, no user with that user id exists" 7 | redirect_to home_dashboard_index_path 8 | end 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20140315002730_create_pays.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreatePays < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :pays do |t| 5 | t.integer :user_id 6 | t.string :bank_account_num 7 | t.string :bank_routing_num 8 | t.integer :percent_of_deposit 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130524222129_create_retirements.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateRetirements < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :retirements do |t| 5 | t.string :total 6 | t.string :employee_contrib 7 | t.string :employer_contrib 8 | t.integer :user_id 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130531182058_create_performances.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreatePerformances < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :performances do |t| 5 | t.integer :user_id 6 | t.date :date_submitted 7 | t.integer :score 8 | t.string :comments 9 | t.integer :reviewer 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130424220355_create_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateUsers < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :users do |t| 5 | t.string :email 6 | t.string :password 7 | t.boolean :admin 8 | t.string :first_name 9 | t.string :last_name 10 | t.integer :user_id 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20130531143853_create_work_infos.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateWorkInfos < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :work_infos do |t| 5 | t.integer :user_id 6 | t.string :income 7 | t.string :bonuses 8 | t.integer :years_worked 9 | t.string :SSN 10 | t.date :DoB 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class UserMailer < ActionMailer::Base 3 | default from: "noreply@railsgoat.dev" 4 | 5 | def forgot_password(email, token) 6 | @token = token 7 | @url = url_for(controller: "password_resets", action: "reset_password", only_path: false) + "?token=#{token}" 8 | 9 | mail(to: "#{email}", subject: "Reset your MetaCorp password") 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130525001150_create_paid_time_offs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreatePaidTimeOffs < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :paid_time_offs do |t| 5 | t.integer :user_id 6 | t.integer :sick_days_taken 7 | t.integer :sick_days_earned 8 | t.integer :pto_taken 9 | t.integer :pto_earned 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130527165832_create_schedules.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class CreateSchedules < ActiveRecord::Migration[4.2] 3 | def change 4 | create_table :schedules do |t| 5 | t.string :event_type 6 | t.date :date_begin 7 | t.date :date_end 8 | t.string :event_name 9 | t.string :event_desc 10 | t.integer :user_id 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | #### Azure Output Variables #### 2 | 3 | output "ip_address" { 4 | value = "${azurerm_container_group.app.ip_address}:3000" 5 | } 6 | 7 | #the dns fqdn of the container group if dns_name_label is set 8 | output "fqdn" { 9 | value = "http://${azurerm_container_group.app.fqdn}:3000" 10 | } 11 | 12 | output "contrast" { 13 | value = "This app should appear in the environment ${data.external.yaml.result.url}" 14 | } -------------------------------------------------------------------------------- /app/views/user_mailer/forgot_password.text.erb: -------------------------------------------------------------------------------- 1 | Need help logging in? 2 | ========================================================== 3 | 4 | A password reset was requested for your user account. 5 | 6 | To reset your MetaCorp password, simply copy the 7 | following link and follow the instructions: 8 | 9 | <%= @url %> 10 | 11 | If you don't want to change your password, you can ignore this email. 12 | 13 | Thanks, and have a great day! -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 5 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 6 | 7 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 8 | # Rails.backtrace_cleaner.remove_silencers! 9 | -------------------------------------------------------------------------------- /app/models/analytics.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Analytics < ApplicationRecord 3 | scope :hits_by_ip, ->(ip, col = "*") { select("#{col}").where(ip_address: ip).order("id DESC") } 4 | 5 | def self.count_by_col(col) 6 | calculate(:count, col) 7 | end 8 | 9 | def self.parse_field(field) 10 | valid_fields = ["ip_address", "referrer", "user_agent"] 11 | 12 | if valid_fields.include?(field) 13 | field 14 | else 15 | "1" 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/helpers/pay_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # require 'spec_helper' 3 | # 4 | # Specs in this file have access to a helper object that includes 5 | # the PayHelper. For example: 6 | # 7 | # describe PayHelper do 8 | # describe "string concat" do 9 | # it "concats two strings with spaces" do 10 | # expect(helper.concat_strings("this","that")).to eq("this that") 11 | # end 12 | # end 13 | # end 14 | # describe PayHelper do 15 | # pending "add some examples to (or delete) #{__FILE__}" 16 | # end 17 | -------------------------------------------------------------------------------- /app/models/paid_time_off.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class PaidTimeOff < ApplicationRecord 3 | belongs_to :user 4 | has_many :schedule, foreign_key: :user_id, primary_key: :user_id, dependent: :destroy 5 | 6 | def sick_days_remaining 7 | self.sick_days_earned - self.sick_days_taken 8 | end 9 | 10 | def pto_days_remaining 11 | self.pto_earned - self.pto_taken 12 | end 13 | 14 | def sick_days_taken_percentage 15 | result = self.sick_days_taken.to_f / self.sick_days_earned.to_f * 100.0 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/helpers/api/v1/users_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # require 'spec_helper' 3 | # 4 | # Specs in this file have access to a helper object that includes 5 | # the Api::V1::UsersHelper. For example: 6 | # 7 | # describe Api::V1::UsersHelper do 8 | # describe "string concat" do 9 | # it "concats two strings with spaces" do 10 | # expect(helper.concat_strings("this","that")).to eq("this that") 11 | # end 12 | # end 13 | # end 14 | # describe Api::V1::UsersHelper do 15 | # pending "add some examples to (or delete) #{__FILE__}" 16 | # end 17 | -------------------------------------------------------------------------------- /spec/helpers/password_resets_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # require 'spec_helper' 3 | # 4 | # Specs in this file have access to a helper object that includes 5 | # the PasswordResetsHelper. For example: 6 | # 7 | # describe PasswordResetsHelper do 8 | # describe "string concat" do 9 | # it "concats two strings with spaces" do 10 | # expect(helper.concat_strings("this","that")).to eq("this that") 11 | # end 12 | # end 13 | # end 14 | # describe PasswordResetsHelper do 15 | # pending "add some examples to (or delete) #{__FILE__}" 16 | # end 17 | -------------------------------------------------------------------------------- /spec/vulnerabilities/url_access_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "url access" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | 7 | before do 8 | UserFixture.reset_all_users 9 | 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "attack\nTutorial: https://github.com/OWASP/railsgoat/wiki/A7-Missing-Function-Level-Access-Control--(Admin-Controller)", js: true do 14 | login(normal_user) 15 | 16 | visit "/admin/1/dashboard" 17 | 18 | expect(current_path).to eq("/") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | # 4 | # This file contains settings for ActionController::ParamsWrapper which 5 | # is enabled by default. 6 | 7 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 8 | ActiveSupport.on_load(:action_controller) do 9 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 10 | end 11 | 12 | # Disable root element in JSON by default. 13 | ActiveSupport.on_load(:active_record) do 14 | self.include_root_in_json = false 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/user_fixture.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class UserFixture 3 | def self.reset_all_users 4 | User.delete_all 5 | Rails.application.load_seed 6 | end 7 | 8 | def self.normal_user 9 | password = "thi$ 1s cOmplExEr" 10 | User.create!(first_name: "Joe", last_name: "Schmoe", email: "joe@schmoe.com", 11 | password: password, password_confirmation: password).tap do |user| 12 | def user.clear_password 13 | "thi$ 1s cOmplExEr" 14 | end 15 | end 16 | end 17 | 18 | def self.admin_user 19 | User.where(admin: true).first 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/models/pay.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Pay < ApplicationRecord 3 | # Associations 4 | belongs_to :user 5 | 6 | # Validations 7 | validates :bank_account_num, presence: true 8 | validates :bank_routing_num, presence: true 9 | validates :percent_of_deposit, presence: true 10 | 11 | # callbacks 12 | before_save :encrypt_bank_account_num 13 | 14 | def as_json 15 | super(only: [:bank_account_num, :bank_routing_num, :percent_of_deposit, :id]) 16 | end 17 | 18 | def encrypt_bank_account_num 19 | self.bank_account_num = Encryption.encrypt_sensitive_value(self.bank_account_num) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/tasks/server.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | namespace :server do 3 | 4 | desc "Start Rails" 5 | task :start do 6 | pid_file = "tmp/pids/server.pid" 7 | if !(File.exist?(pid_file)) 8 | sh("rails s -d") 9 | else 10 | puts "[+] Server is already running" 11 | end 12 | end 13 | 14 | desc "Stop Rails" 15 | task :stop do 16 | pid_file = "tmp/pids/server.pid" 17 | if File.exist?(pid_file) 18 | Process.kill("INT", File.read(pid_file).to_i) 19 | else 20 | puts "[-] Server isn't running" 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class DashboardController < ApplicationController 3 | skip_before_action :has_info 4 | layout false, only: [:change_graph] 5 | 6 | def home 7 | @user = current_user 8 | 9 | # See if the user has a font preference 10 | if params[:font] 11 | cookies[:font] = params[:font] 12 | end 13 | end 14 | 15 | def change_graph 16 | self.try(params[:graph]) 17 | 18 | if params[:graph] == "bar_graph" 19 | render "dashboard/bar_graph" 20 | else 21 | @user = current_user 22 | render "dashboard/pie_charts" 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/models/benefits_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper.rb" 3 | 4 | describe Benefits do 5 | before(:all) do 6 | UserFixture.reset_all_users 7 | DatabaseCleaner.strategy = :transaction 8 | end 9 | 10 | after(:all) do 11 | DatabaseCleaner.strategy = :truncation 12 | end 13 | 14 | it "can be instantiated" do 15 | expect(Benefits.new).to be_an_instance_of(Benefits) 16 | end 17 | 18 | it "name can be updated" do 19 | new_name = "Bobby" 20 | user = User.all.first 21 | user.first_name = new_name 22 | user.save! 23 | expect(User.all.first.first_name).to eq(new_name) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/vulnerabilities/password_hashing_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "improper password hashing" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | 7 | before do 8 | UserFixture.reset_all_users 9 | pending unless verifying_fixed? 10 | end 11 | 12 | scenario "with just md5\nTutorial: https://github.com/OWASP/railsgoat/wiki/A6-Sensitive-Data-Exposure-Insecure-Password-Storage" do 13 | new_pass = "testPassw0rd!" 14 | normal_user.password = new_pass 15 | normal_user.password_confirmation = new_pass 16 | normal_user.save! 17 | 18 | expect(normal_user.password).not_to eq(Digest::MD5.hexdigest(new_pass)) 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /app/views/user_mailer/forgot_password.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Need help logging in?

8 |

9 | A password reset was requested for your user account.
10 |
11 | 12 | To reset your MetaCorp password, simply click on the 13 | following link and follow the instructions:
14 |
15 | 16 | <%= link_to "Click here to reset your password", @url %>
17 |
18 | 19 | If you don't want to change your password, you can ignore this email. 20 |

21 |

Thanks, and have a great day!

22 | 23 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | *= require jquery.fileupload-ui 14 | */ 15 | 16 | p.desc { 17 | max-width: 850px; 18 | word-wrap: break-word; 19 | } -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Your secret key for verifying the integrity of signed cookies. 5 | # If you change this key, all old signed cookies will become invalid! 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | Railsgoat::Application.config.secret_token = "2f1d90a26236c3245d96f5606c201a780dc9ca687e5ed82b45e211bb5dc84c1870f61ca9e002dad5dd8a149c9792d8f07f31a9575065cca064bd6af44f8750e4" 9 | Railsgoat::Application.config.secret_key_base = "2f1d90a26236c3245d96f5606c201a780dc9ca687e5ed82b45e211bb5dc84c1870f61ca9e002dad5dd8a149c9792d8f07f31a9575065cca064bd6af44f8750e4" 10 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Add new inflection rules using the following format. Inflections 5 | # are locale specific, and you may define rules for as many different 6 | # locales as you wish. All of these examples are active by default: 7 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 8 | # inflect.plural /^(ox)$/i, '\1en' 9 | # inflect.singular /^(ox)en/i, '\1' 10 | # inflect.irregular 'person', 'people' 11 | # inflect.uncountable %w( fish sheep ) 12 | # end 13 | 14 | # These inflection rules are supported but not enabled by default: 15 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 16 | # inflect.acronym 'RESTful' 17 | # end 18 | -------------------------------------------------------------------------------- /app/views/layouts/shared/_messages.html.erb: -------------------------------------------------------------------------------- 1 | <% flash.each do |name, msg| %> 2 | <% name = name.to_sym %> 3 | <% if name == :error %> 4 |
5 | × 6 | <%= content_tag :div, msg, :id => "flash_notice" %> 7 |
8 | <% elsif name == :success %> 9 |
10 | × 11 | <%= content_tag :div, msg, :id => "flash_notice" %> 12 |
13 | <% elsif name == :info %> 14 |
15 | × 16 | <%= content_tag :div, msg, :id => "flash_notice" %> 17 |
18 | <% end %> 19 | <% end %> -------------------------------------------------------------------------------- /app/views/layouts/shared/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /spec/vulnerabilities/unvalidated_redirects_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "unvalidated redirect" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | 7 | before do 8 | UserFixture.reset_all_users 9 | 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "attack\nTutorial: https://github.com/OWASP/railsgoat/wiki/A10-Unvalidated-Redirects-and-Forwards-(redirect_to)", js: true do 14 | visit "/?url=http://example.com/do/evil/things" 15 | within(".signup") do 16 | fill_in "email", with: normal_user.email 17 | fill_in "password", with: normal_user.clear_password 18 | end 19 | within(".actions") do 20 | click_on "Login" 21 | end 22 | 23 | expect(current_url).to eq("/dashboard/home") 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/benefit_forms_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class BenefitFormsController < ApplicationController 3 | 4 | def index 5 | @benefits = Benefits.new 6 | end 7 | 8 | def download 9 | begin 10 | path = params[:name] 11 | file = params[:type].constantize.new(path) 12 | send_file file, disposition: "attachment" 13 | rescue 14 | redirect_to user_benefit_forms_path(user_id: current_user.id) 15 | end 16 | end 17 | 18 | def upload 19 | file = params[:benefits][:upload] 20 | if file 21 | flash[:success] = "File Successfully Uploaded!" 22 | Benefits.save(file, params[:benefits][:backup]) 23 | else 24 | flash[:error] = "Something went wrong" 25 | end 26 | redirect_to user_benefit_forms_path(user_id: current_user.id) 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /app/controllers/api/v1/mobile_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Api::V1::MobileController < ApplicationController 3 | skip_before_action :authenticated 4 | before_action :mobile_request? 5 | 6 | respond_to :json 7 | 8 | def show 9 | if params[:class] 10 | model = params[:class].classify.constantize 11 | respond_with model.find(params[:id]).to_json 12 | end 13 | end 14 | 15 | def index 16 | if params[:class] 17 | model = params[:class].classify.constantize 18 | respond_with model.all.to_json 19 | else 20 | respond_with nil.to_json 21 | end 22 | end 23 | 24 | private 25 | 26 | def mobile_request? 27 | if session[:mobile_param] 28 | session[:mobile_param] == "1" 29 | else 30 | request.user_agent =~ /ios|android/i 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/vulnerabilities/sensitive_data_exposure.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "sensitive data exposure" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | let(:user_ssn) { "999-99-9999" } 7 | 8 | before do 9 | UserFixture.reset_all_users 10 | normal_user.work_info.update_attribute(:SSN, user_ssn) 11 | 12 | pending unless verifying_fixed? 13 | end 14 | 15 | # this won't work with javascript_driver, as it'll apply the javascript 16 | # function to mask this value and the source will be overwritten. 17 | scenario "attack\nTutorial: https://github.com/OWASP/railsgoat/wiki/A6-Sensitive-Data-Exposure-Cleartext-Storage-SSNs" do 18 | login(normal_user) 19 | 20 | visit "/users/#{normal_user.id}/work_info" 21 | 22 | expect(page.source).not_to include(user_ssn) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | mysql: 13 | adapter: mysql2 14 | database: development_railsgoat 15 | pool: 5 16 | timeout: 5000 17 | host: localhost 18 | username: root 19 | password: 20 | 21 | # Warning: The database defined as "test" will be erased and 22 | # re-generated from your development database when you run "rails". 23 | # Do not set this db to the same as development or production. 24 | test: 25 | adapter: sqlite3 26 | database: db/test.sqlite3 27 | pool: 5 28 | timeout: 5000 29 | 30 | production: 31 | adapter: sqlite3 32 | database: db/production.sqlite3 33 | pool: 5 34 | timeout: 5000 35 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 🐞 **Problem** 4 | 5 | 6 | 7 | 🎯 **Goal** 8 | 9 | 10 | 11 | 💡 **Possible solutions** 12 | 13 | 14 | 📋 **Steps to solve the problem** 15 | 16 | * Comment below about what you've started working on. 17 | * Add, commit, push your changes 18 | * Submit a pull request and add this in comments - `Addresses #` 19 | * Ask for a review in comments section of pull request 20 | * Celebrate your contribution to this project 🎉 21 | -------------------------------------------------------------------------------- /spec/vulnerabilities/password_complexity_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "password complexity" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | 7 | before do 8 | UserFixture.reset_all_users 9 | pending unless verifying_fixed? 10 | end 11 | 12 | scenario "one\nTutorial: https://github.com/OWASP/railsgoat/wiki/A2-Lack-of-Password-Complexity" do 13 | new_user_email = normal_user.email + "two" 14 | 15 | visit "/signup" 16 | within(".signup") do 17 | fill_in "user_email", with: new_user_email 18 | fill_in "user_first_name", with: normal_user.first_name 19 | fill_in "user_last_name", with: normal_user.last_name + "not" 20 | fill_in "user_password", with: "password" 21 | fill_in "user_password_confirmation", with: "password" 22 | end 23 | click_on "Submit" 24 | 25 | expect(User.find_by(email: new_user_email)).to be_nil 26 | expect(current_path).to eq("/signup") 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper.rb" 3 | 4 | describe User do 5 | before(:all) do 6 | UserFixture.reset_all_users 7 | DatabaseCleaner.strategy = :transaction 8 | end 9 | 10 | after(:all) do 11 | DatabaseCleaner.strategy = :truncation 12 | end 13 | 14 | it "can be instantiated" do 15 | expect(User.new).to be_an_instance_of(User) 16 | end 17 | 18 | it "should require a email" do 19 | expect(User.new(email: "")).not_to be_valid 20 | end 21 | 22 | it "should require valid email" do 23 | expect(User.new(email: "@gmail.com")).not_to be_valid 24 | end 25 | 26 | it "should require unique email" do 27 | user = User.all.first 28 | expect(User.new(email: user.email)).not_to be_valid 29 | end 30 | 31 | it "name can be updated" do 32 | new_name = "Bobby" 33 | user = User.all.first 34 | user.first_name = new_name 35 | user.save! 36 | expect(User.all.first.first_name).to eq(new_name) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/views/password_resets/forgot_password.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

MetaCorp

3 |

A GoatGroup Company

4 |
5 |
6 |
7 | 8 | 27 | 28 |
29 |
30 |
31 |
-------------------------------------------------------------------------------- /lib/encryption.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Encryption 3 | 4 | # Added a re-usable encryption routine, shouldn't be an issue! 5 | def self.encrypt_sensitive_value(val = "") 6 | aes = OpenSSL::Cipher.new(cipher_type) 7 | aes.encrypt 8 | aes.key = key[0..31] 9 | aes.iv = iv[0..15] if iv != nil 10 | new_val = aes.update("#{val}") + aes.final 11 | Base64.strict_encode64(new_val).encode("utf-8") 12 | end 13 | 14 | def self.decrypt_sensitive_value(val = "") 15 | aes = OpenSSL::Cipher.new(cipher_type) 16 | aes.decrypt 17 | aes.key = key[0..31] 18 | aes.iv = iv[0.15] if iv != nil 19 | decoded = Base64.strict_decode64("#{val}") 20 | aes.update("#{decoded}") + aes.final 21 | end 22 | 23 | # Should be able to just re-use the same key we already have! 24 | def self.key 25 | raise "Key Missing. Add one in initializers/key.rb" if !(KEY) 26 | KEY 27 | end 28 | 29 | def self.iv 30 | RG_IV 31 | end 32 | 33 | def self.cipher_type 34 | "aes-256-cbc" 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/models/benefits.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Benefits < ApplicationRecord 3 | 4 | def self.save(file, backup = false) 5 | data_path = Rails.root.join("public", "data") 6 | full_file_name = "#{data_path}/#{file.original_filename}" 7 | f = File.open(full_file_name, "wb+") 8 | f.write file.read 9 | f.close 10 | make_backup(file, data_path, full_file_name) if backup == "true" 11 | end 12 | 13 | def self.make_backup(file, data_path, full_file_name) 14 | if File.exist?(full_file_name) 15 | silence_streams(STDERR) { system("cp #{full_file_name} #{data_path}/bak#{Time.zone.now.to_i}_#{file.original_filename}") } 16 | end 17 | end 18 | 19 | def self.silence_streams(*streams) 20 | on_hold = streams.collect { |stream| stream.dup } 21 | streams.each do |stream| 22 | stream.reopen(RUBY_PLATFORM =~ /mswin/ ? "NUL:" : "/dev/null") 23 | stream.sync = true 24 | end 25 | yield 26 | ensure 27 | streams.each_with_index do |stream, i| 28 | stream.reopen(on_hold[i]) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class SessionsController < ApplicationController 3 | skip_before_action :has_info 4 | skip_before_action :authenticated, only: [:new, :create] 5 | 6 | def new 7 | @url = params[:url] 8 | redirect_to home_dashboard_index_path if current_user 9 | end 10 | 11 | def create 12 | path = params[:url].present? ? params[:url] : home_dashboard_index_path 13 | begin 14 | # Normalize the email address, why not 15 | user = User.authenticate(params[:email].to_s.downcase, params[:password]) 16 | rescue RuntimeError => e 17 | # don't do ANYTHING 18 | end 19 | 20 | if user 21 | if params[:remember_me] 22 | cookies.permanent[:auth_token] = user.auth_token 23 | else 24 | session[:user_id] = user.id 25 | end 26 | redirect_to path 27 | else 28 | flash[:error] = e.message 29 | render "sessions/new" 30 | end 31 | end 32 | 33 | def destroy 34 | cookies.delete(:auth_token) 35 | reset_session 36 | redirect_to root_path 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/models/work_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class WorkInfo < ApplicationRecord 3 | belongs_to :user 4 | has_one :key_management, foreign_key: :user_id, primary_key: :user_id, dependent: :destroy 5 | #before_save :encrypt_ssn 6 | 7 | # We should probably use this 8 | def last_four 9 | "***-**-" << self.decrypt_ssn[-4, 4] 10 | end 11 | 12 | def encrypt_ssn 13 | aes = OpenSSL::Cipher.new(cipher_type) 14 | aes.encrypt 15 | aes.key = key[0..31] 16 | aes.iv = iv if iv != nil 17 | self.encrypted_ssn = aes.update(self.SSN) + aes.final 18 | self.SSN = nil 19 | end 20 | 21 | def decrypt_ssn 22 | aes = OpenSSL::Cipher.new(cipher_type) 23 | aes.decrypt 24 | aes.key = key[0..31] 25 | aes.iv = iv if iv != nil 26 | aes.update(self.encrypted_ssn) + aes.final 27 | end 28 | 29 | def key 30 | raise "Key Missing" unless KEY.present? 31 | KEY 32 | end 33 | 34 | def iv 35 | raise "No IV for this User" unless self.key_management.try(:iv).present? 36 | self.key_management.iv 37 | end 38 | 39 | def cipher_type 40 | "aes-256-cbc" 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/vulnerabilities/command_injection_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | require "tmpdir" 4 | 5 | feature "command injection" do 6 | let(:normal_user) { UserFixture.normal_user } 7 | 8 | before do 9 | UserFixture.reset_all_users 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "attack\nTutorial: https://github.com/OWASP/railsgoat/wiki/A1-Command-Injection", js: true do 14 | login(normal_user) 15 | 16 | legit_file = File.join(Rails.root, "public", "data", "legit.txt") 17 | File.open(legit_file, "w") { |f| f.puts "totes legit" } 18 | 19 | visit "/users/#{normal_user.id}/benefit_forms" 20 | Dir.mktmpdir do |dir| 21 | hackety_file = File.join(dir, "test; cd public && cd data && rm -f * ;") 22 | File.open(hackety_file, "w") { |f| f.print "mwahaha" } 23 | within(".new_benefits") do 24 | attach_file "benefits_upload", hackety_file 25 | find(:xpath, "//input[@id='benefits_backup']", visible: false).set "true" 26 | end 27 | click_on "Start Upload" 28 | end 29 | 30 | expect(File.exist?(legit_file)).to be_truthy 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/vulnerabilities/insecure_dor_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "insecure direct object reference" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | let(:another_user) { User.find_by(id: 2) } 7 | 8 | before do 9 | UserFixture.reset_all_users 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "attack one" do 14 | login(normal_user) 15 | 16 | visit "/users/#{normal_user.id}/benefit_forms" 17 | download_url = first(".widget-body a")[:href] 18 | visit download_url.sub(/name=(.*?)&/, "name=config/database.yml&") 19 | 20 | expect(page.status_code).not_to eq(200) 21 | expect(page.response_headers["Content-Disposition"]).not_to include("database.yml") 22 | end 23 | 24 | scenario "attack two\nTutorial: https://github.com/OWASP/railsgoat/wiki/A4-Insecure-Direct-Object-Reference" do 25 | expect(normal_user.id).not_to eq(another_user.id) 26 | 27 | visit "/users/#{another_user.id}/work_info" 28 | 29 | expect(first("td").text).not_to include(another_user.name) 30 | expect(first("td").text).to include(normal_user.name) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/vulnerabilities/sql_injection_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "sql injection" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | let(:admin_user) { User.where(admin: true).first } 7 | 8 | before do 9 | UserFixture.reset_all_users 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "attack\nTutorial: https://github.com/OWASP/railsgoat/wiki/R5-A1-SQL-Injection-Concatentation" do 14 | expect(admin_user.admin).to be_truthy 15 | 16 | login(normal_user) 17 | 18 | visit "/users/#{normal_user.id}/account_settings" 19 | within("#account_edit") do 20 | fill_in "Email", with: "joe.admin@schmoe.com" 21 | fill_in "user_password", with: "hacketyhack" 22 | fill_in "user_password_confirmation", with: "hacketyhack" 23 | 24 | # this is a hidden field, so cannot use fill_in to access it. 25 | find(:xpath, "//input[@id='user_id']", visible: false).set "8' OR admin='t') --" 26 | end 27 | click_on "Submit" 28 | 29 | admin_user = User.where(admin: true).first 30 | expect(admin_user.email).not_to eq("joe.admin@schmoe.com") 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2018 The Open Web Application Security Project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RailsGoat 5 | <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> 6 | <%= javascript_include_tag "application", "data-turbolinks-track" => true %> 7 | <%#= csrf_meta_tags %> 8 | 9 | <% 10 | if cookies[:font] 11 | %> 12 | 13 | <% 14 | end 15 | %> 16 | 17 | 18 | 19 | <%= render "layouts/shared/header" %> 20 | <%= render "layouts/shared/sidebar" %> 21 |
22 | <% if current_user %> 23 |
24 | <%= render "layouts/shared/messages" %> 25 | <%= yield %> 26 |
27 | <% else %> 28 | 32 | <% end %> 33 |
34 | <%= render "layouts/shared/footer" %> 35 | 36 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /spec/vulnerabilities/broken_auth_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "broken_auth" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | 7 | before do 8 | UserFixture.reset_all_users 9 | 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "one\nTutorial: https://github.com/OWASP/railsgoat/wiki/A2-Credential-Enumeration" do 14 | wrong_email = normal_user.email + "not" 15 | 16 | visit "/" 17 | within(".signup") do 18 | fill_in "email", with: wrong_email 19 | fill_in "password", with: normal_user.clear_password 20 | end 21 | within(".actions") do 22 | click_on "Login" 23 | end 24 | 25 | expect(find("div#flash_notice").text).not_to include(wrong_email) 26 | end 27 | 28 | scenario "two\nTutorial: https://github.com/OWASP/railsgoat/wiki/A2-Credential-Enumeration" do 29 | visit "/" 30 | within(".signup") do 31 | fill_in "email", with: normal_user.email 32 | fill_in "password", with: normal_user.clear_password + "not" 33 | end 34 | within(".actions") do 35 | click_on "Login" 36 | end 37 | 38 | expect(find("div#flash_notice").text).not_to include("Incorrect Password!") 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/vulnerabilities/xss_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "xss" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | 7 | before(:each) do 8 | UserFixture.reset_all_users 9 | 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "attack\nTutorial: https://github.com/OWASP/railsgoat/wiki/A3-Cross-Site-Scripting", js: true do 14 | login(normal_user) 15 | 16 | visit "/users/#{normal_user.id}/account_settings" 17 | within("#account_edit") do 18 | fill_in "First name", with: "" 19 | 20 | # password gets screwed up if you don't re-submit - need to fix 21 | fill_in "user_password", with: normal_user.clear_password 22 | fill_in "user_password_confirmation", with: normal_user.clear_password 23 | end 24 | click_on "Submit" 25 | 26 | sleep(1) 27 | 28 | visit "/users/#{normal_user.id}/account_settings" 29 | 30 | 31 | expect(find("#submit_button").value).not_to include("RailsGoat h4x0r3d") 32 | 33 | # might be nice to demonstrate posting cookie contents or somesuch, but 34 | # this at least shows the vulnerability still exists. 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/assets/javascripts/validation.js: -------------------------------------------------------------------------------- 1 | function validation(){ 2 | $("#account_edit").validate({ 3 | rules: { 4 | "user[password]": { 5 | required: false, 6 | minlength: 5 7 | }, 8 | "user[password_confirmation]": { 9 | required: false, 10 | minlength: 5, 11 | equalTo: "#user_password" 12 | } 13 | }, 14 | messages: { 15 | "user[password]": { 16 | minlength: "Your password must be at least 6 characters long" 17 | }, 18 | "user[password_confirmation]": { 19 | minlength: "Your password must be at least 6 characters long", 20 | equalTo: "Please enter the same password as above" 21 | } 22 | }, 23 | highlight: function(label) { 24 | $("#submit_button").attr('disabled','disabled'); 25 | $(label).closest('.control-group').addClass('error'); 26 | $('.error').css({"color": "red"}); 27 | }, 28 | success: function(label) { 29 | label.closest('.control-group').addClass('success'); 30 | $("#submit_button").removeAttr('disabled'); 31 | $('.error').css({"color": "#333333"}); 32 | } 33 | }); 34 | }; 35 | 36 | $(document).ready(validation()); -------------------------------------------------------------------------------- /app/controllers/messages_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class MessagesController < ApplicationController 3 | 4 | def index 5 | @messages = current_user.messages 6 | @message = Message.new 7 | sleep(3) 8 | end 9 | 10 | def show 11 | @message = Message.where(id: params[:id]).first 12 | end 13 | 14 | def destroy 15 | message = Message.where(id: params[:id]).first 16 | 17 | if message.destroy 18 | flash[:success] = "Your message has been deleted." 19 | redirect_to user_messages_path(user_id: current_user.id) 20 | else 21 | flash[:error] = "Could not delete message." 22 | end 23 | end 24 | 25 | def create 26 | if Message.create(message_params) 27 | respond_to do |format| 28 | format.html { redirect_to user_messages_path(user_id: current_user.id) } 29 | format.json { render json: {msg: "success"} } 30 | end 31 | else 32 | respond_to do |format| 33 | format.html { redirect_to user_messages_path } 34 | format.json { render json: {msg: "failure"} } 35 | end 36 | end 37 | end 38 | 39 | private 40 | 41 | def message_params 42 | params.require(:message).permit(:creator_id, :message, :read, :receiver_id) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/controllers/pay_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class PayController < ApplicationController 3 | 4 | def index 5 | end 6 | 7 | def update_dd_info 8 | msg = false 9 | pay = Pay.new( 10 | bank_account_num: params[:bank_account_num], 11 | bank_routing_num: params[:bank_routing_num], 12 | percent_of_deposit: params[:dd_percent], 13 | user_id: current_user.id 14 | ) 15 | 16 | msg = true if pay.save! 17 | respond_to do |format| 18 | format.json { render json: {msg: msg } } 19 | end 20 | end 21 | 22 | def show 23 | respond_to do |format| 24 | format.json { render json: {user: current_user.pay.as_json} } 25 | end 26 | end 27 | 28 | def destroy 29 | pay = Pay.find_by_id(params[:id]) 30 | if pay.present? and pay.destroy 31 | flash[:success] = "Successfully Deleted Entry" 32 | else 33 | flash[:error] = "Unable to process that request at this time" 34 | end 35 | redirect_to user_pay_index_path 36 | end 37 | 38 | def decrypted_bank_acct_num 39 | decrypted = Encryption.decrypt_sensitive_value(params[:value_to_decrypt]) 40 | respond_to do |format| 41 | format.json { render json: {account_num: decrypted || "No Data" } } 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | #### Azure Terraform Variables #### 2 | 3 | variable "initials" { 4 | description = "Enter your initials to include in URLs. Lowercase only!!!" 5 | default = "" 6 | } 7 | 8 | variable "location" { 9 | description = "The Azure location where all resources in this example should be created, to find your nearest run `az account list-locations -o table`" 10 | default = "" 11 | } 12 | 13 | variable "appname" { 14 | description = "The name of the app to display in Contrast TeamServer. Also used for DNS, so no spaces please!" 15 | default = "RailsGoat" 16 | } 17 | 18 | variable "servername" { 19 | description = "The name of the server to display in Contrast TeamServer." 20 | default = "railsgoat-docker" 21 | } 22 | 23 | variable "environment" { 24 | description = "The Contrast environment for the app. Valid values: development, qa or production" 25 | default = "development" 26 | } 27 | 28 | variable "session_metadata" { 29 | description = "See https://docs.contrastsecurity.com/user-vulnerableapps.html#session" 30 | default = "" 31 | } 32 | 33 | variable "run_automated_tests" { 34 | description = "Instructs the container to run the Ruby feature specs (integration tests)" 35 | default = false 36 | } 37 | 38 | variable "python_binary" { 39 | description = "Path to local Python binary" 40 | default = "python3" 41 | } -------------------------------------------------------------------------------- /spec/vulnerabilities/mass_assignment_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | 4 | feature "mass assignment" do 5 | let(:normal_user) { UserFixture.normal_user } 6 | 7 | before do 8 | UserFixture.reset_all_users 9 | pending unless verifying_fixed? 10 | end 11 | 12 | scenario "attack one" do 13 | expect(normal_user.admin).to be_falsey 14 | login(normal_user) 15 | 16 | params = { user: { admin: "t", 17 | id: normal_user.id, 18 | password: normal_user.clear_password, 19 | password_confirmation: normal_user.clear_password }} 20 | 21 | page.driver.put "/users/#{normal_user.id}.json", params 22 | 23 | expect(normal_user.reload.admin).to be_falsy 24 | end 25 | 26 | scenario "attack two, Tutorial: https://github.com/OWASP/railsgoat/wiki/R5-Extras-Mass-Assignment-Admin-Role" do 27 | params = { user: { admin: "t", 28 | email: "hackety@h4x0rs.c0m", 29 | first_name: "hackety", 30 | last_name: "hax", 31 | password: "foobarewe", 32 | password_confirmation: "foobarewe" }} 33 | 34 | page.driver.post "/users", params 35 | 36 | expect(User.find_by(email: "hackety@h4x0rs.c0m")).to be_nil 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | # Use this file to configure the Overcommit hooks you wish to use. This will 2 | # extend the default configuration defined in: 3 | # https://github.com/brigade/overcommit/blob/master/config/default.yml 4 | # 5 | # At the topmost level of this YAML file is a key representing type of hook 6 | # being run (e.g. pre-commit, commit-msg, etc.). Within each type you can 7 | # customize each hook, such as whether to only run it on certain files (via 8 | # `include`), whether to only display output if it fails (via `quiet`), etc. 9 | # 10 | # For a complete list of hooks, see: 11 | # https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook 12 | # 13 | # For a complete list of options that you can use to customize hooks, see: 14 | # https://github.com/brigade/overcommit#configuration 15 | # 16 | # Uncomment the following lines to make the configuration take effect. 17 | 18 | PreCommit: 19 | TrailingWhitespace: 20 | enabled: true 21 | exclude: 22 | - '**/db/structure.sql' # Ignore trailing whitespace in generated files 23 | 24 | PostCheckout: 25 | enabled: true 26 | ALL: # Special hook name that customizes all hooks of this type 27 | quiet: true # Change all post-checkout hooks to only display output on failure 28 | 29 | # IndexTags: 30 | # enabled: true # Generate a tags file with `ctags` each time HEAD changes 31 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source "https://rubygems.org" 3 | 4 | #don't upgrade 5 | gem "rails", "5.1.7" 6 | 7 | ruby "2.6.2" 8 | 9 | gem "aruba" 10 | gem "bcrypt" 11 | gem "coffee-rails" 12 | gem "execjs" 13 | gem "foreman" 14 | gem "jquery-fileupload-rails" 15 | gem "jquery-rails" 16 | gem "minitest" 17 | gem "powder" # Pow related gem 18 | gem "pry-rails" # not in dev group in case running via prod/staging @ a training 19 | gem "puma" 20 | gem "rails-perftest" 21 | gem "rake" 22 | gem "responders" #For Rails 4.2 # LOCKED DOWN 23 | gem "ruby-prof" 24 | gem "sass-rails" 25 | gem "simplecov", require: false, group: :test 26 | gem "sqlite3", "1.3.13" # 2/7/2019: LOCKED DOWN 27 | gem "therubyracer" 28 | gem "turbolinks" 29 | gem "uglifier" 30 | gem "unicorn" 31 | 32 | # Add SMTP server support using MailCatcher 33 | # NOTE: https://github.com/sj26/mailcatcher#bundler 34 | # gem 'mailcatcher' 35 | 36 | group :development, :mysql do 37 | gem "better_errors" 38 | gem "binding_of_caller" 39 | gem "bundler-audit" 40 | gem "guard-livereload" 41 | gem "guard-rspec" 42 | gem "guard-shell" 43 | gem "pry" 44 | gem "rack-livereload" 45 | gem "rb-fsevent" 46 | gem "rubocop-github" 47 | gem "travis-lint" 48 | end 49 | 50 | group :development, :test, :mysql do 51 | gem "capybara" 52 | gem "database_cleaner" 53 | gem "launchy" 54 | gem "poltergeist" 55 | gem "rspec-rails" 56 | gem "test-unit" 57 | end 58 | 59 | group :mysql do 60 | gem "mysql2" 61 | end 62 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

We're sorry, but something went wrong.

54 |
55 |

If you are the application owner check the logs for more information.

56 | 57 | 58 | -------------------------------------------------------------------------------- /app/views/password_resets/reset_password.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

MetaCorp

3 |

A GoatGroup Company

4 |
5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 35 | 36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The change you wanted was rejected.

54 |

Maybe you tried to change something you didn't have access to.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /app/controllers/api/v1/users_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Api::V1::UsersController < ApplicationController 3 | skip_before_action :authenticated 4 | before_action :valid_api_token 5 | before_action :extrapolate_user 6 | 7 | respond_to :json 8 | 9 | def index 10 | respond_with @user.admin ? User.all : @user 11 | end 12 | 13 | def show 14 | respond_with @user.as_json 15 | end 16 | 17 | private 18 | 19 | def valid_api_token 20 | authenticate_or_request_with_http_token do |token, options| 21 | # TODO :add some functionality to check if the HTTP Header is valid 22 | if !identify_user(token) 23 | redirect_to root_url 24 | end 25 | end 26 | end 27 | 28 | def identify_user(token = "") 29 | # We've had issues with URL encoding, etc. causing issues so just to be safe 30 | # we will go ahead and unescape the user's token 31 | unescape_token(token) 32 | @clean_token =~ /(.*?)-(.*)/ 33 | id = $1 34 | hash = $2 35 | 36 | check_hash(id, hash) 37 | end 38 | 39 | def check_hash(id, hash) 40 | digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}") 41 | hash == digest 42 | end 43 | 44 | # We had some issues with the token and url encoding... 45 | # this is an attempt to normalize the data. 46 | def unescape_token(token = "") 47 | @clean_token = CGI::unescape(token) 48 | end 49 | 50 | # Added a method to make it easy to figure out who the user is. 51 | def extrapolate_user 52 | @user = User.find_by_id(@clean_token.split("-").first) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/views/dashboard/bar_graph.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | 30 |
31 |
32 | 33 |
34 |
35 | 36 | <%= javascript_include_tag "validation.js"%> -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The page you were looking for doesn't exist.

54 |

You may have mistyped the address or the page may have moved.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /app/views/admin/analytics.html.erb: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | " id="data-table"> 10 | 11 | 12 | <% 13 | count = (params[:field] ? (custom_fields.count+1) : 6) 14 | count.times do %> 15 | 16 | <% end %> 17 | 18 | 19 | 20 | <% @analytics.each do |a|%> 21 | 22 | <% a.attributes.each do |k,v| %> 23 | 24 | <% end %> 25 | 26 | <% end %> 27 | 28 |
 
<%= v %>
29 | 31 |
32 |
33 |
34 | 35 | 36 | <%= javascript_include_tag "jquery.dataTables.min.js"%> 37 | 38 | 48 | -------------------------------------------------------------------------------- /app/views/admin/dashboard.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 9 |
10 |
11 | 12 |
13 |
14 | 18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 |
26 | Manage Users 27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 38 | <%= javascript_include_tag "jquery.dataTables.min.js"%> 39 | 40 | 55 | -------------------------------------------------------------------------------- /app/views/messages/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Messages for <%= current_user.full_name %> 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
From:DateMessageActions
<%= @message.creator_name %><%= @message.created_at.to_date %><%= @message.message %><%= link_to "Delete", user_message_path, {:id => "@message.id", :method => 'delete', :class => "btn btn-danger pull-left"}%>
30 |
31 |
32 |
33 |
34 |
35 |
36 | 46 | 47 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class UsersController < ApplicationController 3 | skip_before_action :has_info 4 | skip_before_action :authenticated, only: [:new, :create] 5 | 6 | def new 7 | @user = User.new 8 | end 9 | 10 | def create 11 | user = User.new(user_params) 12 | if user.save 13 | session[:user_id] = user.id 14 | redirect_to home_dashboard_index_path 15 | else 16 | @user = user 17 | flash[:error] = user.errors.full_messages.to_sentence 18 | redirect_to :signup 19 | end 20 | end 21 | 22 | def account_settings 23 | @user = current_user 24 | end 25 | 26 | def update 27 | message = false 28 | 29 | user = User.where("id = '#{params[:user][:id]}'")[0] 30 | 31 | if user 32 | user.update_attributes(user_params_without_password) 33 | if params[:user][:password].present? && (params[:user][:password] == params[:user][:password_confirmation]) 34 | user.password = params[:user][:password] 35 | end 36 | message = true if user.save! 37 | respond_to do |format| 38 | format.html { redirect_to user_account_settings_path(user_id: current_user.id) } 39 | format.json { render json: {msg: message ? "success" : "false "} } 40 | end 41 | else 42 | flash[:error] = "Could not update user!" 43 | redirect_to user_account_settings_path(user_id: current_user.id) 44 | end 45 | end 46 | 47 | private 48 | 49 | def user_params 50 | params.require(:user).permit! 51 | end 52 | 53 | # unpermitted attributes are ignored in production 54 | def user_params_without_password 55 | params.require(:user).permit(:email, :admin, :first_name, :last_name) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/vulnerabilities/csrf_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "spec_helper" 3 | require "tmpdir" 4 | 5 | feature "csrf" do 6 | let(:normal_user) { UserFixture.normal_user } 7 | 8 | before(:each) do 9 | UserFixture.reset_all_users 10 | pending unless verifying_fixed? 11 | end 12 | 13 | scenario "attack\nTutorial: https://github.com/OWASP/railsgoat/wiki/R5-A8-CSRF", js: true do 14 | visit "/" 15 | # TODO: is there a way to get this without visiting root first? 16 | base_url = current_url 17 | 18 | login(normal_user) 19 | 20 | Dir.mktmpdir do |dir| 21 | hackety_file = File.join(dir, "form.on.bad.guy.site.html") 22 | post_url = "#{base_url}schedule.json" 23 | File.open(hackety_file, "w") do |f| 24 | f.print <<-HTML 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | HTML 37 | end 38 | 39 | page.driver.visit "file://#{hackety_file}" 40 | within("#submit_me") do 41 | click_on "Submit request" 42 | end 43 | end 44 | 45 | expect(normal_user.reload.paid_time_off.schedule.last.event_name).not_to eq("Bad Guy") 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | #Terraform `provider` section is required since the `azurerm` provider update to 2.0+ 2 | provider "azurerm" { 3 | features { 4 | } 5 | } 6 | 7 | #Extract the connection from the normal yaml file to pass to the app container 8 | data "external" "yaml" { 9 | program = [var.python_binary, "${path.module}/parseyaml.py"] 10 | } 11 | 12 | #Set up a personal resource group for the SE local to them 13 | resource "azurerm_resource_group" "personal" { 14 | name = "Sales-Engineer-${var.initials}" 15 | location = var.location 16 | } 17 | 18 | #Set up a container group 19 | resource "azurerm_container_group" "app" { 20 | name = "${var.appname}-${var.initials}" 21 | location = azurerm_resource_group.personal.location 22 | resource_group_name = azurerm_resource_group.personal.name 23 | ip_address_type = "Public" 24 | dns_name_label = "${var.appname}-${var.initials}" 25 | os_type = "Linux" 26 | 27 | container { 28 | name = "web" 29 | image = "contrastsecuritydemo/railsgoat:1.0" 30 | cpu = "1" 31 | memory = "1.5" 32 | ports { 33 | port = 3000 34 | protocol = "TCP" 35 | } 36 | environment_variables = { 37 | CONTRAST__API__URL=data.external.yaml.result.url 38 | CONTRAST__API__API_KEY=data.external.yaml.result.api_key 39 | CONTRAST__API__SERVICE_KEY=data.external.yaml.result.service_key 40 | CONTRAST__API__USER_NAME=data.external.yaml.result.user_name 41 | CONTRAST__APPLICATION__NAME=var.appname 42 | CONTRAST__APPLICATION__SESSION_METADATA=var.session_metadata 43 | CONTRAST__SERVER__NAME=var.servername 44 | CONTRAST__SERVER__ENVIRONMENT=var.environment 45 | TEST=var.run_automated_tests 46 | } 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Railsgoat::Application.configure do 3 | # Settings specified here will take precedence over those in config/application.rb 4 | 5 | # The test environment is used exclusively to run your application's 6 | # test suite. You never need to work with it otherwise. Remember that 7 | # your test database is "scratch space" for the test suite and is wiped 8 | # and recreated between test runs. Don't rely on the data there! 9 | config.cache_classes = true 10 | 11 | # Configure static asset server for tests with Cache-Control for performance. 12 | config.public_file_server.enabled = true 13 | config.public_file_server.headers = { "Cache-Control" => "public, max-age=3600" } 14 | 15 | # Show full error reports and disable caching. 16 | config.consider_all_requests_local = true 17 | config.action_controller.perform_caching = false 18 | 19 | # Raise exceptions instead of rendering exception templates. 20 | config.action_dispatch.show_exceptions = false 21 | 22 | # Disable request forgery protection in test environment 23 | config.action_controller.allow_forgery_protection = true 24 | 25 | # Tell Action Mailer not to deliver emails to the real world. 26 | # The :test delivery method accumulates sent emails in the 27 | # ActionMailer::Base.deliveries array. 28 | config.action_mailer.delivery_method = :test 29 | 30 | # Print deprecation notices to the stderr. 31 | config.active_support.deprecation = :stderr 32 | 33 | # For Rails 4.0+ 34 | # Do not eager load code on boot. This avoids loading your whole application 35 | # just for the purpose of running a single test. If you are using a tool that 36 | # preloads Rails for running tests, you may have to set it to true. 37 | config.eager_load = false 38 | end 39 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Railsgoat::Application.routes.draw do 3 | 4 | get "login" => "sessions#new" 5 | get "signup" => "users#new" 6 | get "logout" => "sessions#destroy" 7 | 8 | get "forgot_password" => "password_resets#forgot_password" 9 | post "forgot_password" => "password_resets#send_forgot_password" 10 | get "password_resets" => "password_resets#confirm_token" 11 | post "password_resets" => "password_resets#reset_password" 12 | 13 | get "dashboard/doc" => "dashboard#doc" 14 | 15 | resources :sessions 16 | 17 | resources :users do 18 | get "account_settings" 19 | 20 | resources :retirement 21 | resources :paid_time_off 22 | resources :work_info 23 | resources :performance 24 | resources :benefit_forms 25 | resources :messages 26 | 27 | resources :pay do 28 | collection do 29 | post "update_dd_info" 30 | post "decrypted_bank_acct_num" 31 | end 32 | end 33 | 34 | end 35 | 36 | get "download" => "benefit_forms#download" 37 | post "upload" => "benefit_forms#upload" 38 | 39 | resources :tutorials do 40 | collection do 41 | get "credentials" 42 | end 43 | end 44 | 45 | resources :schedule do 46 | collection do 47 | get "get_pto_schedule" 48 | end 49 | end 50 | 51 | resources :admin do 52 | get "dashboard" 53 | get "get_user" 54 | post "delete_user" 55 | patch "update_user" 56 | get "get_all_users" 57 | get "analytics" 58 | end 59 | 60 | resources :dashboard do 61 | collection do 62 | get "home" 63 | get "change_graph" 64 | end 65 | end 66 | 67 | namespace :api, defaults: {format: "json"} do 68 | namespace :v1 do 69 | resources :users 70 | resources :mobile 71 | end 72 | end 73 | 74 | root to: "sessions#new" 75 | end 76 | -------------------------------------------------------------------------------- /app/controllers/schedule_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class ScheduleController < ApplicationController 3 | 4 | def create 5 | message = false 6 | 7 | if params[:schedule][:event_type] == "pto" 8 | sched = Schedule.new(schedule_params) 9 | sched.date_begin, sched.date_end = format_schedule_date(params[:date_range1]) 10 | sched.user_id = current_user.id 11 | a = sched.date_end 12 | if sched.save 13 | message = true 14 | end 15 | end 16 | 17 | respond_to do |format| 18 | format.json { render json: {msg: message ? "success" : "failure" } } 19 | end 20 | end 21 | 22 | def get_pto_schedule 23 | begin 24 | schedules = current_user.paid_time_off.schedule 25 | jfs = [] 26 | schedules.each do |s| 27 | hash = Hash.new 28 | hash[:id] = s[:id] 29 | hash[:title] = s[:event_name] 30 | hash[:start] = s[:date_begin] 31 | hash[:end] = s[:date_end] 32 | jfs << hash 33 | end 34 | rescue 35 | end 36 | respond_to do |format| 37 | format.json { render json: jfs.to_json } 38 | end 39 | end 40 | 41 | private 42 | 43 | # Returns a two part array consisting of dates 44 | # First value is the begin date and the second is the end date 45 | def format_schedule_date(date_array) 46 | begin 47 | vals = [] 48 | return vals if date_array.empty? 49 | date_array.split("-").each do |s| 50 | date = Date.strptime(s.strip, "%m/%d/%Y") 51 | vals <<(date) 52 | end 53 | rescue ArgumentError 54 | return [] 55 | end 56 | return vals 57 | end 58 | 59 | private 60 | 61 | def schedule_params 62 | params.require(:schedule).permit(:date_begin, :date_end, :event_desc, :event_name, :event_type) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /app/views/admin/get_all_users.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 8 | 11 | 14 | 17 | 18 | 19 | 20 | <% @users.each do |u|%> 21 | 22 | 25 | 28 | 31 | 34 | 35 | <% end %> 36 | 37 |
6 | Name 7 | 9 | Email 10 | 12 | Admin User 13 | 15 | Action 16 |
23 | <%= "#{u.first_name} #{u.last_name}"%> 24 | 26 | <%= u.email%> 27 | 29 | <%= u.admin ? %{ 32 | <%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%> 33 |
38 | 40 |
41 |
42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class ApplicationController < ActionController::Base 3 | before_action :authenticated, :has_info, :create_analytic, :mailer_options 4 | helper_method :current_user, :is_admin?, :sanitize_font 5 | 6 | # Our security guy keep talking about sea-surfing, cool story bro. 7 | # Prevent CSRF attacks by raising an exception. 8 | # For APIs, you may want to use :null_session instead. 9 | #protect_from_forgery with: :exception 10 | 11 | private 12 | 13 | def mailer_options 14 | ActionMailer::Base.default_url_options[:protocol] = request.protocol 15 | ActionMailer::Base.default_url_options[:host] = request.host_with_port 16 | end 17 | 18 | def current_user 19 | @current_user ||= ( 20 | User.find_by(auth_token: cookies[:auth_token].to_s) || 21 | User.find_by(id: session[:user_id].to_s) 22 | ) 23 | end 24 | 25 | def authenticated 26 | path = request.fullpath.present? ? root_url(url: request.fullpath) : root_url 27 | redirect_to path and reset_session if !current_user 28 | end 29 | 30 | def is_admin? 31 | current_user.admin if current_user 32 | end 33 | 34 | def administrative 35 | if !is_admin? 36 | redirect_to root_url 37 | end 38 | end 39 | 40 | def has_info 41 | redirect = false 42 | if current_user 43 | begin 44 | if !(current_user.retirement || current_user.paid_time_off || current_user.paid_time_off.schedule || current_user.work_info || current_user.performance) 45 | redirect = true 46 | end 47 | rescue 48 | redirect = true 49 | end 50 | end 51 | redirect_to home_dashboard_index_path if redirect 52 | end 53 | 54 | def create_analytic 55 | Analytics.create({ ip_address: request.remote_ip, referrer: request.referrer, user_agent: request.user_agent}) 56 | end 57 | 58 | def sanitize_font(css) 59 | css 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # A sample Guardfile 3 | # More info at https://github.com/guard/guard#readme 4 | 5 | guard "brakeman", run_on_start: true do 6 | watch(%r{^app/.+\.(erb|haml|rhtml|rb)$}) 7 | watch(%r{^config/.+\.rb$}) 8 | watch(%r{^lib/.+\.rb$}) 9 | watch("Gemfile") 10 | end 11 | 12 | guard :shell do 13 | watch(%r{^Gemfile|Gemfile.lock$}) { system("bundle-audit") } 14 | end 15 | 16 | guard "livereload", host: "railsgoat.dev", port: "35727" do 17 | watch(%r{app/views/.+\.(erb|haml|slim)$}) 18 | watch(%r{app/helpers/.+\.rb}) 19 | watch(%r{public/.+\.(css|js|html)}) 20 | watch(%r{config/locales/.+\.yml}) 21 | # Rails Assets Pipeline 22 | watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html))).*}) { |m| "/assets/#{m[3]}" } 23 | end 24 | 25 | 26 | guard :rspec, cmd: "bundle exec rspec" do 27 | watch(%r{^spec/.+_spec\.rb$}) 28 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } 29 | watch("spec/spec_helper.rb") { "spec" } 30 | 31 | # Rails example 32 | watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 33 | watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } 34 | watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } 35 | watch(%r{^spec/support/(.+)\.rb$}) { "spec" } 36 | watch("config/routes.rb") { "spec/routing" } 37 | watch("app/controllers/application_controller.rb") { "spec/controllers" } 38 | 39 | # Capybara features specs 40 | watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" } 41 | 42 | # Turnip features and steps 43 | watch(%r{^spec/acceptance/(.+)\.feature$}) 44 | watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance" } 45 | end 46 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # TODO: change to slim or alpine 2 | FROM ruby:2.6.2 3 | 4 | ARG username 5 | ARG service_key 6 | 7 | # Add build and runtime dependencies 8 | # TODO: separate build & runtime and purge build dependencies at the end 9 | RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs 10 | 11 | # Install phantomjs dependencies 12 | RUN apt-get update \ 13 | && apt-get install -y --no-install-recommends \ 14 | ca-certificates \ 15 | bzip2 \ 16 | libfontconfig \ 17 | && apt-get clean \ 18 | && rm -rf /var/lib/apt/lists/* 19 | 20 | # Install phantomjs & clean up 21 | RUN apt-get update \ 22 | && apt-get install -y --no-install-recommends \ 23 | curl \ 24 | && mkdir /tmp/phantomjs \ 25 | && curl -L https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 \ 26 | | tar -xj --strip-components=1 -C /tmp/phantomjs \ 27 | && cd /tmp/phantomjs \ 28 | && mv bin/phantomjs /usr/local/bin \ 29 | && cd \ 30 | && apt-get purge --auto-remove -y \ 31 | curl \ 32 | && apt-get clean \ 33 | && rm -rf /tmp/* /var/lib/apt/lists/* 34 | 35 | # Build and package the app 36 | RUN mkdir /myapp 37 | WORKDIR /myapp 38 | ADD Gemfile /myapp/Gemfile 39 | ADD Gemfile.lock /myapp/Gemfile.lock 40 | 41 | # Add Contrast agent 42 | RUN bundle add contrast-agent 43 | 44 | RUN bundle install 45 | 46 | ADD ./app /myapp/app 47 | ADD ./config /myapp/config 48 | ADD ./db /myapp/db 49 | ADD ./doc /myapp/doc 50 | ADD ./lib /myapp/lib 51 | ADD ./log /myapp/log 52 | ADD ./public /myapp/public 53 | ADD ./script /myapp/script 54 | ADD ./spec /myapp/spec 55 | RUN mkdir /myapp/tmp 56 | ADD ./vendor /myapp/vendor 57 | ADD ./config.ru /myapp/config.ru 58 | ADD ./entrypoint.sh /myapp/entrypoint.sh 59 | ADD ./Rakefile /myapp/Rakefile 60 | 61 | #Setup the database 62 | RUN rails db:setup 63 | 64 | # Make port 3000 available 65 | EXPOSE 3000 66 | 67 | # Start the app server 68 | ENTRYPOINT [ "/bin/bash", "-c", "/myapp/entrypoint.sh"] -------------------------------------------------------------------------------- /app/controllers/admin_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class AdminController < ApplicationController 3 | before_action :administrative, if: :admin_param, except: [:get_user] 4 | skip_before_action :has_info 5 | layout false, only: [:get_all_users, :get_user] 6 | 7 | def dashboard 8 | end 9 | 10 | def analytics 11 | if params[:field].nil? 12 | fields = "*" 13 | else 14 | fields = custom_fields.join(",") 15 | end 16 | 17 | if params[:ip] 18 | @analytics = Analytics.hits_by_ip(params[:ip], fields) 19 | else 20 | @analytics = Analytics.all 21 | end 22 | end 23 | 24 | def get_all_users 25 | @users = User.all 26 | end 27 | 28 | def get_user 29 | @user = User.find_by_id(params[:admin_id].to_s) 30 | arr = ["true", "false"] 31 | @admin_select = @user.admin ? arr : arr.reverse 32 | end 33 | 34 | def update_user 35 | user = User.find_by_id(params[:admin_id]) 36 | if user 37 | user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") }) 38 | pass = params[:user][:password] 39 | user.password = pass if !(pass.blank?) 40 | user.save! 41 | message = true 42 | end 43 | respond_to do |format| 44 | format.json { render json: { msg: message ? "success" : "failure"} } 45 | end 46 | end 47 | 48 | def delete_user 49 | user = User.find_by(id: params[:admin_id]) 50 | if user && !(current_user.id == user.id) 51 | # Call destroy here so that all association records w/ id are destroyed as well 52 | # Example user.retirement records would be destroyed 53 | user.destroy 54 | message = true 55 | end 56 | respond_to do |format| 57 | format.json { render json: { msg: message ? "success" : "failure"} } 58 | end 59 | end 60 | 61 | private 62 | 63 | def custom_fields 64 | params.require(:field).keys 65 | end 66 | helper_method :custom_fields 67 | 68 | def admin_param 69 | params[:admin_id] != "1" 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /config/environments/mysql.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Railsgoat::Application.configure do 3 | # Settings specified here will take precedence over those in config/application.rb 4 | 5 | # In the development environment your application's code is reloaded on 6 | # every request. This slows down response time but is perfect for development 7 | # since you don't have to restart the web server when you make code changes. 8 | config.cache_classes = false 9 | 10 | # Show full error reports and disable caching 11 | config.consider_all_requests_local = true 12 | config.action_controller.perform_caching = false 13 | 14 | # Don't care if the mailer can't send 15 | config.action_mailer.raise_delivery_errors = false 16 | 17 | # Print deprecation notices to the Rails logger 18 | config.active_support.deprecation = :log 19 | 20 | # Only use best-standards-support built into browsers 21 | config.action_dispatch.best_standards_support = :builtin 22 | 23 | # Tired of caching causing issues 24 | config.middleware.delete Rack::ETag 25 | 26 | # Do not compress assets 27 | config.assets.compress = false 28 | 29 | # Expands the lines which load the assets 30 | config.assets.debug = true 31 | 32 | # ActionMailer settings for email support 33 | config.action_mailer.delivery_method = :smtp 34 | config.action_mailer.smtp_settings = { address: "127.0.0.1", port: 1025 } 35 | config.action_mailer.default_url_options = { host: "127.0.0.1:3000" } 36 | 37 | # config.middleware.insert_before( 38 | # Rack::Lock, Rack::LiveReload, 39 | # :min_delay => 500, 40 | # :max_delay => 1000, 41 | # :port => 35727, 42 | # :host => 'railsgoat.dev', 43 | # :ignore => [ %r{dont/modify\.html$} ] 44 | # ) 45 | 46 | # For Rails 4.0+ 47 | # Do not eager load code on boot. This avoids loading your whole application 48 | # just for the purpose of running a single test. If you are using a tool that 49 | # preloads Rails for running tests, you may have to set it to true. 50 | config.eager_load = false 51 | end 52 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Railsgoat::Application.configure do 3 | # Settings specified here will take precedence over those in config/application.rb 4 | 5 | # In the development environment your application's code is reloaded on 6 | # every request. This slows down response time but is perfect for development 7 | # since you don't have to restart the web server when you make code changes. 8 | config.cache_classes = false 9 | 10 | # Show full error reports and disable caching 11 | config.consider_all_requests_local = true 12 | config.action_controller.perform_caching = false 13 | 14 | # Don't care if the mailer can't send 15 | config.action_mailer.raise_delivery_errors = false 16 | 17 | # Print deprecation notices to the Rails logger 18 | config.active_support.deprecation = :log 19 | 20 | # Only use best-standards-support built into browsers 21 | config.action_dispatch.best_standards_support = :builtin 22 | 23 | # Tired of caching causing issues 24 | config.middleware.delete Rack::ETag 25 | 26 | # Do not compress assets 27 | config.assets.compress = false 28 | 29 | # Expands the lines which load the assets 30 | config.assets.debug = true 31 | 32 | # ActionMailer settings for email support 33 | config.action_mailer.delivery_method = :smtp 34 | config.action_mailer.smtp_settings = { address: "127.0.0.1", port: 1025 } 35 | config.action_mailer.default_url_options = { host: "127.0.0.1:3000" } 36 | 37 | # config.middleware.insert_before( 38 | # Rack::Lock, Rack::LiveReload, 39 | # :min_delay => 500, 40 | # :max_delay => 1000, 41 | # :port => 35727, 42 | # :host => 'railsgoat.dev', 43 | # :ignore => [ %r{dont/modify\.html$} ] 44 | # ) 45 | 46 | # For Rails 4.0+ 47 | # Do not eager load code on boot. This avoids loading your whole application 48 | # just for the purpose of running a single test. If you are using a tool that 49 | # preloads Rails for running tests, you may have to set it to true. 50 | config.eager_load = false 51 | end 52 | -------------------------------------------------------------------------------- /app/views/dashboard/home.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Current Statistics 9 |
10 | 11 |
12 | 20 |
21 | 22 |
23 |
24 | <%#= render partial: "dashboard_stats" %> 25 |
26 |
27 |
28 |
29 |
30 |
Need help using this portal? Check out the Readme
31 |
32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require "encryption" 3 | 4 | class User < ApplicationRecord 5 | validates :password, presence: true, 6 | confirmation: true, 7 | length: {within: 6..40}, 8 | on: :create, 9 | if: :password 10 | 11 | validates_presence_of :email 12 | validates_uniqueness_of :email 13 | validates_format_of :email, with: /.+@.+\..+/i 14 | 15 | has_one :retirement, dependent: :destroy 16 | has_one :paid_time_off, dependent: :destroy 17 | has_one :work_info, dependent: :destroy 18 | has_many :performance, dependent: :destroy 19 | has_many :pay, dependent: :destroy 20 | has_many :messages, foreign_key: :receiver_id, dependent: :destroy 21 | 22 | before_save :hash_password 23 | after_create { generate_token(:auth_token) } 24 | before_create :build_benefits_data 25 | 26 | def build_benefits_data 27 | build_retirement(POPULATE_RETIREMENTS.sample) 28 | build_paid_time_off(POPULATE_PAID_TIME_OFF.sample).schedule.build(POPULATE_SCHEDULE.sample) 29 | build_work_info(POPULATE_WORK_INFO.sample) 30 | # Uncomment below line to use encrypted SSN(s) 31 | #work_info.build_key_management(:iv => SecureRandom.hex(32)) 32 | performance.build(POPULATE_PERFORMANCE.sample) 33 | end 34 | 35 | def full_name 36 | "#{self.first_name} #{self.last_name}" 37 | end 38 | 39 | private 40 | 41 | def self.authenticate(email, password) 42 | auth = nil 43 | user = find_by_email(email) 44 | raise "#{email} doesn't exist!" if !(user) 45 | if user.password == Digest::MD5.hexdigest(password) 46 | auth = user 47 | else 48 | raise "Incorrect Password!" 49 | end 50 | return auth 51 | end 52 | 53 | def hash_password 54 | if will_save_change_to_password? 55 | self.password = Digest::MD5.hexdigest(self.password) 56 | end 57 | end 58 | 59 | def generate_token(column) 60 | loop do 61 | self[column] = Encryption.encrypt_sensitive_value(self.id) 62 | break unless User.exists?(column => self[column]) 63 | end 64 | 65 | self.save! 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /app/assets/stylesheets/timepicker.css: -------------------------------------------------------------------------------- 1 | .bootstrap-timepicker.dropdown-menu { 2 | border-radius: 4px 4px 4px 4px; 3 | display: none; 4 | left: 0; 5 | margin-top: 1px; 6 | padding: 4px; 7 | color: #fff; 8 | top: 0; 9 | min-width: 10px; 10 | z-index: 99999; 11 | } 12 | .bootstrap-timepicker.dropdown-menu.open { 13 | display: inline-block; 14 | } 15 | .bootstrap-timepicker.dropdown-menu:before { 16 | border-bottom: 7px solid rgba(0, 0, 0, 0.2); 17 | border-left: 7px solid transparent; 18 | border-right: 7px solid transparent; 19 | content: ""; 20 | left: 6px; 21 | position: absolute; 22 | top: -7px; 23 | } 24 | .bootstrap-timepicker.dropdown-menu:after { 25 | border-bottom: 6px solid #000; 26 | border-left: 6px solid transparent; 27 | border-right: 6px solid transparent; 28 | content: ""; 29 | left: 7px; 30 | position: absolute; 31 | top: -6px; 32 | } 33 | .bootstrap-timepicker.modal { 34 | margin-left: -100px; 35 | margin-top: 0; 36 | top: 30%; 37 | width: 200px; 38 | left: 50% !important; 39 | } 40 | .bootstrap-timepicker.modal .modal-content { 41 | padding: 0; 42 | } 43 | .bootstrap-timepicker table { 44 | margin: 0; 45 | width: 100%; 46 | } 47 | .bootstrap-timepicker table td { 48 | height: 30px; 49 | margin: 0; 50 | padding: 2px; 51 | text-align: center; 52 | } 53 | .bootstrap-timepicker table td span { 54 | width: 100%; 55 | } 56 | .bootstrap-timepicker table td a { 57 | border: 1px solid transparent; 58 | display: inline-block; 59 | margin: 0; 60 | outline: 0 none; 61 | padding: 8px 0; 62 | width: 3em; 63 | } 64 | .bootstrap-timepicker table td a:hover { 65 | background-color: #EEEEEE; 66 | border-color: #DDDDDD; 67 | border-radius: 4px 4px 4px 4px; 68 | } 69 | .bootstrap-timepicker table td a i { 70 | margin-top: 2px; 71 | } 72 | .bootstrap-timepicker table td input { 73 | margin: 0; 74 | text-align: center; 75 | width: 25px; 76 | } 77 | .bootstrap-timepicker-component .add-on { 78 | cursor: pointer; 79 | } 80 | .bootstrap-timepicker-component .add-on i { 81 | display: block; 82 | height: 16px; 83 | width: 16px; 84 | } 85 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | 16 |
17 |
18 |

MetaCorp

19 |

A GoatGroup Company

20 |
21 |
22 |
23 | 24 | 53 |
54 |
55 |
56 | -------------------------------------------------------------------------------- /app/controllers/password_resets_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class PasswordResetsController < ApplicationController 3 | skip_before_action :authenticated 4 | 5 | def reset_password 6 | user = Marshal.load(Base64.decode64(params[:user])) unless params[:user].nil? 7 | 8 | if user && params[:password] && params[:confirm_password] && params[:password] == params[:confirm_password] 9 | user.password = params[:password] 10 | user.save! 11 | flash[:success] = "Your password has been reset please login" 12 | redirect_to :login 13 | else 14 | flash[:error] = "Error resetting your password. Please try again." 15 | redirect_to :login 16 | end 17 | end 18 | 19 | def confirm_token 20 | if !params[:token].nil? && is_valid?(params[:token]) 21 | flash[:success] = "Password reset token confirmed! Please create a new password." 22 | render "password_resets/reset_password" 23 | else 24 | flash[:error] = "Invalid password reset token. Please try again." 25 | redirect_to :login 26 | end 27 | end 28 | 29 | def send_forgot_password 30 | @user = User.find_by_email(params[:email]) unless params[:email].nil? 31 | 32 | if @user && password_reset_mailer(@user) 33 | flash[:success] = "Password reset email sent to #{params[:email]}" 34 | redirect_to :login 35 | else 36 | flash[:error] = "There was an issue sending password reset email to #{params[:email]}".html_safe unless params[:email].nil? 37 | end 38 | end 39 | 40 | private 41 | 42 | def password_reset_mailer(user) 43 | token = generate_token(user.id, user.email) 44 | UserMailer.forgot_password(user.email, token).deliver 45 | end 46 | 47 | def generate_token(id, email) 48 | hash = Digest::MD5.hexdigest(email) 49 | "#{id}-#{hash}" 50 | end 51 | 52 | def is_valid?(token) 53 | if token =~ /(?\d+)-(?[A-Z0-9]{32})/i 54 | 55 | # Fetch the user by their id, and hash their email address 56 | @user = User.find_by(id: $~[:user]) 57 | email = Digest::MD5.hexdigest(@user.email) 58 | 59 | # Compare and validate our hashes 60 | return true if email == $~[:email_hash] 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /app/views/work_info/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Employee Information 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
Full NameIncomeBonus/th> 18 | Years w/ MetaCorpSSNDoB
<%= "#{@user.first_name} #{@user.last_name}" %><%= @user.work_info.income %><%= @user.work_info.bonuses %><%= @user.work_info.years_worked %><%= @user.work_info.SSN %><%= @user.work_info.DoB %>
39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 | 65 | -------------------------------------------------------------------------------- /app/assets/javascripts/load-image.min.js: -------------------------------------------------------------------------------- 1 | (function(a){"use strict";var b=function(a,c,d){var e=document.createElement("img"),f,g;return e.onerror=c,e.onload=function(){g&&(!d||!d.noRevoke)&&b.revokeObjectURL(g),c(b.scale(e,d))},window.Blob&&a instanceof Blob||window.File&&a instanceof File?(f=g=b.createObjectURL(a),e._type=a.type):f=a,f?(e.src=f,e):b.readFile(a,function(a){var b=a.target;b&&b.result?e.src=b.result:c(a)})},c=window.createObjectURL&&window||window.URL&&URL.revokeObjectURL&&URL||window.webkitURL&&webkitURL;b.detectSubsampling=function(a){var b=a.width,c=a.height,d,e;return b*c>1048576?(d=document.createElement("canvas"),d.width=d.height=1,e=d.getContext("2d"),e.drawImage(a,-b+1,0),e.getImageData(0,0,1,1).data[3]===0):!1},b.detectVerticalSquash=function(a,b){var c=document.createElement("canvas"),d=c.getContext("2d"),e,f,g,h,i;c.width=1,c.height=b,d.drawImage(a,0,0),e=d.getImageData(0,0,1,b).data,f=0,g=b,h=b;while(h>f)i=e[(h-1)*4+3],i===0?g=h:f=h,h=g+f>>1;return h/b},b.renderImageToCanvas=function(a,c,d,e){var f=a.width,g=a.height,h=c.getContext("2d"),i,j=1024,k=document.createElement("canvas"),l,m,n,o,p;h.save(),b.detectSubsampling(a)&&(f/=2,g/=2),i=b.detectVerticalSquash(a,g),k.width=k.height=j,l=k.getContext("2d"),m=0;while(mg?g-m:j,o=0;while(of?f-o:j,l.clearRect(0,0,j,j),l.drawImage(a,-o,-m),h.drawImage(k,0,0,p,n,Math.floor(o*d/f),Math.floor(m*e/g/i),Math.ceil(p*d/f),Math.ceil(n*e/g/i)),o+=j;m+=j}h.restore(),k=l=null},b.scale=function(a,c){c=c||{};var d=document.createElement("canvas"),e=a.width,f=a.height,g=Math.max((c.minWidth||e)/e,(c.minHeight||f)/f);return g>1&&(e=parseInt(e*g,10),f=parseInt(f*g,10)),g=Math.min((c.maxWidth||e)/e,(c.maxHeight||f)/f),g<1&&(e=parseInt(e*g,10),f=parseInt(f*g,10)),a.getContext||c.canvas&&d.getContext?(d.width=e,d.height=f,a._type==="image/jpeg"?b.renderImageToCanvas(a,d,e,f):d.getContext("2d").drawImage(a,0,0,e,f),d):(a.width=e,a.height=f,a)},b.createObjectURL=function(a){return c?c.createObjectURL(a):!1},b.revokeObjectURL=function(a){return c?c.revokeObjectURL(a):!1},b.readFile=function(a,b){if(window.FileReader&&FileReader.prototype.readAsDataURL){var c=new FileReader;return c.onload=c.onerror=b,c.readAsDataURL(a),c}return!1},typeof define=="function"&&define.amd?define(function(){return b}):a.loadImage=b})(this); -------------------------------------------------------------------------------- /app/assets/javascripts/html5.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); 8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d :controller do 59 | # # Equivalent to being in spec/controllers 60 | # end 61 | config.infer_spec_type_from_file_location! 62 | end 63 | 64 | Capybara.javascript_driver = :poltergeist 65 | 66 | DatabaseCleaner.strategy = :truncation 67 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require turbolinks 16 | //= require jquery.min.js 17 | //= require jquery.scrollUp.js 18 | //= require bootstrap.js 19 | //= require bootstrap-colorpicker.js 20 | //= require date-picker/date.js 21 | //= require date-picker/daterangepicker.js 22 | //= require bootstrap-timepicker.js 23 | //= require jquery.bootstrap.wizard.js 24 | //= require tiny-scrollbar.js 25 | //= require jquery.validate.min.js 26 | //= require jquery.snippet.js 27 | //= require jquery.easy-pie-chart.js 28 | //= require jquery-fileupload/basic 29 | //= require jquery-fileupload/vendor/tmpl 30 | //= require jsapi 31 | //= html5.js 32 | 33 | function rubyCodeFormat() { 34 | 35 | 36 | 37 | $("pre.ruby").snippet("ruby",{style:"rand01",transparent:true,showNum:true}); 38 | // Finds
 elements with the class "js"
39 |     // and snippet highlights the JAVASCRIPT code within
40 |     // using a random style from the selection of 39
41 |     // with a transparent background
42 |     // without showing line numbers.
43 | 
44 | 
45 | 
46 | $("pre.javascript").snippet("javascript",{style:"rand01",transparent:true,showNum:true});
47 |     // Finds 
 elements with the class "js"
48 |     // and snippet highlights the JAVASCRIPT code within
49 |     // using a random style from the selection of 39
50 |     // with a transparent background
51 |     // without showing line numbers.
52 | 
53 | };
54 | 
55 | function coerceToString(val) {
56 |   return String((val === null || val === undefined) ? '' : val);
57 | }
58 | 
59 | var rAmp = /&/g,
60 |      rLt = //g,
62 |      rApos = /\'/g,
63 |      rQuot = /\"/g,
64 |      hChars = /[&<>\"\']/;
65 | 
66 | function hoganEscape(str) {
67 |     str = coerceToString(str);
68 |     return hChars.test(str) ?
69 |       str
70 |         .replace(rAmp, '&')
71 |         .replace(rLt, '<')
72 |         .replace(rGt, '>')
73 |         .replace(rApos, ''')
74 |         .replace(rQuot, '"') :
75 |       str;
76 |   }
77 | 
78 | $(document).ready(function(){
79 | 	rubyCodeFormat()
80 | });
81 | 


--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | require File.expand_path("../boot", __FILE__)
 3 | 
 4 | require "rails/all"
 5 | 
 6 | # Require the gems listed in Gemfile, including any gems
 7 | # you've limited to :test, :development, or :production.
 8 | Bundler.require(:default, Rails.env)
 9 | 
10 | module Railsgoat
11 |   class Application < Rails::Application
12 |     # Settings in config/environments/* take precedence over those specified here.
13 |     # Application configuration should go into files in config/initializers
14 |     # -- all .rb files in that directory are automatically loaded.
15 | 
16 |     # Custom directories with classes and modules you want to be autoloadable.
17 |     # config.autoload_paths += %W(#{config.root}/extras)
18 | 
19 |     # Only load the plugins named here, in the order given (default is alphabetical).
20 |     # :all can be used as a placeholder for all plugins not explicitly named.
21 |     # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
22 | 
23 |     # Activate observers that should always be running.
24 |     # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
25 | 
26 |     # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
27 |     # Run "rails -D time" for a list of tasks for finding time zone names. Default is UTC.
28 |     # config.time_zone = 'Central Time (US & Canada)'
29 | 
30 |     # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
31 |     # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
32 |     # config.i18n.default_locale = :de
33 | 
34 |     # Configure the default encoding used in templates for Ruby 1.9.
35 |     config.encoding = "utf-8"
36 | 
37 |     # Configure sensitive parameters which will be filtered from the log file.
38 |     config.filter_parameters += [:password]
39 | 
40 |     # Enable escaping HTML in JSON.
41 |     #config.active_support.escape_html_entities_in_json = true
42 | 
43 |     # Use SQL instead of Active Record's schema dumper when creating the database.
44 |     # This is necessary if your schema can't be completely dumped by the schema dumper,
45 |     # like if you have constraints or database-specific column types
46 |     # config.active_record.schema_format = :sql
47 | 
48 |     # Enable the asset pipeline
49 |     config.assets.enabled = true
50 | 
51 |     # add app/assets/fonts to the asset path
52 |     config.assets.paths << Rails.root.join("app", "assets", "fonts")
53 | 
54 |     # Version of your assets, change this if you want to expire all your assets
55 |     config.assets.version = "1.0"
56 | 
57 |     I18n.config.enforce_available_locales = false
58 |   end
59 | end
60 | 


--------------------------------------------------------------------------------
/app/assets/javascripts/jquery.scrollUp.js:
--------------------------------------------------------------------------------
 1 | /*
 2 | 
 3 | scrollUp v1.0.0
 4 | Author: Mark Goodyear - http://www.markgoodyear.com
 5 | Git: https://github.com/markgoodyear/scrollup
 6 | 
 7 | Copyright 2013 Mark Goodyear
 8 | Licensed under the MIT license
 9 | http://www.opensource.org/licenses/mit-license.php
10 | 
11 | Twitter: @markgdyr
12 | 
13 | */
14 | 
15 | ;(function ($) {
16 | 
17 | 	$.scrollUp = function (options) {
18 | 
19 | 		// Settings
20 | 		var settings = {
21 | 			scrollName: 'scrollUp', // Element ID
22 | 			topDistance: '300', // Distance from top before showing element (px)
23 | 			topSpeed: 300, // Speed back to top (ms)
24 | 			animation: 'fade', // Fade, slide, none
25 | 			animationInSpeed: 200, // Animation in speed (ms)
26 | 			animationOutSpeed: 200, // Animation out speed (ms)
27 | 			scrollText: 'Scroll to top', // Text for element
28 | 			activeOverlay: false // Set CSS color to display scrollUp active point, e.g '#00FFFF'
29 | 		};
30 | 
31 | 		// Load settings
32 | 		if (options) {
33 | 			var settings = $.extend(settings, options);
34 | 		}
35 | 
36 | 		// Shorthand setting names
37 | 		var sn = '#' + settings.scrollName,
38 | 			an = settings.animation,
39 | 			os = settings.animationOutSpeed,
40 | 			is = settings.animationInSpeed,
41 | 			td = settings.topDistance,
42 | 			st = settings.scrollText,
43 | 			ts = settings.topSpeed,
44 | 			ao = settings.activeOverlay;
45 | 
46 | 		// Create element
47 | 		$('', {
48 | 		    id: settings.scrollName,
49 | 		    href: '#top',
50 | 		    title: st,
51 | 		    text: st
52 | 		}).appendTo('body');
53 | 
54 | 		// Minium CSS to make the magic happen
55 | 		$(sn).css({
56 | 			'display':'none',
57 | 			'position': 'fixed',
58 | 			'z-index': '2147483647'
59 | 		})
60 | 
61 | 		// Active point overlay
62 | 		if (ao) {
63 | 			$("body").append("
"); 64 | $(sn+"-active").css({ 'position': 'absolute', 'top': td+'px', 'width': '100%', 'border-top': '1px dotted '+ao, 'z-index': '2147483647' }) 65 | } 66 | 67 | // Scroll funtion 68 | $(window).scroll(function(){ 69 | 70 | // Fade animation 71 | if (an === "fade") { 72 | $( ($(window).scrollTop() > td) ? $(sn).fadeIn(is) : $(sn).fadeOut(os) ); 73 | } 74 | 75 | // SlideUp animation 76 | else if (an === "slide") { 77 | $( ($(window).scrollTop() > td) ? $(sn).slideDown(is) : $(sn).slideUp(os) ); 78 | } 79 | 80 | // No animation 81 | else { 82 | $( ($(window).scrollTop() > td) ? $(sn).show(0) : $(sn).hide(0) ); 83 | } 84 | 85 | }); 86 | 87 | // Back to the top 88 | $(sn).click( function(event) { 89 | $('html, body').animate({scrollTop:0}, ts); 90 | return false; 91 | }); 92 | 93 | }; // End scrollUp function 94 | }(jQuery)); -------------------------------------------------------------------------------- /app/assets/javascripts/tiny-scrollbar.js: -------------------------------------------------------------------------------- 1 | (function(a){a.tiny=a.tiny||{};a.tiny.scrollbar={options:{axis:"y",wheel:40,scroll:true,lockscroll:true,size:"auto",sizethumb:"auto",invertscroll:false}};a.fn.tinyscrollbar=function(d){var c=a.extend({},a.tiny.scrollbar.options,d);this.each(function(){a(this).data("tsb",new b(a(this),c))});return this};a.fn.tinyscrollbar_update=function(c){return a(this).data("tsb").update(c)};function b(q,g){var k=this,t=q,j={obj:a(".viewport",q)},h={obj:a(".overview",q)},d={obj:a(".scrollbar",q)},m={obj:a(".track",d.obj)},p={obj:a(".thumb",d.obj)},l=g.axis==="x",n=l?"left":"top",v=l?"Width":"Height",r=0,y={start:0,now:0},o={},e="ontouchstart" in document.documentElement;function c(){k.update();s();return k}this.update=function(z){j[g.axis]=j.obj[0]["offset"+v];h[g.axis]=h.obj[0]["scroll"+v];h.ratio=j[g.axis]/h[g.axis];d.obj.toggleClass("disable",h.ratio>=1);m[g.axis]=g.size==="auto"?j[g.axis]:g.size;p[g.axis]=Math.min(m[g.axis],Math.max(0,(g.sizethumb==="auto"?(m[g.axis]*h.ratio):g.sizethumb)));d.ratio=g.sizethumb==="auto"?(h[g.axis]/m[g.axis]):(h[g.axis]-j[g.axis])/(m[g.axis]-p[g.axis]);r=(z==="relative"&&h.ratio<=1)?Math.min((h[g.axis]-j[g.axis]),Math.max(0,r)):0;r=(z==="bottom"&&h.ratio<=1)?(h[g.axis]-j[g.axis]):isNaN(parseInt(z,10))?r:parseInt(z,10);w()};function w(){var z=v.toLowerCase();p.obj.css(n,r/d.ratio);h.obj.css(n,-r);o.start=p.obj.offset()[n];d.obj.css(z,m[g.axis]);m.obj.css(z,m[g.axis]);p.obj.css(z,p[g.axis])}function s(){if(!e){p.obj.bind("mousedown",i);m.obj.bind("mouseup",u)}else{j.obj[0].ontouchstart=function(z){if(1===z.touches.length){i(z.touches[0]);z.stopPropagation()}}}if(g.scroll&&window.addEventListener){t[0].addEventListener("DOMMouseScroll",x,false);t[0].addEventListener("mousewheel",x,false)}else{if(g.scroll){t[0].onmousewheel=x}}}function i(A){a("body").addClass("noSelect");var z=parseInt(p.obj.css(n),10);o.start=l?A.pageX:A.pageY;y.start=z=="auto"?0:z;if(!e){a(document).bind("mousemove",u);a(document).bind("mouseup",f);p.obj.bind("mouseup",f)}else{document.ontouchmove=function(B){B.preventDefault();u(B.touches[0])};document.ontouchend=f}}function x(B){if(h.ratio<1){var A=B||window.event,z=A.wheelDelta?A.wheelDelta/120:-A.detail/3;r-=z*g.wheel;r=Math.min((h[g.axis]-j[g.axis]),Math.max(0,r));p.obj.css(n,r/d.ratio);h.obj.css(n,-r);if(g.lockscroll||(r!==(h[g.axis]-j[g.axis])&&r!==0)){A=a.event.fix(A);A.preventDefault()}}}function u(z){if(h.ratio<1){if(g.invertscroll&&e){y.now=Math.min((m[g.axis]-p[g.axis]),Math.max(0,(y.start+(o.start-(l?z.pageX:z.pageY)))))}else{y.now=Math.min((m[g.axis]-p[g.axis]),Math.max(0,(y.start+((l?z.pageX:z.pageY)-o.start))))}r=y.now*d.ratio;h.obj.css(n,-r);p.obj.css(n,y.now)}}function f(){a("body").removeClass("noSelect");a(document).unbind("mousemove",u);a(document).unbind("mouseup",f);p.obj.unbind("mouseup",f);document.ontouchmove=document.ontouchend=null}return c()}}(jQuery)); -------------------------------------------------------------------------------- /app/assets/javascripts/bootstrap-image-gallery-main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap Image Gallery JS Example 2.9 3 | * https://github.com/blueimp/Bootstrap-Image-Gallery 4 | * 5 | * Copyright 2012, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | */ 11 | 12 | /*jslint unparam: true */ 13 | /*global window, document, $ */ 14 | 15 | $(function () { 16 | 'use strict'; 17 | 18 | // Start slideshow button: 19 | $('#start-slideshow').button().click(function () { 20 | var options = $(this).data(), 21 | modal = $(options.target), 22 | data = modal.data('modal'); 23 | if (data) { 24 | $.extend(data.options, options); 25 | } else { 26 | options = $.extend(modal.data(), options); 27 | } 28 | modal.find('.modal-slideshow').find('i') 29 | .removeClass('icon-play') 30 | .addClass('icon-pause'); 31 | modal.modal(options); 32 | }); 33 | 34 | // Toggle fullscreen button: 35 | $('#toggle-fullscreen').button().click(function () { 36 | var button = $(this), 37 | root = document.documentElement; 38 | if (!button.hasClass('active')) { 39 | $('#modal-gallery').addClass('modal-fullscreen'); 40 | if (root.webkitRequestFullScreen) { 41 | root.webkitRequestFullScreen( 42 | window.Element.ALLOW_KEYBOARD_INPUT 43 | ); 44 | } else if (root.mozRequestFullScreen) { 45 | root.mozRequestFullScreen(); 46 | } 47 | } else { 48 | $('#modal-gallery').removeClass('modal-fullscreen'); 49 | (document.webkitCancelFullScreen || 50 | document.mozCancelFullScreen || 51 | $.noop).apply(document); 52 | } 53 | }); 54 | 55 | // Load images via flickr for demonstration purposes: 56 | // $.ajax({ 57 | // url: 'http://api.flickr.com/services/rest/', 58 | // data: { 59 | // format: 'json', 60 | // method: 'flickr.interestingness.getList', 61 | // api_key: '7617adae70159d09ba78cfec73c13be3' 62 | // }, 63 | // dataType: 'jsonp', 64 | // jsonp: 'jsoncallback' 65 | // }).done(function (data) { 66 | // var gallery = $('#gallery'), 67 | // url; 68 | // $.each(data.photos.photo, function (index, photo) { 69 | // url = 'http://farm' + photo.farm + '.static.flickr.com/' + 70 | // photo.server + '/' + photo.id + '_' + photo.secret; 71 | // $('
') 72 | // .append($('').prop('src', url + '_s.jpg')) 73 | // .prop('href', url + '_b.jpg') 74 | // .prop('title', photo.title) 75 | // .appendTo(gallery); 76 | // }); 77 | // }); 78 | }); -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Ways to Contribute to RailsGoat 2 | 3 | Thanks for your interest on contributing to RailsGoat! 4 | Here are a few general guidelines on contributing and reporting 5 | bugs to RailsGoat that we ask you to take a look first. 6 | Notice that all of your interactions in the project are 7 | expected to follow our [Code of Conduct](./CODE_OF_CONDUCT.md). 8 | 9 | ## Reporting Issues 10 | 11 | Before reporting a new issue, please be sure that the issue wasn't 12 | already reported or fixed by searching on GitHub through our 13 | [issues](https://github.com/OWASP/railsgoat/issues). 14 | 15 | When creating a new issue, be sure to include a **title and clear description**, 16 | as much relevant information as possible, and either a test case example or 17 | even better a **sample Rails app that replicates the issue** - 18 | RailsGoat has a lot of moving parts and it's functionality can be affected 19 | by third party gems, so we need as much context and details as possible 20 | to identify what might be broken for you. 21 | 22 | Avoid opening new issues to ask questions in our issues tracker. 23 | Please go through the project wiki, documentation and source code first, 24 | or try to ask your question in our 25 | [Slack Channel](https://owasp.slack.com/messages/C04THC44W). 26 | 27 | ## Sending Pull Requests 28 | 29 | Before sending a new Pull Request, take a look on existing Pull Requests 30 | and Issues to see if the proposed change or fix has been discussed in 31 | the past, or if the change was already implemented but not yet released. 32 | 33 | We expect new Pull Requests to include enough tests for new or changed 34 | behavior, and we aim to maintain everything as most backwards compatible 35 | as possible, reserving breaking changes to be ship in major releases 36 | when necessary 37 | 38 | If your Pull Request includes new or changed behavior, be sure that the 39 | changes are beneficial to a wide range of use cases or it's an application 40 | specific change that might not be so valuable to other applications. 41 | 42 | We also welcome Pull Requests that improve our existing documentation 43 | (both our `README.md` and the doc sections in the source code). 44 | 45 | ## Other Ways to Contribute 46 | 47 | We welcome anyone that wants to contribute to RailsGoat to triage 48 | and reply to open issues to help troubleshoot and fix existing bugs 49 | on RailsGoat. Here is what you can do: 50 | 51 | * Help ensure that existing issues follows the recommendations from the 52 | [Reporting Issues template](./ISSUE_TEMPLATE.md), 53 | providing feeback to the issue's author on what might be missing. 54 | * Review and update the existing content of our 55 | [Wiki](https://github.com/OWASP/railsgoat/wiki) 56 | with up to date instructions and code samples - the wiki was grown 57 | with several different tutorials and references that we can't keep 58 | track of everything, so if there is a page that showcases an integration 59 | or customization that you are familiar with feel free to update it 60 | as necessary. 61 | * Review existing Pull Requests, and testing patches against real 62 | existing applications that use RailsGoat. 63 | 64 | Thanks again for your interest on contributing to the project! 65 | 66 | :heart: 67 | -------------------------------------------------------------------------------- /app/views/admin/get_user.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 10 | 46 | 53 | <% end %> 54 | 55 | 56 | <%= javascript_include_tag ('validation.js')%> 57 | 58 | 97 | -------------------------------------------------------------------------------- /app/views/layouts/shared/_header.html.erb: -------------------------------------------------------------------------------- 1 | <% if current_user %> 2 |
3 | 4 | 5 | Font Size: 6 | A 7 | A 8 | 9 | 10 | 24 |
    25 |
  • 26 | 31 | Welcome, <%= current_user.first_name.html_safe %> 32 |
  • 33 |
34 |
    35 |
  • 36 | <%= button_to "Visit Tutorial", nil, 37 | { 38 | :class => "btn", 39 | :method => "get", 40 | :onclick => "window.open('https://github.com/OWASP/railsgoat/wiki', '_blank')" 41 | } %> 42 |
  • 43 |
44 |
45 | <% else %> 46 | 47 |
48 |
    49 |
  • 50 | <%= button_to "Signup", signup_path, {:class => "btn btn-primary", :method => "get"} %> 51 |
  • 52 |
53 |
    54 |
  • 55 | <%= button_to "login", login_path, {:class => "btn", :method => "get"} %> 56 |
  • 57 |
58 |
    59 |
  • 60 | <%= button_to "Tutorial Credentials", "#myModalLabel1", {:id => "show_creds_btn", :class => "btn btn-danger", :method => "get"} %> 61 |
  • 62 |
63 |
    64 |
  • 65 | <%= button_to "Visit Tutorial", nil, 66 | { 67 | :class => "btn", 68 | :method => "get", 69 | :onclick => "window.open('https://github.com/OWASP/railsgoat/wiki', '_blank')" 70 | } %> 71 |
  • 72 |
73 |
74 | 75 | 77 | 78 | 86 | 87 | <% end %> 88 | 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RailsGoat: A deliberately insecure Ruby web application 2 | 3 | This is a Ruby demo application, based on https://github.com/OWASP/railsgoat. 4 | 5 | **Warning**: The computer running this application will be vulnerable to attacks, please take appropriate precautions. 6 | 7 | # Running standalone 8 | 9 | You can run RailGoat locally on any machine with Ruby and Rails 5.x installed. 10 | 11 | 1. Place a `contrast_security.yaml` file into the application's root folder. 12 | 13 | 1. Install the Contrast agent using: 14 | ```sh 15 | bundle add contrast-agent 16 | bundle install 17 | ``` 18 | 3. Initialize the database: 19 | ```sh 20 | rails db:setup 21 | ``` 22 | 4. Start the Thin web server: 23 | 24 | ```sh 25 | rails server 26 | ``` 27 | 5. Browse the application at http://localhost:3000 28 | 29 | # Running in Docker 30 | 31 | You can run RailsGoat within a Docker container, tested on OSX. The agent is added automatically during the Docker build process. 32 | 33 | 1. Place a `contrast_security.yaml` file into the application's root folder. 34 | 1. Build the RailsGoat container image using `./1-Build-Docker-Image.sh` 35 | 1. Run the container using 36 | ```sh 37 | docker run \ 38 | -v $PWD/contrast_security.yaml:/myapp/contrast_security.yaml \ 39 | -e CONTRAST__APPLICATION__NAME=railsgoat \ 40 | -p 3000:3000 railsgoat:latest 41 | ``` 42 | 4. Browse the application at http://localhost:3000 43 | 44 | # Running in Azure (Azure App Service): 45 | 46 | ## Pre-Requisites 47 | 48 | 1. Place a `contrast_security.yaml` file into the application's root folder. 49 | 1. Install Terraform from here: https://www.terraform.io/downloads.html. 50 | 1. Install PyYAML using `pip install PyYAML`. 51 | 1. Install the Azure cli tools using `brew update && brew install azure-cli`. 52 | 1. Log into Azure to make sure you cache your credentials using `az login`. 53 | 1. Edit the [variables.tf](variables.tf) file (or add a terraform.tfvars) to add your initials, preferred Azure location, app name, server name and environment. 54 | 1. Run `terraform init` to download the required plugins. 55 | 1. Run `terraform plan` and check the output for errors. 56 | 1. Run `terraform apply` to build the infrastructure that you need in Azure, this will output the web address for the application. 57 | 1. Run `terraform destroy` when you would like to stop the app service and release the resources. 58 | 59 | ## Running automated tests 60 | 61 | RailsGoat includes a set of failing Capybara RSpecs, each one indicating that a separate vulnerability exists in the application. To run them, you first need to install [PhantomJS](https://github.com/jonleighton/poltergeist#installing-phantomjs) (version 2.1.1 has been tested in Dev and on Travis CI), which is required by the Poltergeist Capybara driver. Upon installation, simply run the following task: 62 | 63 | ```sh 64 | rails training 65 | ``` 66 | 67 | For Docker run: 68 | 69 | ```sh 70 | docker run \ 71 | -v $PWD/contrast_security.yaml:/myapp/contrast_security.yaml \ 72 | -e CONTRAST__APPLICATION__NAME=railsgoat \ 73 | -e TEST=true \ 74 | -p 3000:3000 railsgoat:latest 75 | ``` 76 | 77 | ## Updating the Docker Image 78 | 79 | You can re-build the docker image (used by Terraform) by running two scripts in order: 80 | 81 | * 1-Build-Docker-Image.sh 82 | * 2-Deploy-Docker-Image-To-Docker-Hub.sh 83 | 84 | # License 85 | 86 | [The MIT License (MIT)](./LICENSE.md) 87 | -------------------------------------------------------------------------------- /spec/support/capybara_shared.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # By default this will return true, and thus all of the Capybara specs will 3 | # fail until a developer using the site for training has patched up all of 4 | # the vulnerabilities. 5 | # 6 | # However, RailsGoat maintainers need the Capybara features to pass to indicate 7 | # changes to the site have not inadvertently removed or fixed any vulnerabilities 8 | # since the whole point is to provide a site for a developer to fix. 9 | $displayed_spec_notice = false 10 | 11 | def verifying_fixed? 12 | maintainer_env_name = "RAILSGOAT_MAINTAINER" 13 | result = !ENV[maintainer_env_name] 14 | if !$displayed_spec_notice && result 15 | puts <<-NOTICE 16 | 17 | ****************************************************************************** 18 | You are running the RailsGoat Capybara Specs in Training mode. These specs 19 | are supposed to fail, indicating vulnerabilities exist. They contain spoilers, 20 | so do not read the code in spec/vulnerabilities if your goal is to learn more 21 | about patching the vulnerabilities. You should fix the vulnerabilities in the 22 | application in order to get these specs to pass**. You can use them to measure 23 | your progress. 24 | 25 | These same specs will pass if you set the #{maintainer_env_name} ENV variable. 26 | 27 | **NOTE: The RSpec pending feature is used to toggle the outcome of these specs 28 | between Training mode and RailsGoat Maintainer mode. When the vulnerabilities 29 | are removed, the specs will pass instead. Try to get a fully passing suite. 30 | ****************************************************************************** 31 | 32 | NOTICE 33 | $displayed_spec_notice = true 34 | end 35 | result 36 | end 37 | 38 | def login(user) 39 | visit "/" 40 | within(".signup") do 41 | fill_in "email", with: user.email 42 | fill_in "password", with: user.clear_password 43 | end 44 | within(".actions") do 45 | click_on "Login" 46 | end 47 | end 48 | 49 | ##Hack to fix PhantomJS errors on Mavericks - https://gist.github.com/ericboehs/7125105 50 | module Capybara::Poltergeist 51 | class Client 52 | private 53 | def redirect_stdout 54 | prev = STDOUT.dup 55 | prev.autoclose = false 56 | $stdout = @write_io 57 | STDOUT.reopen(@write_io) 58 | 59 | prev = STDERR.dup 60 | prev.autoclose = false 61 | $stderr = @write_io 62 | STDERR.reopen(@write_io) 63 | yield 64 | ensure 65 | STDOUT.reopen(prev) 66 | $stdout = STDOUT 67 | STDERR.reopen(prev) 68 | $stderr = STDERR 69 | end 70 | end 71 | end 72 | 73 | class WarningSuppressor 74 | IGNORE_PATTERNS = [ 75 | /QFont::setPixelSize: Pixel size <= 0/, 76 | /CoreText performance note:/, 77 | /WARNING: Method userSpaceScaleFactor/ 78 | ] 79 | 80 | def write(message) 81 | if ignore?(message) 82 | 0 83 | else 84 | puts(message) 85 | 1 86 | end 87 | end 88 | 89 | private 90 | 91 | def ignore?(message) 92 | IGNORE_PATTERNS.any? { |regexp| message =~ regexp } 93 | end 94 | end 95 | 96 | Capybara.register_driver :poltergeist do |app| 97 | Capybara::Poltergeist::Driver.new(app, phantomjs_logger: WarningSuppressor.new, timeout: 60) 98 | end 99 | 100 | Capybara.javascript_driver = :poltergeist 101 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. 50 | Examples of 51 | representing a project or community include using an official project e-mail 52 | address, posting via an official social media account, or acting as an appointed 53 | representative at an online or offline event. 54 | Representation of a project may be 55 | further defined and clarified by project maintainers. 56 | 57 | ## Enforcement 58 | 59 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 60 | reported by contacting the project team at railsgoat@gmail.com. 61 | All complaints will be reviewed and investigated and will result in a response that 62 | is deemed necessary and appropriate to the circumstances. 63 | The project team is 64 | obligated to maintain confidentiality with regard to the reporter of an incident. 65 | Further details of specific enforcement policies may be posted separately. 66 | 67 | Project maintainers who do not follow or enforce the Code of Conduct in good 68 | faith may face temporary or permanent repercussions as determined by other 69 | members of the project's leadership. 70 | 71 | ## Attribution 72 | 73 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 74 | available at [http://contributor-covenant.org/version/1/4][version] 75 | 76 | [homepage]: http://contributor-covenant.org 77 | [version]: http://contributor-covenant.org/version/1/4/ 78 | -------------------------------------------------------------------------------- /app/views/tutorials/credentials.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 96 | 105 | 106 | 107 | 108 | 116 | -------------------------------------------------------------------------------- /app/views/retirement/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Employee Contribution 9 |
10 |
11 |
12 |
13 |
14 |

<%= @info.employee_contrib %>

15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Employer Contribution 25 |
26 |
27 |
28 |
29 |
30 |

<%= @info.employer_contrib %>

31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Total Contribution 41 |
42 |
43 |
44 |
45 |
46 |

<%= @info.total %>

47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | Employee Services 59 |
60 |
61 |
62 |

63 | 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. 64 |

65 |
66 |

67 | MetaCorp is dedicated to its employees and this service is just one way of showing it! 68 |

69 |
70 |
71 |
72 |
73 |
74 |
75 | 76 | -------------------------------------------------------------------------------- /app/views/performance/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Performance History Visualization 9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Performance History 22 |
23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | <% @perf.each do |p| %> 36 | 37 | 38 | 39 | 40 | 41 | 42 | <% end %> 43 | 44 |
ReviewerDateScoreComments
<%= p.reviewer_name %><%= p.date_submitted %><%= p.score %><%= p.comments %>
45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20171007010129) do 15 | 16 | create_table "analytics", force: :cascade do |t| 17 | t.string "ip_address" 18 | t.string "referrer" 19 | t.string "user_agent" 20 | t.datetime "created_at" 21 | t.datetime "updated_at" 22 | end 23 | 24 | create_table "benefits", force: :cascade do |t| 25 | t.datetime "created_at" 26 | t.datetime "updated_at" 27 | end 28 | 29 | create_table "key_managements", force: :cascade do |t| 30 | t.string "iv" 31 | t.integer "user_id" 32 | t.datetime "created_at" 33 | t.datetime "updated_at" 34 | end 35 | 36 | create_table "messages", force: :cascade do |t| 37 | t.integer "creator_id" 38 | t.integer "receiver_id" 39 | t.text "message" 40 | t.boolean "read" 41 | t.datetime "created_at" 42 | t.datetime "updated_at" 43 | end 44 | 45 | create_table "paid_time_offs", force: :cascade do |t| 46 | t.integer "user_id" 47 | t.integer "sick_days_taken" 48 | t.integer "sick_days_earned" 49 | t.integer "pto_taken" 50 | t.integer "pto_earned" 51 | t.datetime "created_at" 52 | t.datetime "updated_at" 53 | end 54 | 55 | create_table "pays", force: :cascade do |t| 56 | t.integer "user_id" 57 | t.string "bank_account_num" 58 | t.string "bank_routing_num" 59 | t.integer "percent_of_deposit" 60 | t.datetime "created_at" 61 | t.datetime "updated_at" 62 | end 63 | 64 | create_table "performances", force: :cascade do |t| 65 | t.integer "user_id" 66 | t.date "date_submitted" 67 | t.integer "score" 68 | t.string "comments" 69 | t.integer "reviewer" 70 | t.datetime "created_at" 71 | t.datetime "updated_at" 72 | end 73 | 74 | create_table "retirements", force: :cascade do |t| 75 | t.string "total" 76 | t.string "employee_contrib" 77 | t.string "employer_contrib" 78 | t.integer "user_id" 79 | t.datetime "created_at" 80 | t.datetime "updated_at" 81 | end 82 | 83 | create_table "schedules", force: :cascade do |t| 84 | t.string "event_type" 85 | t.date "date_begin" 86 | t.date "date_end" 87 | t.string "event_name" 88 | t.string "event_desc" 89 | t.integer "user_id" 90 | t.datetime "created_at" 91 | t.datetime "updated_at" 92 | end 93 | 94 | create_table "users", force: :cascade do |t| 95 | t.string "email" 96 | t.string "password" 97 | t.boolean "admin" 98 | t.string "first_name" 99 | t.string "last_name" 100 | t.datetime "created_at" 101 | t.datetime "updated_at" 102 | t.string "auth_token" 103 | end 104 | 105 | create_table "work_infos", force: :cascade do |t| 106 | t.integer "user_id" 107 | t.string "income" 108 | t.string "bonuses" 109 | t.integer "years_worked" 110 | t.string "SSN" 111 | t.date "DoB" 112 | t.datetime "created_at" 113 | t.datetime "updated_at" 114 | t.binary "encrypted_ssn" 115 | end 116 | 117 | end 118 | -------------------------------------------------------------------------------- /app/views/users/account_settings.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 13 |
14 |
15 |
16 |
17 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Profile Settings 33 | 34 | Edit your account details 35 | 36 |
37 |
38 |
39 | <%= form_for @user, :html => {:id => "account_edit"} do |f|%> 40 | <%= f.hidden_field :id %> 41 |
42 | <%= f.label :email, nil, {:class => "control-label"}%> 43 | <%= f.text_field :email, {:class => "span12"}%> 44 |
45 | 46 |
47 | <%= f.label :first_name, nil, {:class => "control-label"}%> 48 | <%= f.text_field :first_name, {:class => "span12"} %> 49 |
50 | 51 |
52 | <%= f.label :last_name, nil, {:class => "control-label"}%> 53 | <%= f.text_field :last_name, {:class => "span12"} %> 54 |
55 | 56 |
57 | <%= f.label :password, nil, {:class => "control-label"}%> 58 | <%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%> 59 |
60 | 61 |
62 | <%= f.label :password_confirmation, nil, {:class => "control-label"}%> 63 | <%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %> 64 |
65 | 66 |
67 | <%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %> 68 |
69 | 70 |
71 |
72 | <% end %> 73 | 74 |
75 |
76 |
77 |
78 | 79 | <%= javascript_include_tag ('validation.js')%> 80 | 81 | 104 | -------------------------------------------------------------------------------- /config/initializers/populate_user_data.rb: -------------------------------------------------------------------------------- 1 | 2 | # frozen_string_literal: true 3 | POPULATE_RETIREMENTS = [ 4 | { 5 | employee_contrib: "1000", 6 | employer_contrib: "2000", 7 | total: "4500" 8 | }, 9 | { 10 | employee_contrib: "8000", 11 | employer_contrib: "16000", 12 | total: "30000" 13 | }, 14 | { 15 | employee_contrib: "10000", 16 | employer_contrib: "20000", 17 | total: "40000" 18 | }, 19 | { 20 | employee_contrib: "3000", 21 | employer_contrib: "6000", 22 | total: "12500" 23 | } 24 | 25 | ] 26 | 27 | POPULATE_PAID_TIME_OFF = [ 28 | { 29 | sick_days_taken: 2, 30 | sick_days_earned: 5, 31 | pto_taken: 5, 32 | pto_earned: 30 33 | }, 34 | { 35 | sick_days_taken: 3, 36 | sick_days_earned: 6, 37 | pto_taken: 3, 38 | pto_earned: 20 39 | }, 40 | { 41 | sick_days_taken: 2, 42 | sick_days_earned: 5, 43 | pto_taken: 5, 44 | pto_earned: 30 45 | }, 46 | { 47 | sick_days_taken: 1, 48 | sick_days_earned: 5, 49 | pto_taken: 10, 50 | pto_earned: 30 51 | } 52 | 53 | ] 54 | 55 | POPULATE_SCHEDULE = [ 56 | { 57 | date_begin: Date.new(2014, 7, 30), 58 | date_end: Date.new(2014, 8, 2), 59 | event_type: "pto", 60 | event_desc: "vacation to france", 61 | event_name: "My 2014 Vacation" 62 | 63 | }, 64 | { 65 | date_begin: Date.new(2013, 9, 1), 66 | date_end: Date.new(2013, 9, 12), 67 | event_type: "pto", 68 | event_desc: "Going Home to see folks", 69 | event_name: "Visit Parents" 70 | 71 | }, 72 | { 73 | date_begin: Date.new(2013, 9, 13), 74 | date_end: Date.new(2013, 9, 20), 75 | event_type: "pto", 76 | event_desc: "Taking kids to Grand Canyon", 77 | event_name: "AZ Trip" 78 | 79 | }, 80 | { 81 | date_begin: Date.new(2013, 12, 20), 82 | date_end: Date.new(2013, 12, 30), 83 | event_type: "pto", 84 | event_desc: "Xmas Staycation", 85 | event_name: "Christmas Leave" 86 | } 87 | 88 | ] 89 | 90 | POPULATE_WORK_INFO = [ 91 | { 92 | income: "$50,000", 93 | bonuses: "$10,000", 94 | years_worked: 2, 95 | SSN: "666-66-6666", 96 | DoB: "01-01-1980" 97 | }, 98 | { 99 | income: "$40,000", 100 | bonuses: "$10,000", 101 | years_worked: 1, 102 | SSN: "777-77-7777", 103 | DoB: "01-01-1979" 104 | }, 105 | { 106 | income: "$60,000", 107 | bonuses: "$12,000", 108 | years_worked: 3, 109 | SSN: "888-88-8888", 110 | DoB: "01-01-1981" 111 | }, 112 | { 113 | income: "$30,000", 114 | bonuses: "7,000", 115 | years_worked: 1, 116 | SSN: "999-99-9999", 117 | DoB: "01-01-1982" 118 | } 119 | ] 120 | 121 | POPULATE_PERFORMANCE = [ 122 | { 123 | reviewer: 1, 124 | comments: "Great job! You are my hero", 125 | date_submitted: Date.new(2012, 01, 01), 126 | score: 5 127 | }, 128 | { 129 | reviewer: 1, 130 | comments: "Once again, you've done a great job this year. We greatly appreciate your hard work.", 131 | date_submitted: Date.new(2013, 01, 01), 132 | score: 5 133 | }, 134 | { 135 | reviewer: 1, 136 | comments: "Great worker, great attitude for this newcomer!", 137 | date_submitted: Date.new(2013, 01, 01), 138 | score: 5 139 | }, 140 | { 141 | reviewer: 1, 142 | comments: "Wow, right out of the gate we've been very impressed but unfortunately, our system doesn't allow us to give you a full 5.0 because other ppl have gotten 5.0 ratings.", 143 | date_submitted: Date.new(2011, 01, 01), 144 | score: 4 145 | }, 146 | { 147 | reviewer: 1, 148 | comments: "We highly recommend promotion for this employee! Consistent performer with proven leadership qualities.", 149 | date_submitted: Date.new(2012, 01, 01), 150 | score: 5 151 | }, 152 | { 153 | reviewer: 1, 154 | comments: "Right out of the gate has made incredible moves as a newly appointed leader. His only improvement would be more cowbell. Not enough of it.", 155 | date_submitted: Date.new(2013, 01, 01), 156 | score: 4 157 | }, 158 | { 159 | reviewer: 1, 160 | comments: "Ehh, you are okay, we will let you stay..... barely", 161 | date_submitted: Date.new(2013, 01, 01), 162 | score: 2 163 | } 164 | ] 165 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Railsgoat::Application.configure do 3 | # Settings specified here will take precedence over those in config/application.rb 4 | 5 | # Code is not reloaded between requests. 6 | config.cache_classes = true 7 | 8 | # Full error reports are disabled and caching is turned on. 9 | config.consider_all_requests_local = false 10 | config.action_controller.perform_caching = true 11 | 12 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 13 | # Add `rack-cache` to your Gemfile before enabling this. 14 | # For large-scale production use, consider using a caching 15 | # reverse proxy like nginx, varnish or squid. 16 | # config.action_dispatch.rack_cache = true 17 | 18 | # Disable Rails's static asset server (Apache or nginx will already do this). 19 | config.public_file_server.enabled = false 20 | 21 | # Compress JavaScripts and CSS 22 | config.assets.compress = true 23 | 24 | # Compress JavaScripts and CSS. 25 | config.assets.js_compressor = :uglifier 26 | # config.assets.css_compressor = :sass 27 | 28 | # Do not fallback to assets pipeline if a precompiled asset is missed. 29 | config.assets.compile = true # default is false 30 | 31 | # Generate digests for assets URLs. 32 | config.assets.digest = true 33 | 34 | # For Rails 4.0+: Version of your assets, change this if you want to expire all your assets. 35 | config.assets.version = "1.0" 36 | 37 | # Defaults to nil and saved in location specified by config.assets.prefix 38 | # config.assets.manifest = YOUR_PATH 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Set to :debug to see everything in the log. 48 | config.log_level = :info 49 | 50 | # Prepend all log lines with the following tags 51 | # config.log_tags = [ :subdomain, :uuid ] 52 | 53 | # Use a different logger for distributed setups 54 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 55 | 56 | # Use a different cache store in production 57 | # config.cache_store = :mem_cache_store 58 | 59 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 60 | # config.action_controller.asset_host = "http://assets.example.com" 61 | 62 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 63 | # config.assets.precompile += %w( search.js ) 64 | 65 | # Disable delivery errors, bad email addresses will be ignored 66 | # config.action_mailer.raise_delivery_errors = false 67 | 68 | # Enable threaded mode 69 | # config.threadsafe! 70 | 71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 72 | # the I18n.default_locale when a translation can not be found). 73 | config.i18n.fallbacks = [I18n.default_locale] 74 | 75 | # Send deprecation notices to registered listeners. 76 | config.active_support.deprecation = :notify 77 | 78 | # For Rails 4.0+: Eager load code on boot. This eager loads most of 79 | # Rails and your application in memory, allowing both thread web 80 | # servers and those relying on copy on write to perform better. 81 | # Rake tasks automatically ignore this option for performance. 82 | config.eager_load = true 83 | 84 | # For Rails 4.0+: Use default logging formatter so that PID and timestamp are not suppressed. 85 | config.log_formatter = ::Logger::Formatter.new 86 | 87 | # For Rails 4.0+: Disable automatic flushing of the log to improve performance. 88 | # config.autoflush_log = false 89 | 90 | # Prepend all log lines with the following tags. 91 | # config.log_tags = [ :subdomain, :uuid ] 92 | 93 | # Use a different logger for distributed setups. 94 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 95 | 96 | # Use a different cache store in production. 97 | # config.cache_store = :mem_cache_store 98 | 99 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 100 | # config.action_controller.asset_host = "http://assets.example.com" 101 | 102 | # Precompile additional assets. 103 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 104 | # config.assets.precompile += %w( search.js ) 105 | 106 | # Ignore bad email addresses and do not raise email delivery errors. 107 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 108 | # config.action_mailer.raise_delivery_errors = false 109 | end 110 | -------------------------------------------------------------------------------- /app/views/layouts/shared/_sidebar.html.erb: -------------------------------------------------------------------------------- 1 | <% if current_user %> 2 | 92 | 93 | 142 | <% end %> 143 | -------------------------------------------------------------------------------- /app/views/benefit_forms/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 |
7 |
8 |
9 |
10 | Health Insurance 11 |
12 |
13 | 14 |
15 | Click on PDF to download

16 | <%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %> 17 |
18 |
19 | 20 | 21 | PDF 22 | 23 |
24 | 25 |
26 | <% end %> 27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 |
36 | Dental Insurance 37 |
38 |
39 | 40 |
41 | Click on PDF to download

42 | <%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %> 43 |
44 |
45 | 46 | 47 | PDF 48 | 49 |
50 | 51 |
52 | <% end %> 53 |
54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Health Insurance 64 |
65 |
66 | 67 |
68 |
69 |

Upload file

70 | <%= form_for @benefits, :url => upload_path, :html => { :action => "upload", :multipart => true, :id => "fi" } do |f| %> 71 | 72 |
73 |
74 | <%= hidden_field "benefits", "backup", :value => false %> 75 | 76 | 77 | 78 | Add file 79 | <%= f.file_field :upload %> 80 | 81 | 85 |

Nothing selected 86 |
87 |
88 | 89 |
90 |
91 |
92 |
93 |
94 | 95 |
96 |
97 | 98 | 99 |
100 | <% end %> 101 |
102 |
103 |
104 | 105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | 113 | -------------------------------------------------------------------------------- /app/views/messages/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 |
7 |
8 |
9 |
10 | Messages for <%= current_user.full_name %> 11 | 12 |
13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% if @messages.empty? %> 27 | 28 | <% end %> 29 | <% @messages.each do |message| %> 30 | 31 | 32 | 33 | 35 | 36 | 37 | <% end %> 38 |
From:DateMessageActions
<%= "No messages!" %><%= message.creator_name %><%= message.created_at.to_date %><%= message.message %><%= link_to "Details", user_message_path(:id => message.id), {:class => "btn btn-info pull-left"}%> 34 | <%= link_to "Delete", user_message_path(:id => message.id), {:method => 'delete', :class => "btn btn-danger pull-left"}%>
39 |
40 |
41 |
42 | 43 |
44 | 45 | 46 |
47 | 48 |
49 |
50 |
51 |
52 | Send Message 53 |
54 |
55 |
56 | 64 | 72 |
73 |
74 | 75 | <%= form_for @message, :url => user_messages_path, :method => :post, :html => {:id => "send_message"} do |f|%> 76 | <%= f.hidden_field :creator_id, :value => current_user.id %> 77 | <%= f.hidden_field :read, :value => '0' %> 78 |
79 | <%= f.label "To:", nil, {:class => "control-label"}%> 80 | <%= f.select(:receiver_id, options_from_collection_for_select(User.all, :id, :full_name)) %> 81 |
82 | 83 |
84 | <%= f.label :message, nil, {:class => "control-label"}%> 85 | <%= f.text_area :message, {:class => "span12"} %> 86 |
87 | 88 |
89 | <%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %> 90 |
91 | 92 |
93 | <% end %> 94 | 95 |
96 |
97 |
98 |
99 |
100 | 101 |
102 | 103 |
104 |
105 | 106 | 107 | 108 | 139 | --------------------------------------------------------------------------------