Commit 6dcf819c authored by GitLab Release Tools Bot's avatar GitLab Release Tools Bot

Merge branch 'security-backend-xss-admin-email' into 'master'

Prevent malicious entry for group name

See merge request gitlab-org/security/gitlab!281
parents 4a79f8ef 976a25c8
...@@ -70,6 +70,9 @@ class Group < Namespace ...@@ -70,6 +70,9 @@ class Group < Namespace
validates :variables, variable_duplicates: true validates :variables, variable_duplicates: true
validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :name,
format: { with: Gitlab::Regex.group_name_regex,
message: Gitlab::Regex.group_name_regex_message }
add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
......
---
title: Prevent malicious entry for group name
merge_request:
author:
type: security
...@@ -202,7 +202,7 @@ describe SubscriptionsController do ...@@ -202,7 +202,7 @@ describe SubscriptionsController do
group.save group.save
subject subject
expect(response.body).to eq({ name: ["can't be blank"] }.to_json) expect(response.body).to include({ name: ["can't be blank", Gitlab::Regex.group_name_regex_message] }.to_json)
end end
end end
......
...@@ -16,6 +16,14 @@ module Gitlab ...@@ -16,6 +16,14 @@ module Gitlab
"It must start with letter, digit, emoji or '_'." "It must start with letter, digit, emoji or '_'."
end end
def group_name_regex
project_name_regex
end
def group_name_regex_message
project_name_regex_message
end
## ##
# Docker Distribution Registry repository / tag name rules # Docker Distribution Registry repository / tag name rules
# #
......
...@@ -258,6 +258,18 @@ describe GroupsController do ...@@ -258,6 +258,18 @@ describe GroupsController do
end end
end end
end end
context "malicious group name" do
subject { post :create, params: { group: { name: "<script>alert('Mayday!');</script>", path: "invalid_group_url" } } }
before do
sign_in(user)
end
it { expect { subject }.not_to change { Group.count } }
it { expect(subject).to render_template(:new) }
end
end end
describe 'GET #index' do describe 'GET #index' do
...@@ -836,6 +848,16 @@ describe GroupsController do ...@@ -836,6 +848,16 @@ describe GroupsController do
put :update, params: { id: group.to_param, group: { name: 'world' } } put :update, params: { id: group.to_param, group: { name: 'world' } }
end.to change { group.reload.name } end.to change { group.reload.name }
end end
context "malicious group name" do
subject { put :update, params: { id: group.to_param, group: { name: "<script>alert('Attack!');</script>" } } }
it { is_expected.to render_template(:edit) }
it 'does not update name' do
expect { subject }.not_to change { group.reload.name }
end
end
end end
describe 'DELETE #destroy' do describe 'DELETE #destroy' do
......
...@@ -523,7 +523,12 @@ describe Banzai::Filter::LabelReferenceFilter do ...@@ -523,7 +523,12 @@ describe Banzai::Filter::LabelReferenceFilter do
end end
context 'when group name has HTML entities' do context 'when group name has HTML entities' do
let(:another_group) { create(:group, name: '<img src=x onerror=alert(1)>', path: 'another_group') } let(:another_group) { create(:group, name: 'random', path: 'another_group') }
before do
another_group.name = "<img src=x onerror=alert(1)>"
another_group.save!(validate: false)
end
it 'escapes the HTML entities' do it 'escapes the HTML entities' do
expect(result.text) expect(result.text)
......
...@@ -3,9 +3,7 @@ ...@@ -3,9 +3,7 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Regex do describe Gitlab::Regex do
describe '.project_name_regex' do shared_examples_for 'project/group name regex' do
subject { described_class.project_name_regex }
it { is_expected.to match('gitlab-ce') } it { is_expected.to match('gitlab-ce') }
it { is_expected.to match('GitLab CE') } it { is_expected.to match('GitLab CE') }
it { is_expected.to match('100 lines') } it { is_expected.to match('100 lines') }
...@@ -15,6 +13,34 @@ describe Gitlab::Regex do ...@@ -15,6 +13,34 @@ describe Gitlab::Regex do
it { is_expected.not_to match('?gitlab') } it { is_expected.not_to match('?gitlab') }
end end
shared_examples_for 'project/group name error message' do
it { is_expected.to eq("can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.") }
end
describe '.project_name_regex' do
subject { described_class.project_name_regex }
it_behaves_like 'project/group name regex'
end
describe '.group_name_regex' do
subject { described_class.group_name_regex }
it_behaves_like 'project/group name regex'
end
describe '.project_name_regex_message' do
subject { described_class.project_name_regex_message }
it_behaves_like 'project/group name error message'
end
describe '.group_name_regex_message' do
subject { described_class.group_name_regex_message }
it_behaves_like 'project/group name error message'
end
describe '.environment_name_regex' do describe '.environment_name_regex' do
subject { described_class.environment_name_regex } subject { described_class.environment_name_regex }
......
...@@ -48,6 +48,9 @@ describe Group do ...@@ -48,6 +48,9 @@ describe Group do
describe 'validations' do describe 'validations' do
it { is_expected.to validate_presence_of :name } it { is_expected.to validate_presence_of :name }
it { is_expected.to allow_value('group test_4').for(:name) }
it { is_expected.not_to allow_value('test/../foo').for(:name) }
it { is_expected.not_to allow_value('<script>alert("Attack!")</script>').for(:name) }
it { is_expected.to validate_presence_of :path } it { is_expected.to validate_presence_of :path }
it { is_expected.not_to validate_presence_of :owner } it { is_expected.not_to validate_presence_of :owner }
it { is_expected.to validate_presence_of :two_factor_grace_period } it { is_expected.to validate_presence_of :two_factor_grace_period }
......
...@@ -642,6 +642,20 @@ describe API::Groups do ...@@ -642,6 +642,20 @@ describe API::Groups do
expect(json_response['default_branch_protection']).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) expect(json_response['default_branch_protection']).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
end end
context 'malicious group name' do
subject { put api("/groups/#{group1.id}", user1), params: { name: "<SCRIPT>alert('DOUBLE-ATTACK!')</SCRIPT>" } }
it 'returns bad request' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'does not update group name' do
expect { subject }.not_to change { group1.reload.name }
end
end
it 'returns 404 for a non existing group' do it 'returns 404 for a non existing group' do
put api('/groups/1328', user1), params: { name: new_group_name } put api('/groups/1328', user1), params: { name: new_group_name }
...@@ -1083,6 +1097,20 @@ describe API::Groups do ...@@ -1083,6 +1097,20 @@ describe API::Groups do
expect(json_response["parent_id"]).to eq(parent.id) expect(json_response["parent_id"]).to eq(parent.id)
end end
context 'malicious group name' do
subject { post api("/groups", user3), params: group_params }
let(:group_params) { attributes_for_group_api name: "<SCRIPT>alert('ATTACKED!')</SCRIPT>", path: "unique-url" }
it 'returns bad request' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
end
it { expect { subject }.not_to change { Group.count } }
end
it "does not create group, duplicate" do it "does not create group, duplicate" do
post api("/groups", user3), params: { name: 'Duplicate Test', path: group2.path } post api("/groups", user3), params: { name: 'Duplicate Test', path: group2.path }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment