Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6786337
Add security middleware and rate limiting configuration
rsmoke Apr 30, 2025
eabbeb2
Update test output message for SHOW_BROWSER environment variable
rsmoke May 6, 2025
0b8b321
Refactor editable content system tests for clarity and consistency
rsmoke May 6, 2025
737a4ee
Add custom Rack middleware for enhanced security measures
rsmoke May 6, 2025
1915d09
Update entry retrieval in profile authorization to use policy scope
rsmoke May 6, 2025
1c3ff29
Refactor user role setup in profile access system tests
rsmoke May 6, 2025
e437a1b
Update round judge assignment controller to use policy scope for cont…
rsmoke May 6, 2025
02af4a8
Update user role retrieval in UserRolesController to use policy scope
rsmoke May 6, 2025
eb510c5
Bump rack from 3.1.12 to 3.1.14 in the bundler group across 1 directory
dependabot[bot] May 8, 2025
20d6580
Bump trix in the npm_and_yarn group across 1 directory
dependabot[bot] May 8, 2025
9504ef2
Add stackprof gem for performance profiling
rsmoke May 12, 2025
820c569
Update Bootstrap dependency to version 5.3.3 in package.json and yarn…
rsmoke May 12, 2025
5436343
Refactor footer links for improved layout and accessibility
rsmoke May 12, 2025
50cd354
Fix whitespace issues in available contests partial for improved read…
rsmoke May 12, 2025
a74deb8
Update contest entry icon in applicant dashboard for improved clarity
rsmoke May 12, 2025
cfb29e3
Add inactive submissions summary partial to applicant dashboard
rsmoke May 12, 2025
761e45c
Enhance submissions summary in applicant dashboard to display only ac…
rsmoke May 12, 2025
fa4ba95
Update applicant dashboard layout to include inactive submissions sum…
rsmoke May 12, 2025
5f0279a
Add form instructions to container form and update seed data
rsmoke May 12, 2025
671fb1f
Merge pull request #124 from lsa-mis/LRA-1050-lsa-evaluate-entrant-da…
rsmoke May 12, 2025
5d23e44
Merge pull request #122 from lsa-mis/dependabot/bundler/bundler-a195c…
rsmoke May 12, 2025
e2060b8
Merge pull request #123 from lsa-mis/dependabot/npm_and_yarn/npm_and_…
rsmoke May 12, 2025
e85c93d
Bump rack-session in the bundler group across 1 directory
dependabot[bot] May 12, 2025
d836b5b
Merge pull request #125 from lsa-mis/dependabot/bundler/bundler-457f0…
rsmoke May 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ gem 'pundit'
gem 'redis', '~> 5.0'
gem 'sentry-ruby'
gem 'sentry-rails'
gem 'stackprof'
gem 'sidekiq', '~> 7.3'
gem 'sassc-rails'
gem 'simple_form', '~> 5.3'
Expand Down
7 changes: 5 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -317,14 +317,15 @@ GEM
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
racc (1.8.1)
rack (3.1.12)
rack (3.1.14)
rack-accept (0.4.5)
rack (>= 0.4)
rack-protection (4.1.1)
base64 (>= 0.1.0)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
rack-session (2.0.0)
rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0)
rack-test (2.1.0)
rack (>= 1.3)
Expand Down Expand Up @@ -500,6 +501,7 @@ GEM
net-scp (>= 1.1.2)
net-sftp (>= 2.1.2)
net-ssh (>= 2.8.0)
stackprof (0.2.27)
stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.1.1)
Expand Down Expand Up @@ -600,6 +602,7 @@ DEPENDENCIES
simple_form (~> 5.3)
simplecov
skylight
stackprof
stimulus-rails
turbo-rails
turnout2024
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/entries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def set_entry

# For applicant_profile, we want to find the entry first, then authorize it
def set_entry_for_profile
@entry = Entry.find(params[:id])
@entry = policy_scope(Entry).find(params[:id])
end

def authorize_entry
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/round_judge_assignments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def index

def create
@round_judge_assignment = @judging_round.round_judge_assignments.build(round_judge_assignment_params)

