diff options
-rw-r--r-- | app/assets/javascripts/blob/openapi/index.js | 6 | ||||
-rw-r--r-- | config/application.rb | 1 | ||||
-rw-r--r-- | doc/user/packages/maven_repository/index.md | 2 | ||||
-rw-r--r-- | lib/api/entities/project.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/regex.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/slash_commands/deploy.rb | 12 | ||||
-rw-r--r-- | spec/features/projects/blobs/blob_show_spec.rb | 51 | ||||
-rw-r--r-- | spec/lib/api/entities/project_spec.rb | 22 | ||||
-rw-r--r-- | spec/lib/gitlab/regex_spec.rb | 13 | ||||
-rw-r--r-- | spec/lib/gitlab/slash_commands/deploy_spec.rb | 59 | ||||
-rw-r--r-- | spec/models/packages/package_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/api/projects_spec.rb | 2 |
12 files changed, 168 insertions, 8 deletions
diff --git a/app/assets/javascripts/blob/openapi/index.js b/app/assets/javascripts/blob/openapi/index.js index cb251274b18..b19cc19cb8c 100644 --- a/app/assets/javascripts/blob/openapi/index.js +++ b/app/assets/javascripts/blob/openapi/index.js @@ -1,5 +1,6 @@ import { SwaggerUIBundle } from 'swagger-ui-dist'; import createFlash from '~/flash'; +import { removeParams, updateHistory } from '~/lib/utils/url_utility'; import { __ } from '~/locale'; export default () => { @@ -7,9 +8,14 @@ export default () => { Promise.all([import(/* webpackChunkName: 'openapi' */ 'swagger-ui-dist/swagger-ui.css')]) .then(() => { + // Temporary fix to prevent an XSS attack due to "useUnsafeMarkdown" + // Once we upgrade Swagger to "4.0.0", we can safely remove this as it will be deprecated + // Follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/339696 + updateHistory({ url: removeParams(['useUnsafeMarkdown']), replace: true }); SwaggerUIBundle({ url: el.dataset.endpoint, dom_id: '#js-openapi-viewer', + useUnsafeMarkdown: false, }); }) .catch((error) => { diff --git a/config/application.rb b/config/application.rb index dde1eae30e7..f64e5c998eb 100644 --- a/config/application.rb +++ b/config/application.rb @@ -411,6 +411,7 @@ module Gitlab config.cache_store = :redis_cache_store, Gitlab::Redis::Cache.active_support_config config.active_job.queue_adapter = :sidekiq + config.active_job.logger = nil config.action_mailer.deliver_later_queue_name = :mailers # This is needed for gitlab-shell diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md index 1ca4bb2759d..6646f18e6fe 100644 --- a/doc/user/packages/maven_repository/index.md +++ b/doc/user/packages/maven_repository/index.md @@ -806,7 +806,7 @@ When the pipeline is successful, the package is created. The version string is validated by using the following regex. ```ruby -\A(\.?[\w\+-]+\.?)+\z +\A(?!.*\.\.)[\w+.-]+\z ``` You can play around with the regex and try your version strings on [this regular expression editor](https://rubular.com/r/rrLQqUXjfKEoL6). diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index e3f1e90b80f..662ca59852e 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -55,7 +55,9 @@ module API expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } expose(:container_registry_enabled) { |project, options| project.feature_available?(:container_registry, options[:current_user]) } expose :service_desk_enabled - expose :service_desk_address + expose :service_desk_address, if: -> (project, options) do + Ability.allowed?(options[:current_user], :admin_issue, project) + end expose(:can_create_merge_request_in) do |project, options| Ability.allowed?(options[:current_user], :create_merge_request_in, project) diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index 8b2f786a91a..904fc744c6b 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -57,7 +57,7 @@ module Gitlab end def maven_version_regex - @maven_version_regex ||= /\A(\.?[\w\+-]+\.?)+\z/.freeze + @maven_version_regex ||= /\A(?!.*\.\.)[\w+.-]+\z/.freeze end def maven_app_group_regex diff --git a/lib/gitlab/slash_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb index 157d924f99f..9fcefd99f81 100644 --- a/lib/gitlab/slash_commands/deploy.rb +++ b/lib/gitlab/slash_commands/deploy.rb @@ -3,8 +3,18 @@ module Gitlab module SlashCommands class Deploy < BaseCommand + DEPLOY_REGEX = /\Adeploy\s/.freeze + def self.match(text) - /\Adeploy\s+(?<from>\S+.*)\s+to+\s+(?<to>\S+.*)\z/.match(text) + return unless text&.match?(DEPLOY_REGEX) + + from, _, to = text.sub(DEPLOY_REGEX, '').rpartition(/\sto+\s/) + return if from.blank? || to.blank? + + { + from: from.strip, + to: to.strip + } end def self.help_message diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 8281e82959b..9d05c985af1 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -7,8 +7,8 @@ RSpec.describe 'File blob', :js do let(:project) { create(:project, :public, :repository) } - def visit_blob(path, anchor: nil, ref: 'master') - visit project_blob_path(project, File.join(ref, path), anchor: anchor) + def visit_blob(path, anchor: nil, ref: 'master', **additional_args) + visit project_blob_path(project, File.join(ref, path), anchor: anchor, **additional_args) wait_for_requests end @@ -1501,6 +1501,53 @@ RSpec.describe 'File blob', :js do end end end + + context 'openapi.yml' do + before do + file_name = 'openapi.yml' + + create_file(file_name, ' + swagger: \'2.0\' + info: + title: Classic API Resource Documentation + description: | + <div class="foo-bar" style="background-color: red;" data-foo-bar="baz"> + <h1>Swagger API documentation</h1> + </div> + version: production + basePath: /JSSResource/ + produces: + - application/xml + - application/json + consumes: + - application/xml + - application/json + security: + - basicAuth: [] + paths: + /accounts: + get: + responses: + \'200\': + description: No response was specified + tags: + - accounts + operationId: findAccounts + summary: Finds all accounts + ') + visit_blob(file_name, useUnsafeMarkdown: '1') + click_button('Display rendered file') + + wait_for_requests + end + + it 'removes `style`, `class`, and `data-*`` attributes from HTML' do + expect(page).to have_css('h1', text: 'Swagger API documentation') + expect(page).not_to have_css('.foo-bar') + expect(page).not_to have_css('[style="background-color: red;"]') + expect(page).not_to have_css('[data-foo-bar="baz"]') + end + end end end diff --git a/spec/lib/api/entities/project_spec.rb b/spec/lib/api/entities/project_spec.rb index 8d1c3aa878d..6b542278fa6 100644 --- a/spec/lib/api/entities/project_spec.rb +++ b/spec/lib/api/entities/project_spec.rb @@ -13,6 +13,28 @@ RSpec.describe ::API::Entities::Project do subject(:json) { entity.as_json } + describe '.service_desk_address' do + before do + allow(project).to receive(:service_desk_enabled?).and_return(true) + end + + context 'when a user can admin issues' do + before do + project.add_reporter(current_user) + end + + it 'is present' do + expect(json[:service_desk_address]).to be_present + end + end + + context 'when a user can not admin project' do + it 'is empty' do + expect(json[:service_desk_address]).to be_nil + end + end + end + describe '.shared_with_groups' do let(:group) { create(:group, :private) } diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 9514654204b..05f1c88a6ab 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -344,6 +344,18 @@ RSpec.describe Gitlab::Regex do describe '.maven_version_regex' do subject { described_class.maven_version_regex } + it 'has no ReDoS issues with long strings' do + Timeout.timeout(5) do + expect(subject).to match("aaaaaaaa.aaaaaaaaa+aa-111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111.11111111111111111111111111111111111111111111111111111111") + end + end + + it 'has no ReDos issues with long strings ending with an exclamation mark' do + Timeout.timeout(5) do + expect(subject).not_to match('a' * 50000 + '!') + end + end + it { is_expected.to match('0')} it { is_expected.to match('1') } it { is_expected.to match('03') } @@ -364,6 +376,7 @@ RSpec.describe Gitlab::Regex do it { is_expected.to match('703220b4e2cea9592caeb9f3013f6b1e5335c293') } it { is_expected.to match('RELEASE') } it { is_expected.not_to match('..1.2.3') } + it { is_expected.not_to match('1.2.3..beta') } it { is_expected.not_to match(' 1.2.3') } it { is_expected.not_to match("1.2.3 \r\t") } it { is_expected.not_to match("\r\t 1.2.3") } diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb index 36f47c711bc..71fca1e1fc8 100644 --- a/spec/lib/gitlab/slash_commands/deploy_spec.rb +++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb @@ -109,6 +109,21 @@ RSpec.describe Gitlab::SlashCommands::Deploy do end end end + + context 'with extra spaces in the deploy command' do + let(:regex_match) { described_class.match('deploy staging to production ') } + + before do + create(:ci_build, :manual, pipeline: pipeline, name: 'production', environment: 'production') + create(:ci_build, :manual, pipeline: pipeline, name: 'not prod', environment: 'not prod') + end + + it 'deploys to production' do + expect(subject[:text]) + .to start_with('Deployment started from staging to production') + expect(subject[:response_type]).to be(:in_channel) + end + end end end @@ -119,5 +134,49 @@ RSpec.describe Gitlab::SlashCommands::Deploy do expect(match[:from]).to eq('staging') expect(match[:to]).to eq('production') end + + it 'matches the environment with spaces in it' do + match = described_class.match('deploy staging env to production env') + + expect(match[:from]).to eq('staging env') + expect(match[:to]).to eq('production env') + end + + it 'matches the environment name with surrounding spaces' do + match = described_class.match('deploy staging to production ') + + # The extra spaces are stripped later in the code + expect(match[:from]).to eq('staging') + expect(match[:to]).to eq('production') + end + + it 'returns nil for text that is not a deploy command' do + match = described_class.match('foo bar') + + expect(match).to be_nil + end + + it 'returns nil for a partial command' do + match = described_class.match('deploy staging to ') + + expect(match).to be_nil + end + + context 'with ReDoS attempts' do + def duration_for(&block) + start = Time.zone.now + yield if block_given? + Time.zone.now - start + end + + it 'has smaller than linear execution time growth with a malformed "to"' do + Timeout.timeout(3.seconds) do + sample1 = duration_for { described_class.match("deploy abc t" + "o" * 1000 + "X") } + sample2 = duration_for { described_class.match("deploy abc t" + "o" * 4000 + "X") } + + expect((sample2 / sample1) < 4).to be_truthy + end + end + end end end diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index 6ee5219819c..44ba6e0e2fd 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -289,7 +289,6 @@ RSpec.describe Packages::Package, type: :model do it { is_expected.to allow_value('1.1-beta-2').for(:version) } it { is_expected.to allow_value('1.2-SNAPSHOT').for(:version) } it { is_expected.to allow_value('12.1.2-2-1').for(:version) } - it { is_expected.to allow_value('1.2.3..beta').for(:version) } it { is_expected.to allow_value('1.2.3-beta').for(:version) } it { is_expected.to allow_value('10.2.3-beta').for(:version) } it { is_expected.to allow_value('2.0.0.v200706041905-7C78EK9E_EkMNfNOd2d8qq').for(:version) } @@ -297,6 +296,7 @@ RSpec.describe Packages::Package, type: :model do it { is_expected.to allow_value('703220b4e2cea9592caeb9f3013f6b1e5335c293').for(:version) } it { is_expected.to allow_value('RELEASE').for(:version) } it { is_expected.not_to allow_value('..1.2.3').for(:version) } + it { is_expected.not_to allow_value('1.2.3..beta').for(:version) } it { is_expected.not_to allow_value(' 1.2.3').for(:version) } it { is_expected.not_to allow_value("1.2.3 \r\t").for(:version) } it { is_expected.not_to allow_value("\r\t 1.2.3").for(:version) } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 4f84e6f2562..cc546cbcda1 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -225,7 +225,7 @@ RSpec.describe API::Projects do create(:project, :public, group: create(:group)) end - it_behaves_like 'projects response without N + 1 queries', 0 do + it_behaves_like 'projects response without N + 1 queries', 1 do let(:current_user) { user } let(:additional_project) { create(:project, :public, group: create(:group)) } end |