Improve handling and escaping of special characters in input fields [SCI-8125] (#5135)

This commit is contained in:
Alex Kriuchykhin 2023-03-13 18:00:30 +01:00 committed by GitHub
parent 40f223deca
commit d82470aa03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 85 additions and 84 deletions

View file

@ -476,7 +476,7 @@ var ProjectsIndex = (function() {
data: { ...requestParams, ...{ page: 1 } },
success: function(data) {
$('#breadcrumbsWrapper').html(data.breadcrumbs_html);
$(projectsWrapper).find('.projects-title').text(data.title);
$(projectsWrapper).find('.projects-title').html(data.title_html);
$(toolbarWrapper).html(data.toolbar_html);
viewContainer.data('projects-cards-url', data.projects_cards_url);
viewContainer.removeClass('no-results');

View file

@ -296,11 +296,11 @@ class ExperimentsController < ApplicationController
.select(:id, :name, :color)
tags = tags.map do |tag|
{ value: tag.id, label: sanitize_input(tag.name), params: { color: sanitize_input(tag.color) } }
{ value: tag.id, label: escape_input(tag.name), params: { color: escape_input(tag.color) } }
end
if params[:query].present? && tags.select { |tag| tag[:label] == params[:query] }.blank?
tags << { value: 0, label: sanitize_input(params[:query]), params: { color: nil } }
tags << { value: 0, label: escape_input(params[:query]), params: { color: nil } }
end
render json: tags
end
@ -358,12 +358,12 @@ class ExperimentsController < ApplicationController
@experiment.workflowimg.purge
render json: { message: t('experiments.table.modal_move_modules.success_flash',
experiment: sanitize_input(dst_experiment.name)) }
experiment: escape_input(dst_experiment.name)) }
rescue StandardError => e
Rails.logger.error(e.message)
Rails.logger.error(e.backtrace.join("\n"))
render json: {
message: t('experiments.table.modal_move_modules.error_flash', experiment: sanitize_input(dst_experiment.name))
message: t('experiments.table.modal_move_modules.error_flash', experiment: escape_input(dst_experiment.name))
}, status: :unprocessable_entity
raise ActiveRecord::Rollback
end
@ -444,7 +444,7 @@ class ExperimentsController < ApplicationController
def assigned_users_to_tasks
users = current_team.users.where(id: @experiment.my_modules.joins(:user_my_modules).select(:user_id))
.search(false, params[:query]).map do |u|
{ value: u.id, label: sanitize_input(u.name), params: { avatar_url: avatar_path(u, :icon_small) } }
{ value: u.id, label: escape_input(u.name), params: { avatar_url: avatar_path(u, :icon_small) } }
end
render json: users, status: :ok

View file

@ -120,11 +120,11 @@ class MyModuleTagsController < ApplicationController
.limit(6)
tags = tags.map do |tag|
{ value: tag.id, label: sanitize_input(tag.name), params: { color: sanitize_input(tag.color) } }
{ value: tag.id, label: escape_input(tag.name), params: { color: escape_input(tag.color) } }
end
if params[:query].present? && tags.select { |tag| tag[:label] == params[:query] }.blank?
tags << { value: 0, label: sanitize_input(params[:query]), params: { color: nil } }
tags << { value: 0, label: escape_input(params[:query]), params: { color: nil } }
end
render json: tags

View file

@ -51,15 +51,15 @@ class ProjectsController < ApplicationController
breadcrumbs_html = render_to_string(partial: 'projects/index/breadcrumbs.html.erb',
locals: { target_folder: current_folder, folder_page: true })
projects_cards_url = project_folder_cards_url(current_folder)
title = if @inline_editable_title_config.present?
render_to_string(partial: 'shared/inline_editing',
locals: {
initial_value: current_folder&.name,
config: @inline_editable_title_config
})
else
current_folder.name
end
title_html = if @inline_editable_title_config.present?
render_to_string(partial: 'shared/inline_editing',
locals: {
initial_value: current_folder&.name,
config: @inline_editable_title_config
})
else
escape_input(current_folder.name)
end
else
breadcrumbs_html = ''
projects_cards_url = cards_projects_url
@ -71,7 +71,7 @@ class ProjectsController < ApplicationController
render json: {
projects_cards_url: projects_cards_url,
breadcrumbs_html: breadcrumbs_html,
title: title,
title_html: title_html,
next_page: cards.next_page,
toolbar_html: render_to_string(partial: 'projects/index/toolbar.html.erb'),
cards_html: render_to_string(
@ -347,7 +347,7 @@ class ProjectsController < ApplicationController
def users_filter
users = current_team.users.search(false, params[:query]).map do |u|
{ value: u.id, label: sanitize_input(u.name), params: { avatar_url: avatar_path(u, :icon_small) } }
{ value: u.id, label: escape_input(u.name), params: { avatar_url: avatar_path(u, :icon_small) } }
end
render json: users, status: :ok

View file

@ -696,18 +696,18 @@ class ProtocolsController < ApplicationController
@db_json = {}
@toolong = false
@db_json['name'] = pio_eval_title_len(
sanitize_input(not_null(params['protocol']['name']))
escape_input(not_null(params['protocol']['name']))
)
# since scinote only has description field, and protocols.io has many others
# ,here i am putting everything important from protocols.io into description
@db_json['authors'] = pio_eval_title_len(
sanitize_input(not_null(params['protocol']['authors']))
escape_input(not_null(params['protocol']['authors']))
)
@db_json['created_at'] = pio_eval_title_len(
sanitize_input(not_null(params['protocol']['created_at']))
escape_input(not_null(params['protocol']['created_at']))
)
@db_json['updated_at'] = pio_eval_title_len(
sanitize_input(not_null(params['protocol']['last_modified']))
escape_input(not_null(params['protocol']['last_modified']))
)
@db_json['steps'] = {}

View file

@ -131,10 +131,8 @@ class RepositoriesController < ApplicationController
if @repository.save
log_activity(:create_inventory)
flash[:success] = t('repositories.index.modal_create.success_flash',
name: @repository.name)
render json: { url: repository_path(@repository) },
status: :ok
flash[:success] = t('repositories.index.modal_create.success_flash_html', name: @repository.name)
render json: { url: repository_path(@repository) }
else
render json: @repository.errors,
status: :unprocessable_entity
@ -455,7 +453,10 @@ class RepositoriesController < ApplicationController
item_id: @repository.id,
field_to_udpate: 'name',
path_to_update: team_repository_path(@repository),
label_after: "<span class=\"repository-share-icon\">#{inventory_shared_status_icon(@repository, current_team)}</span>"
label_after:
sanitize_input(
"<span class=\"repository-share-icon\">#{inventory_shared_status_icon(@repository, current_team)}</span>"
)
}
end

View file

@ -134,7 +134,7 @@ class UserMyModulesController < ApplicationController
user_hash = {
value: user.id,
label: sanitize_input(user.full_name),
label: escape_input(user.full_name),
params: {
avatar_url: avatar_path(user, :icon_small),
designated: user.designated,

View file

@ -36,11 +36,11 @@ class LabelTemplateDatatable < CustomDatatable
'0' => record.id,
'1' => record.default,
'2' => append_format_icon(record),
'3' => sanitize_input(record.label_format),
'4' => sanitize_input(record.description),
'5' => sanitize_input(record.modified_by),
'3' => escape_input(record.label_format),
'4' => escape_input(record.description),
'5' => escape_input(record.modified_by),
'6' => I18n.l(record.updated_at, format: :full),
'7' => sanitize_input(record.created_by_user),
'7' => escape_input(record.created_by_user),
'8' => I18n.l(record.created_at, format: :full),
'recordInfoUrl' => '',
'DT_RowAttr': {
@ -60,7 +60,7 @@ class LabelTemplateDatatable < CustomDatatable
"label_template_icons/#{record.icon}.svg",
class: 'label-template-icon'
),
name: sanitize_input(record.name)
name: escape_input(record.name)
}
end

View file

@ -185,7 +185,7 @@ class ProtocolsDatatable < CustomDatatable
kws = record.protocol_keywords_str.split(", ")
res = []
kws.sort_by{ |word| word.downcase }.each do |kw|
sanitized_kw = sanitize_input(kw)
sanitized_kw = escape_input(kw)
res << "<a href='#' data-action='filter' " \
"data-param='#{sanitized_kw}'>#{sanitized_kw}</a>"
end

View file

@ -56,13 +56,13 @@ class ReportDatatable < CustomDatatable
records.map do |record|
{
'0' => record.id,
'1' => sanitize_input(record.project_name),
'2' => sanitize_input(record.name),
'3' => sanitize_input(record.code),
'1' => escape_input(record.project_name),
'2' => escape_input(record.name),
'3' => escape_input(record.code),
'4' => pdf_file(record),
'5' => docx_file(record),
'6' => sanitize_input(record.created_by_name),
'7' => sanitize_input(record.modified_by_name),
'6' => escape_input(record.created_by_name),
'7' => escape_input(record.modified_by_name),
'8' => I18n.l(record.created_at, format: :full),
'9' => I18n.l(record.updated_at, format: :full),
'archived' => record.project.archived?,

View file

@ -159,16 +159,16 @@ module ApplicationHelper
<img src='#{user_avatar_absolute_url(user, :thumb, base64_encoded_imgs)}'
alt='thumb'></div><div class='col-xs-8'>
<div class='row'><div class='col-xs-9 text-left'><h5>
#{sanitize_input(user.full_name)}</h5></div><div class='col-xs-3 text-right'>
#{escape_input(user.full_name)}</h5></div><div class='col-xs-3 text-right'>
<span class='fas fa-times' aria-hidden='true'></span>
</div></div><div class='row'><div class='col-xs-12'>
<p class='silver'>#{sanitize_input(user.email)}</p>)
<p class='silver'>#{escape_input(user.email)}</p>)
if user_still_in_team
user_team_assignment = user.user_assignments.find_by(assignable: team)
user_description += %(<p>
#{I18n.t('atwho.users.popover_html',
role: sanitize_input(user_team_assignment.user_role.name.capitalize),
team: sanitize_input(user_team_assignment.assignable.name),
role: escape_input(user_team_assignment.user_role.name.capitalize),
team: escape_input(user_team_assignment.assignable.name),
time: I18n.l(user_team_assignment.created_at, format: :full_date))}
</p></div></div></div>)
else
@ -190,7 +190,7 @@ module ApplicationHelper
'class="atwho-user-popover" data-container="body" ' \
'data-html="true" tabindex="0" data-trigger="focus" ' \
'data-placement="top" data-toggle="popover" data-content="') +
raw(user_description) + raw('" >') + sanitize_input(user.full_name) + raw('</a>')
raw(user_description) + raw('" >') + escape_input(user.full_name) + raw('</a>')
html << " #{I18n.t('atwho.res.removed')}" unless skip_user_status || user_still_in_team
"<span class=\"atwho-user-container\">#{html}</span>"

View file

@ -4,7 +4,7 @@ require 'sanitize'
module InputSanitizeHelper
def sanitize_input(html, _tags = [], _attributes = [])
Sanitize.fragment(html, Constants::INPUT_SANITIZE_CONFIG)
Sanitize.fragment(html, Constants::INPUT_SANITIZE_CONFIG).html_safe
end
def escape_input(text)
@ -19,11 +19,11 @@ module InputSanitizeHelper
preview_repository = options.fetch(:preview_repository, false)
format_opt = wrapper_tag.merge(sanitize: false)
base64_encoded_imgs = options.fetch(:base64_encoded_imgs, false)
text = sanitize_input(text, tags)
text = simple_format(text, {}, format_opt) if simple_f
if text =~ SmartAnnotations::TagToHtml::USER_REGEX || text =~ SmartAnnotations::TagToHtml::REGEX
text = smart_annotation_parser(text, team, base64_encoded_imgs, preview_repository)
end
text = sanitize_input(text, tags)
auto_link(
text,
html: { target: '_blank' },

View file

@ -18,7 +18,7 @@ module ProjectsHelper
end
def user_name_with_role(user_assignment)
"#{sanitize_input(user_assignment.user.name)} - #{user_assignment.user_role.name}"
"#{escape_input(user_assignment.user.name)} - #{escape_input(user_assignment.user_role.name)}"
end
def construct_module_connections(my_module)

View file

@ -7,7 +7,7 @@ module UserAssignmentsHelper
else
assignee.name
end
sanitize_input(display_name)
escape_input(display_name)
end
def user_assignment_resource_role_name(user, resource, inherit = '')
@ -17,7 +17,7 @@ module UserAssignmentsHelper
return user_assignment_resource_role_name(user, parent, '_inherit')
end
"#{user_assignment.user_role.name}
"#{escape_input(user_assignment.user_role.name)}
<span class='permission-object-tag'
title='#{t("access_permissions.partials.#{resource.class.to_s.downcase}_tooltip#{inherit}")}'>
#{t("access_permissions.partials.#{resource.class.to_s.downcase}")}

View file

@ -26,8 +26,8 @@ module Reports
type_of: :deliver_error,
title: I18n.t('projects.reports.index.generation.error_docx_notification_title'),
message: I18n.t('projects.reports.index.generation.error_notification_message',
report_link: "<a href='#{report_path}'>#{sanitize_input(report.name)}</a>",
team_name: sanitize_input(report.team.name))
report_link: "<a href='#{report_path}'>#{escape_input(report.name)}</a>",
team_name: escape_input(report.team.name))
)
notification.create_user_notification(user)
Rails.logger.error("Couldn't generate DOCX for Report with id: #{report.id}. Error:\n #{error}")
@ -49,8 +49,8 @@ module Reports
type_of: :deliver,
title: I18n.t('projects.reports.index.generation.completed_docx_notification_title'),
message: I18n.t('projects.reports.index.generation.completed_notification_message',
report_link: "<a href='#{report_path}'>#{sanitize_input(report.name)}</a>",
team_name: sanitize_input(report.team.name))
report_link: "<a href='#{report_path}'>#{escape_input(report.name)}</a>",
team_name: escape_input(report.team.name))
)
Reports::DocxPreviewJob.perform_now(report.id)