if @round_judge_assignment.save
redirect_to container_contest_description_contest_instance_judging_round_round_judge_assignments_path(
@container, @contest_description, @contest_instance, @judging_round
Expand All @@ -33,7 +33,7 @@ def destroy
private

def set_judging_round
@container = Container.find(params[:container_id])
@container = policy_scope(Container).find(params[:container_id])
@contest_description = @container.contest_descriptions.find(params[:contest_description_id])
@contest_instance = @contest_description.contest_instances.find(params[:contest_instance_id])
@judging_round = @contest_instance.judging_rounds.find(params[:judging_round_id])
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/user_roles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def destroy
private

def set_user_role
@user_role = UserRole.find(params[:id])
@user_role = policy_scope(UserRole).find(params[:id])
end

def user_role_params
Expand Down
6 changes: 3 additions & 3 deletions app/views/applicant_dashboard/_available_contests.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<% if params[:filter] && (params[:filter][:department_id].present? || params[:filter][:container_id].present?) %>
<h2>Filtered Contests</h2>
<p>
Showing contests for
Showing contests for
<% if params[:filter][:department_id].present? %>
<% department = Department.find_by(id: params[:filter][:department_id]) %>
<% if department %>
Expand Down Expand Up @@ -44,12 +44,12 @@
<% end %>
<% end %>
</div>

<% if @active_contests_by_container.any? %>
<% @active_contests_by_container.each do |container, contests| %>
<%= render partial: 'applicant_dashboard/contest_table', locals: { container: container, contests: contests } %>
<% end %>
<% else %>
<p>No active contests available for your class level.</p>
<% end %>
</div>
</div>
4 changes: 2 additions & 2 deletions app/views/applicant_dashboard/_contest_table.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
data: { 'bs-toggle': 'tooltip' },
title: 'Enter contest',
aria: { label: 'Enter contest' } do %>
<i class="bi bi-pencil" style="font-size: 1.5rem;" aria-hidden="true"></i>
<i class="bi bi-send-arrow-up" style="font-size: 1.5rem;" aria-hidden="true"></i>
<span class="visually-hidden">Enter instance</span>
<% end %>
</td>
Expand All @@ -33,4 +33,4 @@
</tbody>
</table>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<div class="card bg-light mt-4">
<div class="card-header bg-light">
<div class="d-flex align-items-center">
<i class="bi bi-clock-history me-2 text-muted"></i>
<h5 class="card-title mb-0 text-muted">Past Contest Entries</h5>
</div>
</div>
<div class="card-body p-0">
<% if (content = render_editable_content('applicant_dashboard', 'inactivesubmission_summary')) %>
<div class="p-3 border-bottom">
<%= content %>
</div>
<% end %>
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr>
<th>Contest</th>
<th>Title</th>
<th>Type</th>
<th>Submitted</th>
<th class="text-center">Entry</th>
</tr>
</thead>
<tbody>
<% @entries.joins(:contest_instance).where(contest_instances: { active: false }).each do |entry| %>
<tr id="<%= dom_id(entry) %>" data-controller="fade-out">
<td><small><%= "#{entry.contest_instance.contest_description.name } - #{entry.contest_instance.date_open.year}" %></small></td>
<td><small><%= entry.title %></small></td>
<td><small><%= entry.category.kind %></small></td>
<td><small><%= entry.created_at.strftime("%b %d, %Y") %></small></td>
<td class="text-center">
<% if entry.entry_file.attached? %>
<div class="d-flex flex-row justify-content-center">
<%= link_to rails_blob_path(entry.entry_file, disposition: "inline"),
target: "_blank", rel: "noopener", class: "me-2",
title: "View in new tab", aria: { label: "View file" } do %>
<i class="bi bi-eye text-muted" style="font-size: 1.2rem;" aria-hidden="true"></i>
<% end %>
<%= link_to rails_blob_path(entry.entry_file, disposition: "attachment"),
download: "", class: "me-2",
title: "Download file", aria: { label: "Download file" } do %>
<i class="bi bi-download text-muted" style="font-size: 1.2rem;" aria-hidden="true"></i>
<% end %>
</div>
<% else %>
<small class="text-muted">No file</small>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
26 changes: 15 additions & 11 deletions app/views/applicant_dashboard/_submissions_summary.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="mt-4">
<div class="">
<h2 class="">Contests You Have Entered:</h2>
<h2 class="">Active Contests You Have Entered:</h2>
<% if (content = render_editable_content('applicant_dashboard', 'submission_summary')) %>
<%= content %>
<% end %>
Expand All @@ -18,22 +18,26 @@
</tr>
</thead>
<tbody>
<% @entries.each do |entry| %>
<% @entries.joins(:contest_instance).where(contest_instances: { active: true }).each do |entry| %>
<tr id="<%= dom_id(entry) %>" data-controller="fade-out">
<td><%= "#{entry.contest_instance.contest_description.name } - #{entry.contest_instance.date_open.year}" %></td>
<td><%= entry.title %></td>
<td><%= entry.category.kind %></td>
<td><%= entry.created_at.strftime("%B %d, %Y") %></td>
<td class="text-center">
<% if entry.entry_file.attached? %>
<%= link_to rails_blob_path(entry.entry_file, disposition: "inline"), target: "_blank", rel: "noopener", class: "me-2", title: "View in new tab" do %>
<i class="bi bi-eye" style="font-size: 1.5rem;" aria-hidden="true"></i>
<span class="visually-hidden">View file</span>
<% end %>
<%= link_to rails_blob_path(entry.entry_file, disposition: "attachment"), download: "", class: "me-2", title: "Download file" do %>
<i class="bi bi-download" style="font-size: 1.5rem;" aria-hidden="true"></i>
<span class="visually-hidden">Download file</span>
<% end %>
<div class="d-flex flex-row justify-content-center">
<%= link_to rails_blob_path(entry.entry_file, disposition: "inline"),
target: "_blank", rel: "noopener", class: "me-2",
title: "View in new tab", aria: { label: "View file" } do %>
<i class="bi bi-eye" style="font-size: 1.5rem;" aria-hidden="true"></i>
<% end %>
<%= link_to rails_blob_path(entry.entry_file, disposition: "attachment"),
download: "", class: "me-2",
title: "Download file", aria: { label: "Download file" } do %>
<i class="bi bi-download" style="font-size: 1.5rem;" aria-hidden="true"></i>
<% end %>
</div>
<% else %>
No file attached
<% end %>
Expand Down Expand Up @@ -61,4 +65,4 @@
</tbody>
</table>
</div>
</div>
</div>
10 changes: 8 additions & 2 deletions app/views/applicant_dashboard/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@
<%= render 'applicant_dashboard/opportunities' %>
</div>
</div>
<div class="row">
<div class="col-12 available-contests" id="available-contests">
<%= render 'applicant_dashboard/available_contests' %>
</div>
</div>
<div class="row">
<div class="col-12 submissions-summary" id="submissions-summary">
<%= render 'applicant_dashboard/submissions_summary', submissions: @submissions %>
</div>
</div>
<hr class="my-4 w-75 mx-auto">
<div class="row">
<div class="col-12 available-contests" id="available-contests">
<%= render 'applicant_dashboard/available_contests' %>
<div class="col-12 submissions-summary" id="inactive-submissions-summary">
<%= render 'applicant_dashboard/inactive_submissions_summary', submissions: @submissions %>
</div>
</div>
</div>
Expand Down
6 changes: 5 additions & 1 deletion app/views/containers/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<div id="container_form">
<% if (content = render_editable_content('container', 'form_instructions')) %>
<%= content %>
<% end %>
<%= simple_form_for(@container, html: { id: 'new_container_form' }) do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
Expand All @@ -17,10 +20,11 @@

<%= f.input :visibility_id,
collection: Visibility.all,
label: 'Dashboard Visibility',
label_method: :kind,
value_method: :id,
hint: safe_join([
content_tag(:strong, "Public"), " visibility means that this collection of contests will be visible in the applicant's dashboard. ",
content_tag(:strong, "Public"), " visibility means that this collection of contests will be visible to all user's of LSA Evaluate.",
tag(:br),
content_tag(:strong, "Private"), " visibility requires you provide a special link to applicants in order for them to apply to this collection of contests, it will NOT be visible in the applicant's dashboard."
]),
Expand Down
20 changes: 13 additions & 7 deletions app/views/shared/_footer.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
<p class="d-none d-md-block">
<%= image_tag 'LSA_Technology_logo.svg', alt: 'LSA Technology Logo', class: "img-fluid" %>
<% if user_signed_in? && current_user.axis_mundi? %>
<%= link_to editable_contents_path, class: "edit-icon", data: { bs_toggle: "tooltip", bs_placement: "top" }, title: "Edit the text blocks in the application" do %>
<i class="bi bi-pencil" aria-label="Edit Content"></i>
<% end %>
|
<%= link_to users_dashboard_index_path, class: "profile-icon", data: { bs_toggle: "tooltip", bs_placement: "top" }, title: "Users Dashboard" do %>
<i class="bi bi-people" aria-label="Users Dashboard"></i>
<% end %>
<div class="d-flex flex-row justify-content-center">
<%= link_to editable_contents_path, class: "edit-icon", data: { bs_toggle: "tooltip", bs_placement: "top" }, title: "Edit the text blocks in the application" do %>
<i class="bi bi-pencil-fill" aria-label="Edit Instruction Content"></i>
<% end %>
<span class="mx-2 align-middle text-secondary">|</span>
<%= link_to users_dashboard_index_path, class: "profile-icon", data: { bs_toggle: "tooltip", bs_placement: "top" }, title: "Users Dashboard" do %>
<i class="bi bi-people-fill" aria-label="Users Dashboard"></i>
<% end %>
<span class="mx-2 align-middle text-secondary">|</span>
<%= link_to departments_path, class: "profile-icon", data: { bs_toggle: "tooltip", bs_placement: "top" }, title: "Departments" do %>
<i class="bi bi-building" aria-label="Departments"></i>
<% end %>
</div>
<% end %>
</p>
<p class="text-center footer-bold d-md-none">
Expand Down
46 changes: 46 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,41 @@
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

# Custom middleware to block PHP-related requests
class Rack::Defense
def initialize(app)
@app = app
end

def call(env)
request = Rack::Request.new(env)

# Block requests with PHP-related content
if request.post? && (
request.path.include?('.php') ||
request.query_string.include?('php') ||
request.content_type.to_s.include?('php') ||
request.body.read.to_s.include?('php')
)
return [403, { 'Content-Type' => 'text/plain' }, ['Forbidden']]
end

# Block requests with suspicious headers
if request.env['HTTP_USER_AGENT'].to_s.include?('Custom-AsyncHttpClient') ||
request.env['HTTP_X_REQUEST_ID'].to_s.include?('cve_2024_4577')
return [403, { 'Content-Type' => 'text/plain' }, ['Forbidden']]
end

# Block known malicious IPs
suspicious_ips = ['91.99.22.81'] # Add more IPs as needed
if suspicious_ips.include?(request.ip)
return [403, { 'Content-Type' => 'text/plain' }, ['Forbidden']]
end

@app.call(env)
end
end

module LsaEvaluate
class Application < Rails::Application # rubocop:disable Style/Documentation
# Initialize configuration defaults for originally generated Rails version.
Expand All @@ -43,5 +78,16 @@ class Application < Rails::Application # rubocop:disable Style/Documentation
# config.eager_load_paths << Rails.root.join("extras")
# Don't generate system test files.
config.generators.system_tests = nil

# Add security middleware
config.middleware.use Rack::Defense

# Configure rate limiting
config.action_dispatch.rate_limiter = {
limit: 300,
period: 5.minutes,
store: :redis,
key: ->(request) { request.ip }
}
end
end
6 changes: 5 additions & 1 deletion db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@
{ page: "judging_assignments", section: "round_specific_instructions",
content: ActionText::RichText.new(body: "Instructions for the round_specific_instructions") },
{ page: "judging_rounds", section: "comment_interface_behavior",
content: ActionText::RichText.new(body: "Instructions for the comment_interface_behavior") }
content: ActionText::RichText.new(body: "Instructions for the comment_interface_behavior") },
{ page: "applicant_dashboard", section: "inactivesubmission_summary",
content: ActionText::RichText.new(body: "Instructions for the inactivesubmission_summary") },
{ page: "containers", section: "form_instructions",
content: ActionText::RichText.new(body: "Instructions for the form_instructions") }
])

# Seed data for School
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"@popperjs/core": "^2.11.8",
"@rails/actiontext": "^7.0.8-3",
"autoprefixer": "^10.4.17",
"bootstrap": "^5.3.2",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"esbuild": "^0.25.0",
"mac-ca": "^3.1.0",
Expand All @@ -16,7 +16,7 @@
"postcss-cli": "^11.0.0",
"sass": "^1.70.0",
"sortablejs": "^1.15.6",
"trix": "^2.1.12"
"trix": "^2.1.15"
},
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets",
Expand Down
Loading
Loading