View file

@ -30,8 +30,8 @@ module Reports
type_of: :deliver_error,
title: I18n.t('projects.reports.index.generation.error_pdf_notification_title'),
message: I18n.t('projects.reports.index.generation.error_notification_message',
report_link: "<a href='#{report_path}'>#{sanitize_input(report.name)}</a>",
team_name: sanitize_input(report.team.name))
report_link: "<a href='#{report_path}'>#{escape_input(report.name)}</a>",
team_name: escape_input(report.team.name))
)
notification.create_user_notification(user)
Rails.logger.error("Couldn't generate PDF for Report with id: #{report.id}. Error:\n #{error}")
@ -87,8 +87,8 @@ module Reports
type_of: :deliver,
title: I18n.t('projects.reports.index.generation.completed_pdf_notification_title'),
message: I18n.t('projects.reports.index.generation.completed_notification_message',
report_link: "<a href='#{report_path}'>#{sanitize_input(report.name)}</a>",
team_name: sanitize_input(report.team.name))
report_link: "<a href='#{report_path}'>#{escape_input(report.name)}</a>",
team_name: escape_input(report.team.name))
)
notification.create_user_notification(user)
ensure

View file

@ -12,7 +12,7 @@ class LabelPrinterSerializer < ActiveModel::Serializer
end
def display_name
object.description.present? ? sanitize_input("#{object.name}#{object.description}") : sanitize_input(object.name)
object.description.present? ? escape_input("#{object.name}#{object.description}") : escape_input(object.name)
end
def status

View file

@ -104,7 +104,7 @@ module Experiments
def task_name_presenter(my_module)
{
id: my_module.id,
name: my_module.name,
name: escape_input(my_module.name),
provisioning_status: my_module.provisioning_status,
url: protocols_my_module_path(my_module)
}
@ -148,8 +148,8 @@ module Experiments
def status_presenter(my_module)
{
name: my_module.my_module_status.name,
color: my_module.my_module_status.color
name: escape_input(my_module.my_module_status.name),
color: escape_input(my_module.my_module_status.color)
}
end

View file

@ -10,7 +10,7 @@
</span>
<% end %>
<% if users.length > 4 %>
<span class="more-users avatar-container" title="<%= sanitize_input(users[4..].map(&:full_name).join('&#013;')) %>">
<span class="more-users avatar-container" title="<%= users[4..].map(&:full_name).join('&#013;') %>">
+<%= users.length - 4 %>
</span>
<% end %>

View file

@ -8,9 +8,9 @@
<%= select_tag "activity",
options_for_select(my_module.tags.order(:id).map { |i|
[
i[:name],
i[:id],
{'data-params' => {color: i[:color]}.to_json}
escape_input(i[:name]),
escape_input(i[:id]),
{'data-params' => {color: escape_input(i[:color])}.to_json}
]
}),
{

View file

@ -7,7 +7,7 @@
<h1 data-view-mode="active" class="projects-title">
<%= render partial: "shared/inline_editing",
locals: {
initial_value: current_folder&.name,
initial_value: current_folder.name,
config: @inline_editable_title_config
} %>
</h1>

View file

@ -6,7 +6,7 @@
<% more_users = project.user_assignments[4..-1].to_a %>
<% if more_users.any? %>
<span class="more-users" title='<%= sanitize_input(user_names_with_roles(more_users)) %>'>
<span class="more-users" title='<%= user_names_with_roles(more_users) %>'>
+<%= more_users.size %>
</span>
<% end %>

View file

@ -5,7 +5,7 @@
data-item-id="<%= config[:item_id] %>"
data-path-to-update="<%= config[:path_to_update] %>"
data-original-name="<%= initial_value %>"
data-label-after='<%= config[:label_after]&.html_safe %>'
data-label-after='<%= config[:label_after] %>'
data-placeholder='<%= config[:placeholder] %>'
>
<div class="view-mode" data-placeholder="<%= config[:placeholder] %>" tabindex=0><%= initial_value %></div>

View file

@ -7,7 +7,7 @@
</div>
<div class="items">
<% experiment_group[:experiments].each do |experiment| %>
<li class="item" data-name="<%= sanitize_input(experiment.name) %>" data-id="<%= experiment.id.base62_encode %>" data-type="exp">
<li class="item" data-name="<%= experiment.name %>" data-id="<%= experiment.id.base62_encode %>" data-type="exp">
<span class='sa-type'>Exp</span>
<span class="item-text"><%= experiment.name %></span>
</li>

View file

@ -9,7 +9,7 @@
</div>
<div class="items">
<% task_group[:tasks].each do |task| %>
<li class="item" data-name="<%= sanitize_input(task.name) %>" data-id="<%= task.id.base62_encode %>" data-type="tsk">
<li class="item" data-name="<%= task.name %>" data-id="<%= task.id.base62_encode %>" data-type="tsk">
<span class='sa-type'>Tsk</span>
<span class="item-text"><%= task.name %></span>
</li>

View file

@ -1,7 +1,7 @@
<% limit_reached = projects.length == Constants::ATWHO_SEARCH_LIMIT + 1 %>
<div class="atwho-scroll-container">
<% projects.limit(Constants::ATWHO_SEARCH_LIMIT).each do |project| %>
<li class="item" data-name="<%= sanitize_input(project.name) %>" data-id="<%= project.id.base62_encode %>" data-type="prj">
<li class="item" data-name="<%= project.name %>" data-id="<%= project.id.base62_encode %>" data-type="prj">
<span class='sa-type'>Prj</span>
<span class="item-text"><%= project.name %></span>
</li>

View file

@ -1,7 +1,7 @@
<% limit_reached = repository_rows.length == Constants::ATWHO_SEARCH_LIMIT + 1 %>
<div class="atwho-scroll-container">
<% repository_rows.take(Constants::ATWHO_SEARCH_LIMIT).each do |row| %>
<li class="item" data-name="<%= sanitize_input(row[:name]) %>" data-id="<%= row[:id] %>" data-type="rep_item">
<li class="item" data-name="<%= row[:name] %>" data-id="<%= row[:id] %>" data-type="rep_item">
<span class='sa-type'><%= row[:repository_tag] %></span>
<span class="item-text"><%= row[:name] %></span>
</li>

View file

@ -5,7 +5,7 @@
</div>
<div class="atwho-scroll-container">
<% users.limit(Constants::ATWHO_SEARCH_LIMIT).each do |user| %>
<li class="atwho-user" data-full-name="<%= sanitize_input(user.full_name) %>" data-id="<%= user.id.base62_encode %>" data-type="rep_item">
<li class="atwho-user" data-full-name="<%= user.full_name %>" data-id="<%= user.id.base62_encode %>" data-type="rep_item">
<img src="<%= avatar_path(user, :icon_small) %>" class="avatar" />
<div class="user-info">
<div class="user-name item-text"><%= user.full_name %></div>

View file

@ -20,9 +20,9 @@
<span class="name-block"><strong><%= step.name %></strong></span>
<% unless step.new_record? %>
<span class="delimiter">|</span>
<span class="author-block"><%= sanitize_input t('protocols.steps.published_on',
timestamp: l(step.created_at, format: :full),
user: h(step.user.full_name)) %></span>
<span class="author-block">
<%= t('protocols.steps.published_on_html', timestamp: l(step.created_at, format: :full), user: step.user.full_name) %>
</span>
<% end %>
</a>
</div>

View file

@ -1,4 +1,4 @@
<% provide(:head_title, sanitize_input(t("notifications.title"))) %>
<% provide(:head_title, t('notifications.title')) %>
<div class="content-pane">
<div class="notifications-container">

View file

@ -63,17 +63,17 @@
data-field-to-update="description"
data-params-group="team"
data-path-to-update="<%= update_team_path(@team, format: :json) %>"
data-original-name="<%= sanitize_input(@team.description) %>"
data-original-name="<%= @team.description %>"
>
<div class="view-mode" data-placeholder="<%= t("users.settings.teams.show.enter_description") %>"><%= sanitize_input(@team.description) %></div>
<textarea placeholder="<%= t("users.settings.teams.show.enter_description") %>" class="hidden input-field smart-text-area" type="text" value="<%= sanitize_input(@team.description) %>" disabled><%= sanitize_input(@team.description) %></textarea>
<div class="view-mode" data-placeholder="<%= t("users.settings.teams.show.enter_description") %>"><%= @team.description %></div>
<textarea placeholder="<%= t("users.settings.teams.show.enter_description") %>" class="hidden input-field smart-text-area" type="text" value="<%= @team.description %>" disabled><%= @team.description %></textarea>
<div class="button-container">
<span class="cancel-button inline-field-button"><%= t('general.cancel') %></span>
<span class="save-button inline-field-button"><%= t('general.save') %></span>
</div>
</div>
<% else %>
<span class="view-mode disable-select"><%= @team.description.blank? ? t('users.settings.teams.edit.header_no_description') : sanitize_input(@team.description) %></span>
<span class="view-mode disable-select"><%= @team.description.blank? ? t('users.settings.teams.edit.header_no_description') : @team.description %></span>
<% end %>
</div>
<!-- End of HEADER -->

View file

@ -1669,7 +1669,7 @@ en:
name_label: "Inventory name"
name_placeholder: "My inventory"
submit: "Create"
success_flash: "Inventory <strong>%{name}</strong> successfully created."
success_flash_html: "Inventory <strong>%{name}</strong> successfully created."
modal_share:
title: "Share Inventory"
submit: "Save sharing options"
@ -2799,7 +2799,7 @@ en:
new_step: "New step"
subtitle: "Protocol Steps"
no_steps: "Protocol has no steps."
published_on: "Published on <em>%{timestamp}</em> by <em>%{user}</em>"
published_on_html: "Published on <em>%{timestamp}</em> by <em>%{user}</em>"
info_tab: "Info"
comments_tab: "Comments"
no_description: "This step has no description."