Merge tag 'debian/12.6.7-1' into buster-fasttrack

gitlab Debian release 12.6.7-1
This commit is contained in:
Pirate Praveen 2020-02-22 14:35:53 +05:30
commit b399c68f5c
102 changed files with 1905 additions and 335 deletions

View file

@ -1,5 +1,17 @@
Please view this file on the master branch, on stable branches it's out of date.
## 12.6.6
- No changes.
## 12.6.5
- No changes.
## 12.6.4
- No changes.
## 12.6.3
- No changes.

View file

@ -2,6 +2,45 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 12.6.7
### Security (1 change)
- Fix ProjectAuthorization calculation for shared groups.
## 12.6.6
### Security (1 change)
- Update workhorse to v8.20.0.
## 12.6.5
### Security (19 changes, 1 of them is from the community)
- Update rack-cors to 1.0.6.
- Update rdoc to 6.1.2.
- Bump rubyzip to 2.0.0. (Utkarsh Gupta)
- Cleanup todos for users from a removed linked group.
- Disable access to last_pipeline in commits API for users without read permissions.
- Add constraint to group dependency proxy endpoint param.
- Limit number of AsciiDoc includes per document.
- Prevent API access for unconfirmed users.
- Enforce permission check when counting activity events.
- Prevent gafana integration token from being displayed as a plain text to other project maintainers, by only displaying a masked version of it.
- Fix xss on frequent groups dropdown.
- Fix XSS vulnerability on custom project templates form.
- Protect internal CI builds from external overrides.
- ImportExport::ExportService to require admin_project permission.
- Make sure that only system notes where all references are visible to user are exposed in GraphQL API.
- Disable caching of repository/files/:file_path/raw API endpoint.
- Make cross-repository comparisons happen in the source repository.
- Update excon to 0.71.1 to fix CVE-2019-16779.
- Add workhorse request verification to package upload endpoints.
## 12.6.4
### Security (1 change)

View file

@ -1 +1 @@
8.18.0
8.20.0

View file

@ -65,7 +65,7 @@ gem 'u2f', '~> 0.2.1'
# GitLab Pages
gem 'validates_hostname', '~> 1.0.6'
gem 'rubyzip', '~> 1.3.0', require: 'zip'
gem 'rubyzip', '~> 2.0.0', require: 'zip'
# GitLab Pages letsencrypt support
gem 'acme-client', '~> 2.0.2'
@ -142,7 +142,7 @@ gem 'gitlab-markup', '~> 1.7.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'commonmarker', '~> 0.20'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.0'
gem 'rdoc', '~> 6.1.2'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'

View file

@ -260,7 +260,7 @@ GEM
et-orbi (1.2.1)
tzinfo
eventmachine (1.2.7)
excon (0.62.0)
excon (0.71.1)
execjs (2.6.0)
expression_parser (0.9.0)
extended-markdown-filter (0.6.0)
@ -763,7 +763,8 @@ GEM
rack (>= 0.4)
rack-attack (6.2.0)
rack (>= 1.0, < 3)
rack-cors (1.0.2)
rack-cors (1.0.6)
rack (>= 1.6.0)
rack-oauth2 (1.9.3)
activesupport
attr_required
@ -820,7 +821,7 @@ GEM
ffi (>= 1.0.6)
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
rdoc (6.0.4)
rdoc (6.1.2)
re2 (1.1.1)
recaptcha (4.13.1)
json
@ -929,7 +930,7 @@ GEM
sexp_processor (~> 4.9)
rubyntlm (0.6.2)
rubypants (0.2.0)
rubyzip (1.3.0)
rubyzip (2.0.0)
rugged (0.28.4.1)
safe_yaml (1.0.4)
sanitize (4.6.6)
@ -1299,7 +1300,7 @@ DEPENDENCIES
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
rbtrace (~> 0.4)
rdoc (~> 6.0)
rdoc (~> 6.1.2)
re2 (~> 1.1.1)
recaptcha (~> 4.11)
redis (~> 4.0)
@ -1323,7 +1324,7 @@ DEPENDENCIES
ruby-prof (~> 1.0.0)
ruby-progressbar
ruby_parser (~> 3.8)
rubyzip (~> 1.3.0)
rubyzip (~> 2.0.0)
rugged (~> 0.28)
sanitize (~> 4.6)
sassc-rails (~> 2.1.0)

View file

@ -1 +1 @@
12.6.4
12.6.7

View file

@ -5,7 +5,7 @@ import AccessorUtilities from '~/lib/utils/accessor';
import eventHub from '../event_hub';
import store from '../store/';
import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants';
import { isMobile, updateExistingFrequentItem } from '../utils';
import { isMobile, updateExistingFrequentItem, sanitizeItem } from '../utils';
import FrequentItemsSearchInput from './frequent_items_search_input.vue';
import FrequentItemsList from './frequent_items_list.vue';
import frequentItemsMixin from './frequent_items_mixin';
@ -64,7 +64,9 @@ export default {
this.fetchFrequentItems();
}
},
logItemAccess(storageKey, item) {
logItemAccess(storageKey, unsanitizedItem) {
const item = sanitizeItem(unsanitizedItem);
if (!AccessorUtilities.isLocalStorageAccessSafe()) {
return false;
}

View file

@ -1,6 +1,7 @@
<script>
import FrequentItemsListItem from './frequent_items_list_item.vue';
import frequentItemsMixin from './frequent_items_mixin';
import { sanitizeItem } from '../utils';
export default {
components: {
@ -48,6 +49,9 @@ export default {
? this.translations.itemListErrorMessage
: this.translations.itemListEmptyMessage;
},
sanitizedItems() {
return this.items.map(sanitizeItem);
},
},
};
</script>
@ -59,7 +63,7 @@ export default {
{{ listEmptyMessage }}
</li>
<frequent-items-list-item
v-for="item in items"
v-for="item in sanitizedItems"
v-else
:key="item.id"
:item-id="item.id"

View file

@ -1,5 +1,6 @@
import _ from 'underscore';
import bp from '~/breakpoints';
import sanitize from 'sanitize-html';
import { FREQUENT_ITEMS, HOUR_IN_MS } from './constants';
export const isMobile = () => {
@ -47,3 +48,9 @@ export const updateExistingFrequentItem = (frequentItem, item) => {
lastAccessedOn: accessedOverHourAgo ? Date.now() : frequentItem.lastAccessedOn,
};
};
export const sanitizeItem = item => ({
...item,
name: sanitize(item.name.toString(), { allowedTags: [] }),
namespace: sanitize(item.namespace.toString(), { allowedTags: [] }),
});

View file

@ -1,6 +1,7 @@
import $ from 'jquery';
import axios from './lib/utils/axios_utils';
import Api from './api';
import { escape } from 'lodash';
import { normalizeHeaders } from './lib/utils/common_utils';
import { __ } from '~/locale';
@ -75,10 +76,12 @@ const groupsSelect = () => {
}
},
formatResult(object) {
return `<div class='group-result'> <div class='group-name'>${object.full_name}</div> <div class='group-path'>${object.full_path}</div> </div>`;
return `<div class='group-result'> <div class='group-name'>${escape(
object.full_name,
)}</div> <div class='group-path'>${object.full_path}</div> </div>`;
},
formatSelection(object) {
return object.full_name;
return escape(object.full_name);
},
dropdownCssClass: 'ajax-groups-dropdown select2-infinite',
// we do not want to escape markup since we are displaying html in results

View file

@ -5,6 +5,7 @@ require 'fogbugz'
class ApplicationController < ActionController::Base
include Gitlab::GonHelper
include Gitlab::NoCacheHeaders
include GitlabRoutingHelper
include PageLayoutHelper
include SafeParamsHelper
@ -54,7 +55,6 @@ class ApplicationController < ActionController::Base
# Adds `no-store` to the DEFAULT_CACHE_CONTROL, to prevent security
# concerns due to caching private data.
DEFAULT_GITLAB_CACHE_CONTROL = "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store"
DEFAULT_GITLAB_CONTROL_NO_CACHE = "#{DEFAULT_GITLAB_CACHE_CONTROL}, no-cache"
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@ -246,9 +246,9 @@ class ApplicationController < ActionController::Base
end
def no_cache_headers
headers['Cache-Control'] = DEFAULT_GITLAB_CONTROL_NO_CACHE
headers['Pragma'] = 'no-cache' # HTTP 1.0 compatibility
headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
DEFAULT_GITLAB_NO_CACHE_HEADERS.each do |k, v|
headers[k] = v
end
end
def default_headers

View file

@ -19,7 +19,7 @@ class DashboardController < Dashboard::ApplicationController
format.json do
load_events
pager_json("events/_events", @events.count)
pager_json('events/_events', @events.count { |event| event.visible_to_user?(current_user) })
end
end
end
@ -37,6 +37,7 @@ class DashboardController < Dashboard::ApplicationController
@events = EventCollection
.new(projects, offset: params[:offset].to_i, filter: event_filter)
.to_a
.map(&:present)
Events::RenderService.new(current_user).execute(@events)
end

View file

@ -91,7 +91,7 @@ class GroupsController < Groups::ApplicationController
format.json do
load_events
pager_json("events/_events", @events.count)
pager_json("events/_events", @events.count { |event| event.visible_to_user?(current_user) })
end
end
end
@ -209,8 +209,9 @@ class GroupsController < Groups::ApplicationController
.includes(:namespace)
@events = EventCollection
.new(projects, offset: params[:offset].to_i, filter: event_filter, groups: groups)
.to_a
.new(projects, offset: params[:offset].to_i, filter: event_filter, groups: groups)
.to_a
.map(&:present)
Events::RenderService
.new(current_user)

View file

@ -119,7 +119,7 @@ class ProjectsController < Projects::ApplicationController
format.html
format.json do
load_events
pager_json('events/_events', @events.count)
pager_json('events/_events', @events.count { |event| event.visible_to_user?(current_user) })
end
end
end
@ -340,6 +340,7 @@ class ProjectsController < Projects::ApplicationController
@events = EventCollection
.new(projects, offset: params[:offset].to_i, filter: event_filter)
.to_a
.map(&:present)
Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?)
end

View file

@ -368,8 +368,8 @@ module ProjectsHelper
@project.grafana_integration&.grafana_url
end
def grafana_integration_token
@project.grafana_integration&.token
def grafana_integration_masked_token
@project.grafana_integration&.masked_token
end
def grafana_integration_enabled?

View file

@ -1,11 +1,14 @@
# frozen_string_literal: true
class GenericCommitStatus < CommitStatus
EXTERNAL_STAGE_IDX = 1_000_000
before_validation :set_default_values
validates :target_url, addressable_url: true,
length: { maximum: 255 },
allow_nil: true
validate :name_uniqueness_across_types, unless: :importing?
# GitHub compatible API
alias_attribute :context, :name
@ -13,7 +16,7 @@ class GenericCommitStatus < CommitStatus
def set_default_values
self.context ||= 'default'
self.stage ||= 'external'
self.stage_idx ||= 1000000
self.stage_idx ||= EXTERNAL_STAGE_IDX
end
def tags
@ -25,4 +28,14 @@ class GenericCommitStatus < CommitStatus
.new(self, current_user)
.fabricate!
end
private
def name_uniqueness_across_types
return if !pipeline || name.blank?
if pipeline.statuses.by_name(name).where.not(type: type).exists?
errors.add(:name, :taken)
end
end
end

View file

@ -8,11 +8,13 @@ class GrafanaIntegration < ApplicationRecord
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32
before_validation :check_token_changes
validates :grafana_url,
length: { maximum: 1024 },
addressable_url: { enforce_sanitization: true, ascii_only: true }
validates :token, :project, presence: true
validates :encrypted_token, :project, presence: true
validates :enabled, inclusion: { in: [true, false] }
@ -23,4 +25,28 @@ class GrafanaIntegration < ApplicationRecord
@client ||= ::Grafana::Client.new(api_url: grafana_url.chomp('/'), token: token)
end
def masked_token
mask(encrypted_token)
end
def masked_token_was
mask(encrypted_token_was)
end
private
def token
decrypt(:token, encrypted_token)
end
def check_token_changes
return unless [encrypted_token_was, masked_token_was].include?(token)
clear_attribute_changes [:token, :encrypted_token, :encrypted_token_iv]
end
def mask(token)
token&.squish&.gsub(/./, '*')
end
end

View file

@ -551,7 +551,8 @@ class Note < ApplicationRecord
# if they are not equal, then there are private/confidential references as well
user_visible_reference_count > 0 && user_visible_reference_count == total_reference_count
else
referenced_mentionables(user).any?
refs = all_references(user)
refs.all.any? && refs.stateful_not_visible_counter == 0
end
end

View file

@ -2333,6 +2333,10 @@ class Project < ApplicationRecord
end
end
def template_source?
false
end
private
def closest_namespace_setting(name)

View file

@ -21,6 +21,14 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0
condition(:deactivated) { @user&.deactivated? }
desc "User email is unconfirmed or user account is locked"
with_options scope: :user, score: 0
condition(:inactive) do
Feature.enabled?(:inactive_policy_condition, default_enabled: true) &&
@user &&
!@user&.active_for_authentication?
end
with_options scope: :user, score: 0
condition(:external_user) { @user.nil? || @user.external? }

View file

@ -36,6 +36,13 @@ class GlobalPolicy < BasePolicy
enable :use_slash_commands
end
rule { inactive }.policy do
prevent :log_in
prevent :access_api
prevent :access_git
prevent :use_slash_commands
end
rule { blocked | internal }.policy do
prevent :log_in
prevent :access_api

View file

@ -3,6 +3,18 @@
class EventPresenter < Gitlab::View::Presenter::Delegated
presents :event
def initialize(subject, **attributes)
super
@visible_to_user_cache = ActiveSupport::Cache::MemoryStore.new
end
# Caching `visible_to_user?` method in the presenter beause it might be called multiple times.
def visible_to_user?(user = nil)
@visible_to_user_cache.fetch(user&.id) { super(user) }
end
# implement cache here
def resource_parent_name
resource_parent&.full_name || ''
end

View file

@ -18,7 +18,7 @@ class CompareService
return unless raw_compare && raw_compare.base && raw_compare.head
Compare.new(raw_compare,
target_project,
start_project,
base_sha: base_sha,
straight: straight)
end

View file

@ -6,6 +6,12 @@ module Projects
def execute(group_link)
return false unless group_link
if group_link.project.private?
TodosDestroyer::ProjectPrivateWorker.perform_in(Todo::WAIT_FOR_DELETE, project.id)
else
TodosDestroyer::ConfidentialIssueWorker.perform_in(Todo::WAIT_FOR_DELETE, nil, project.id)
end
group_link.destroy
end
end

View file

@ -4,6 +4,12 @@ module Projects
module ImportExport
class ExportService < BaseService
def execute(after_export_strategy = nil, options = {})
unless project.template_source? || can?(current_user, :admin_project, project)
raise ::Gitlab::ImportExport::Error.new(
"User with ID: %s does not have permission to Project %s with ID: %s." %
[current_user.id, project.name, project.id])
end
@shared = project.import_export_shared
save_all!

View file

@ -19,8 +19,10 @@ module Users
LEASE_TIMEOUT = 1.minute.to_i
# user - The User for which to refresh the authorized projects.
def initialize(user)
def initialize(user, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
@user = user
@incorrect_auth_found_callback = incorrect_auth_found_callback
@missing_auth_found_callback = missing_auth_found_callback
# We need an up to date User object that has access to all relations that
# may have been created earlier. The only way to ensure this is to reload
@ -55,6 +57,10 @@ module Users
# rows not in the new list or with a different access level should be
# removed.
if !fresh[project_id] || fresh[project_id] != row.access_level
if incorrect_auth_found_callback
incorrect_auth_found_callback.call(project_id, row.access_level)
end
array << row.project_id
end
end
@ -63,6 +69,10 @@ module Users
# rows not in the old list or with a different access level should be
# added.
if !current[project_id] || current[project_id].access_level != level
if missing_auth_found_callback
missing_auth_found_callback.call(project_id, level)
end
array << [user.id, project_id, level]
end
end
@ -104,5 +114,9 @@ module Users
def fresh_authorizations
Gitlab::ProjectAuthorizations.new(user).calculate
end
private
attr_reader :incorrect_auth_found_callback, :missing_auth_found_callback
end
end

View file

@ -1,2 +1,2 @@
.js-grafana-integration{ data: { operations_settings_endpoint: project_settings_operations_path(@project),
grafana_integration: { url: grafana_integration_url, token: grafana_integration_token, enabled: grafana_integration_enabled?.to_s } } }
grafana_integration: { url: grafana_integration_url, token: grafana_integration_masked_token, enabled: grafana_integration_enabled?.to_s } } }

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
class ScheduleRecalculateProjectAuthorizations < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'RecalculateProjectAuthorizations'
BATCH_SIZE = 2_500
DELAY_INTERVAL = 2.minutes.to_i
disable_ddl_transaction!
class Namespace < ActiveRecord::Base
include ::EachBatch
self.table_name = 'namespaces'
end
class ProjectAuthorization < ActiveRecord::Base
include ::EachBatch
self.table_name = 'project_authorizations'
end
def up
say "Scheduling #{MIGRATION} jobs"
max_group_id = Namespace.where(type: 'Group').maximum(:id)
project_authorizations = ProjectAuthorization.where('project_id <= ?', max_group_id)
.select(:user_id)
.distinct
project_authorizations.each_batch(of: BATCH_SIZE, column: :user_id) do |authorizations, index|
delay = index * DELAY_INTERVAL
user_ids = authorizations.map(&:user_id)
BackgroundMigrationWorker.perform_in(delay, MIGRATION, [user_ids])
end
end
def down
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_12_16_183532) do
ActiveRecord::Schema.define(version: 2020_02_04_113223) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"

17
debian/changelog vendored
View file

@ -1,3 +1,20 @@
gitlab (12.6.7-1) experimental; urgency=medium
[ Abhijith PA ]
* New upstream version 12.6.6 (Fixes: CVE-2020-7973, CVE-2020-7968)
* Remove patch 0760-bump-rubyzip.patch
* Refresh patches.
[ Pirate Praveen ]
* Update minimum version of ruby-excon and ruby-rack-cors
* New upstream version 12.6.7
* Relax ruby-rack-cors version in Gemfile
* Add lines removed by mistake in an earlier commit
* Relax dependency on rdoc (ruby 2.5 comes with rdoc 6.0)
* Refresh patches again to relax all stable gems
-- Pirate Praveen <praveen@debian.org> Sat, 15 Feb 2020 14:12:32 +0100
gitlab (12.6.4-1+fto10+1) buster-fasttrack; urgency=medium
* Rebuild for buster-fasttrack.

4
debian/control vendored
View file

@ -97,7 +97,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
# API
ruby-grape (>= 1.1~),
ruby-grape-entity (>= 0.7.1~),
ruby-rack-cors (>= 1.0~),
ruby-rack-cors (>= 1.0.6~),
# GraphQL API
ruby-graphql (>= 1.9.11~),
ruby-graphiql-rails (>= 1.4.10~),
@ -114,7 +114,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
# for backups
ruby-fog-aws (>= 3.5~),
ruby-fog-core (>= 2.1~),
ruby-excon (>= 0.60~),
ruby-excon (>= 0.72~),
ruby-fog-google (>= 1.9~),
ruby-fog-local (>= 0.6~),
ruby-fog-openstack (>= 1.0~),

View file

@ -16,15 +16,24 @@ gitlab Gemfile
# Supported DBs
gem 'pg', '~> 1.1'
@@ -29,25 +29,25 @@
@@ -22,32 +22,32 @@
gem 'grape-path-helpers', '~> 1.1'
gem 'faraday', '~> 0.12'
-gem 'marginalia', '~> 1.8.0'
+gem 'marginalia', '~> 1.8'
# Authentication libraries
gem 'devise', '~> 4.6'
gem 'doorkeeper', '~> 4.3'
gem 'doorkeeper-openid_connect', '~> 1.5'
gem 'omniauth', '~> 1.8'
-gem 'omniauth-auth0', '~> 2.0.0'
+gem 'omniauth-auth0', '~> 2.0'
gem 'omniauth-azure-oauth2', '~> 0.0.9'
gem 'omniauth-cas3', '~> 1.1.4'
-gem 'omniauth-cas3', '~> 1.1.4'
-gem 'omniauth-facebook', '~> 4.0.0'
+gem 'omniauth-cas3', '~> 1.1', '>= 1.1.4'
+gem 'omniauth-facebook', '~> 4.0'
gem 'omniauth-github', '~> 1.3'
-gem 'omniauth-gitlab', '~> 1.0.2'
@ -50,7 +59,7 @@ gitlab Gemfile
# Kerberos authentication. EE-only
gem 'gssapi', group: :kerberos
@@ -58,41 +58,41 @@
@@ -58,42 +58,42 @@
gem 'invisible_captcha', '~> 0.12.1'
# Two-factor authentication
@ -63,9 +72,9 @@ gitlab Gemfile
# GitLab Pages
-gem 'validates_hostname', '~> 1.0.6'
-gem 'rubyzip', '~> 1.3.0', require: 'zip'
-gem 'rubyzip', '~> 2.0.0', require: 'zip'
+gem 'validates_hostname', '~> 1.0', '>= 1.0.6'
+gem 'rubyzip', '~> 1.3', require: 'zip'
+gem 'rubyzip', '~> 2.0', require: 'zip'
# GitLab Pages letsencrypt support
-gem 'acme-client', '~> 2.0.2'
+gem 'acme-client', '~> 2.0', '>= 2.0.2'
@ -98,12 +107,13 @@ gitlab Gemfile
# TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released:
# https://gitlab.com/gitlab-org/gitlab/issues/31747
-gem 'graphiql-rails', '~> 1.4.10'
-gem 'apollo_upload_server', '~> 2.0.0.beta3'
+gem 'graphiql-rails', '~> 1.4', '>= 1.4.10'
+gem 'apollo_upload_server', '>= 2.0.0.beta3'
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
gem 'apollo_upload_server', '~> 2.0.0.beta3'
-gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
+gem 'graphql-docs', '~> 1.6', group: [:development, :test]
# Disable strong_params so that Mash does not respond to :permitted?
gem 'hashie-forbidden_attributes'
@@ -102,7 +102,7 @@
gem 'kaminari', '~> 1.0'
@ -139,16 +149,16 @@ gitlab Gemfile
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.12'
-gem 'deckar01-task_list', '2.2.1'
gem 'deckar01-task_list', '2.2.1'
-gem 'gitlab-markup', '~> 1.7.0'
-gem 'github-markup', '~> 1.7.0', require: 'github/markup'
+gem 'deckar01-task_list', '~> 2.2', '>= 2.2.1'
+gem 'gitlab-markup', '~> 1.7'
+gem 'github-markup', '~> 1.7', require: 'github/markup'
gem 'commonmarker', '~> 0.20'
-gem 'RedCloth', '~> 4.3.2'
-gem 'rdoc', '~> 6.1.2'
+gem 'RedCloth', '~> 4.3', '>= 4.3.2'
gem 'rdoc', '~> 6.0'
+gem 'rdoc', '~> 6.1'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
@ -161,8 +171,8 @@ gitlab Gemfile
gem 'truncato', '~> 0.7.11'
-gem 'bootstrap_form', '~> 4.2.0'
-gem 'nokogiri', '~> 1.10.5'
+gem 'bootstrap_form', '~> 4.2', '>= 4.2.0'
+gem 'nokogiri', '~> 1.10', '>= 1.10.5'
+gem 'bootstrap_form', '~> 4.2'
+gem 'nokogiri', '~> 1.0', '>= 1.10.5'
gem 'escape_utils', '~> 1.1'
# Calendar rendering
@ -273,10 +283,10 @@ gitlab Gemfile
-gem 'sassc-rails', '~> 2.1.0'
-gem 'uglifier', '~> 2.7.2'
+gem 'sassc-rails', '~> 2.1'
+gem 'uglifier', '~> 2.7', '>=2.7.2'
+gem 'uglifier', '~> 2.7', '>= 2.7.2'
-gem 'addressable', '~> 2.5.2'
+gem 'addressable', '~> 2.5', '>=2.5.2'
+gem 'addressable', '~> 2.5', '>= 2.5.2'
gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.3'
gem 'gon', '~> 6.2'

View file

@ -5,8 +5,8 @@ Bundler will fail when it can't find these locally
@@ -93,7 +93,6 @@
# https://gitlab.com/gitlab-org/gitlab/issues/31747
gem 'graphiql-rails', '~> 1.4', '>= 1.4.10'
gem 'apollo_upload_server', '>= 2.0.0.beta3'
-gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
gem 'apollo_upload_server', '~> 2.0.0.beta3'
-gem 'graphql-docs', '~> 1.6', group: [:development, :test]
# Disable strong_params so that Mash does not respond to :permitted?
gem 'hashie-forbidden_attributes'
@ -18,7 +18,7 @@ Bundler will fail when it can't find these locally
gem 'batch-loader', '~> 1.4'
@@ -331,21 +329,6 @@
@@ -331,68 +329,6 @@
gem 'raindrops', '~> 0.18'
end
@ -37,6 +37,53 @@ Bundler will fail when it can't find these locally
- gem 'thin', '~> 1.7.0'
-end
-
group :development, :test do
gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
gem 'pry-byebug', '~> 3.5.1', platform: :mri
-group :development, :test do
- gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
- gem 'pry-byebug', '~> 3.5.1', platform: :mri
- gem 'pry-rails', '~> 0.3.4'
-
- gem 'awesome_print', require: false
-
- gem 'database_cleaner', '~> 1.7.0'
- gem 'factory_bot_rails', '~> 5.1.0'
- gem 'rspec-rails', '~> 4.0.0.beta3'
-
- # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
- gem 'minitest', '~> 5.11.0'
-
- # Generate Fake data
- gem 'ffaker', '~> 2.10'
-
- gem 'spring', '~> 2.0.0'
- gem 'spring-commands-rspec', '~> 1.0.4'
-
- gem 'gitlab-styles', '~> 2.7', require: false
- # Pin these dependencies, otherwise a new rule could break the CI pipelines
- gem 'rubocop', '~> 0.69.0'
- gem 'rubocop-performance', '~> 1.1.0'
- gem 'rubocop-rspec', '~> 1.22.1'
-
- gem 'scss_lint', '~> 0.56.0', require: false
- gem 'haml_lint', '~> 0.34.0', require: false
- gem 'simplecov', '~> 0.16.1', require: false
- gem 'bundler-audit', '~> 0.5.0', require: false
-
- gem 'benchmark-ips', '~> 2.3.0', require: false
-
- gem 'knapsack', '~> 1.17'
-
- gem 'stackprof', '~> 0.2.13', require: false
-
- gem 'simple_po_parser', '~> 1.1.2', require: false
-
- gem 'timecop', '~> 0.8.0'
-end
-
-# Gems required in omnibus-gitlab pipeline
-group :development, :test, :omnibus do
- gem 'license_finder', '~> 5.4', require: false
-end
-
group :test do
gem 'fuubar', '~> 2.2.0'
gem 'rspec-retry', '~> 0.6.1'

View file

@ -4,23 +4,8 @@
gem 'raindrops', '~> 0.18'
end
-group :development, :test do
+if ENV["INCLUDE_TEST_DEPENDS"] == "true"
gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem 'pry-rails', '~> 0.3.4'
@@ -369,14 +369,10 @@
gem 'simple_po_parser', '~> 1.1.2', require: false
gem 'timecop', '~> 0.8.0'
-end
# Gems required in omnibus-gitlab pipeline
-group :development, :test, :omnibus do
gem 'license_finder', '~> 5.4', require: false
-end
-group :test do
+if ENV["INCLUDE_TEST_DEPENDS"] == "true"
gem 'fuubar', '~> 2.2.0'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_profiling', '~> 0.0.5'

View file

@ -3,7 +3,7 @@
@@ -137,7 +137,6 @@
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.12'
gem 'deckar01-task_list', '~> 2.2', '>= 2.2.1'
gem 'deckar01-task_list', '2.2.1'
-gem 'gitlab-markup', '~> 1.7'
gem 'github-markup', '~> 1.7', require: 'github/markup'
gem 'commonmarker', '~> 0.20'

View file

@ -1,10 +0,0 @@
--- a/Gemfile
+++ b/Gemfile
@@ -323,7 +323,6 @@
end
if ENV["INCLUDE_TEST_DEPENDS"] == "true"
- gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem 'pry-rails', '~> 0.3.4'

View file

@ -18,7 +18,7 @@ Subject: [PATCH 1/2] Update asciidoctor-plantuml 0.0.9 -> 0.0.10
+gem 'asciidoctor-plantuml', '~> 0.0.10'
gem 'rouge', '~> 3.11'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2', '>= 4.2.0'
gem 'bootstrap_form', '~> 4.2'
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -71,7 +71,7 @@
@ -30,7 +30,7 @@ Subject: [PATCH 1/2] Update asciidoctor-plantuml 0.0.9 -> 0.0.10
asciidoctor (>= 1.5.6, < 3.0.0)
ast (2.4.0)
atlassian-jwt (0.2.0)
@@ -1125,7 +1125,7 @@
@@ -1126,7 +1126,7 @@
asana (~> 0.9)
asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1)

View file

@ -1,6 +1,6 @@
--- a/Gemfile
+++ b/Gemfile
@@ -421,9 +421,9 @@
@@ -379,9 +379,9 @@
# Gitaly GRPC protocol definitions
gem 'gitaly', '~> 1.73'

11
debian/patches/0488-relax-rdoc.patch vendored Normal file
View file

@ -0,0 +1,11 @@
--- a/Gemfile
+++ b/Gemfile
@@ -137,7 +137,7 @@
gem 'github-markup', '~> 1.7', require: 'github/markup'
gem 'commonmarker', '~> 0.20'
gem 'RedCloth', '~> 4.3', '>= 4.3.2'
-gem 'rdoc', '~> 6.1'
+gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'

View file

@ -1,6 +1,6 @@
--- a/package.json
+++ b/package.json
@@ -142,62 +142,6 @@
@@ -143,62 +143,6 @@
"xterm": "^3.5.0"
},
"devDependencies": {

View file

@ -3,7 +3,7 @@ Author: Utkarsh Gupta <guptautkarsh2102@gmail.com>
--- a/package.json
+++ b/package.json
@@ -88,6 +88,7 @@
@@ -82,6 +82,7 @@
"fuzzaldrin-plus": "^0.5.0",
"glob": "^7.1.2",
"graphql": "^14.0.2",

View file

@ -1,6 +1,6 @@
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -6,7 +6,6 @@
@@ -6,7 +6,6 @@ const VueLoaderPlugin = require('vue-loa
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
const CompressionPlugin = require('compression-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
@ -8,7 +8,7 @@
const CopyWebpackPlugin = require('copy-webpack-plugin');
const vendorDllHash = require('./helpers/vendor_dll_hash');
@@ -151,12 +150,12 @@
@@ -151,12 +150,12 @@ module.exports = {
resolve: {
extensions: ['.js', '.gql', '.graphql'],
@ -23,7 +23,7 @@
},
module: {
@@ -431,19 +430,6 @@
@@ -431,19 +430,6 @@ module.exports = {
// enable HMR only in webpack-dev-server
DEV_SERVER_LIVERELOAD && new webpack.HotModuleReplacementPlugin(),
@ -43,7 +43,7 @@
new webpack.DefinePlugin({
// This one is used to define window.gon.ee and other things properly in tests:
'process.env.IS_EE': JSON.stringify(IS_EE),
@@ -469,6 +455,7 @@
@@ -469,6 +455,7 @@ module.exports = {
node: {
fs: 'empty', // sqljs requires fs
@ -53,7 +53,7 @@
};
--- a/package.json
+++ b/package.json
@@ -50,58 +50,33 @@
@@ -50,59 +50,34 @@
"apollo-link-batch-http": "^1.2.11",
"apollo-upload-client": "^10.0.0",
"at.js": "^1.5.4",
@ -101,6 +101,7 @@
- "jszip": "^3.1.3",
- "jszip-utils": "^0.0.2",
"katex": "^0.10.0",
"lodash": "^4.17.15",
- "marked": "^0.3.12",
"mermaid": "^8.4.2",
"monaco-editor": "^0.15.6",
@ -112,7 +113,7 @@
"prismjs": "^1.6.0",
"prosemirror-markdown": "^1.3.0",
"prosemirror-model": "^1.6.4",
@@ -118,13 +93,9 @@
@@ -119,13 +94,9 @@
"svg4everybody": "2.1.9",
"swagger-ui-dist": "^3.24.3",
"three": "^0.84.0",
@ -126,7 +127,7 @@
"url-loader": "^2.1.0",
"visibilityjs": "^1.2.4",
"vue": "^2.6.10",
@@ -135,10 +106,6 @@
@@ -136,10 +107,6 @@
"vue-virtual-scroll-list": "^1.3.1",
"vuedraggable": "^2.23.0",
"vuex": "^3.1.0",

View file

@ -5,7 +5,6 @@
#0400-Relax-recaptcha-version.patch
0430-remove-gitlab-markup.patch
0440-remove-puma.patch
0450-remove-bullet.patch
0460-embed-derailed-benchmarks.patch
0470-relax-bootsnap.patch
0480-embed-elasticsearch-model.patch
@ -15,6 +14,7 @@
0484-relax-asciidoctor-plantuml.patch
0485-relax-responders.patch
0486-relax-grpc-protobuf.patch
0488-relax-rdoc.patch
0500-set-webpack-root.patch
0510-remove-dev-dependencies.patch
0520-add-system-lib-path-for-webpack.patch
@ -30,6 +30,5 @@
0730-install-graphql-tag.patch
0740-use-packaged-modules.patch
0750-fix-relative-paths.patch
0760-bump-rubyzip.patch
0770-bump-node-d3.patch
0780-embed-doorkeeper.patch

View file

@ -1,5 +1,10 @@
## master (unreleased)
## 1.6.0
- Added the `perf:app` command to compare commits within the same application. (https://github.com/schneems/derailed_benchmarks/pull/157)
- Allow Rails < 7 and 1.0 <= Thor < 2 (https://github.com/schneems/derailed_benchmarks/pull/168)
## 1.5.0
- Test `perf:library` results against 99% confidence interval in addition to 95% (https://github.com/schneems/derailed_benchmarks/pull/165)

View file

@ -415,13 +415,23 @@ or point it at your local copy:
gem 'rails', path: "<path/to/your/local/copy/rails>"
```
To run your test:
To run your tests within the context of your current app/repo:
```
$ bundle exec derailed exec perf:app
```
This will automatically test the two latest commits of your library/current directory.
If you'd like to test the Rails library instead, make sure that `ENV[DERAILED_PATH_TO_LIBRARY]` is unset.
```
$ bundle exec derailed exec perf:library
```
This will automatically test the two latest commits of Rails (or the library you've specified). If you would like to compare against different SHAs you can manually specify them:
This will automatically test the two latest commits of Rails.
If you would also like to compare against different SHAs you can manually specify them:
```
$ SHAS_TO_TEST="7b4d80cb373e,13d6aa3a7b70" bundle exec derailed exec perf:library

View file

@ -28,12 +28,12 @@ Gem::Specification.new do |gem|
gem.add_dependency "benchmark-ips", "~> 2"
gem.add_dependency "rack", ">= 1"
gem.add_dependency "rake", "> 10", "< 14"
gem.add_dependency "thor", "~> 0.19"
gem.add_dependency "thor", ">= 0.19", "< 2"
gem.add_dependency "ruby-statistics", ">= 2.1"
gem.add_development_dependency "capybara", "~> 2"
gem.add_development_dependency "m"
gem.add_development_dependency "rails", "> 3", "<= 6"
gem.add_development_dependency "rails", "> 3", "<= 7"
gem.add_development_dependency "devise", "> 3", "< 6"
gem.add_development_dependency "appraisal", "2.2.0"
end

View file

@ -1,6 +1,12 @@
require_relative 'load_tasks'
namespace :perf do
desc "runs the performance test against two most recent commits of the current app"
task :app do
ENV["DERAILED_PATH_TO_LIBRARY"] = '.'
Rake::Task["perf:library"].invoke
end
desc "runs the same test against two different branches for statistical comparison"
task :library do
begin

View file

@ -1,5 +1,5 @@
# frozen_string_literal: true
module DerailedBenchmarks
VERSION = "1.5.0"
VERSION = "1.6.0"
end

View file

@ -13,6 +13,13 @@ class TasksTest < ActiveSupport::TestCase
FileUtils.remove_entry_secure(rails_app_path('tmp'))
end
def run!(cmd)
puts "Running: #{cmd}"
out = `#{cmd}`
raise "Could not run #{cmd}, output: #{out}" unless $?.success?
out
end
def rake(cmd, options = {})
assert_success = options.key?(:assert_success) ? options[:assert_success] : true
env = options[:env] || {}
@ -55,6 +62,13 @@ class TasksTest < ActiveSupport::TestCase
rake "perf:test"
end
test 'app' do
skip unless ENV['USING_RAILS_GIT']
run!("cd #{rails_app_path} && git init . && git add . && git commit -m first && git commit --allow-empty -m second")
env = { "TEST_COUNT" => 10, "DERAILED_SCRIPT_COUNT" => 2 }
puts rake "perf:app", { env: env }
end
test 'TEST_COUNT' do
result = rake "perf:test", env: { "TEST_COUNT" => 1 }
assert_match "1 derailed requests", result

View file

@ -221,6 +221,11 @@ include::basics.adoc[]
include::https://example.org/installation.adoc[]
```
To guarantee good system performance and prevent malicious documents causing
problems, GitLab enforces a **maximum limit** on the number of include directives
processed in any one document. Currently a total of 32 documents can be
included, a number that is inclusive of transitive dependencies.
### Blocks
```asciidoc

View file

@ -85,6 +85,8 @@ module API
protected: @project.protected_for?(ref))
end
authorize! :update_pipeline, pipeline
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
project: @project,
pipeline: pipeline,

View file

@ -154,7 +154,7 @@ module API
not_found! 'Commit' unless commit
present commit, with: Entities::CommitDetail, stats: params[:stats]
present commit, with: Entities::CommitDetail, stats: params[:stats], current_user: current_user
end
desc 'Get the diff for a specific commit of a project' do

View file

@ -476,8 +476,18 @@ module API
class CommitDetail < Commit
expose :stats, using: Entities::CommitStats, if: :stats
expose :status
expose :last_pipeline, using: 'API::Entities::PipelineBasic'
expose :project_id
expose :last_pipeline do |commit, options|
pipeline = commit.last_pipeline if can_read_pipeline?
::API::Entities::PipelineBasic.represent(pipeline, options)
end
private
def can_read_pipeline?
Ability.allowed?(options[:current_user], :read_pipeline, object.last_pipeline)
end
end
class CommitSignature < Grape::Entity

View file

@ -127,6 +127,7 @@ module API
get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
no_cache_headers
set_http_headers(blob_data)
send_git_blob @repo, @blob

View file

@ -256,11 +256,21 @@ module API
end
def require_gitlab_workhorse!
verify_workhorse_api!
unless env['HTTP_GITLAB_WORKHORSE'].present?
forbidden!('Request should be executed via GitLab Workhorse')
end
end
def verify_workhorse_api!
Gitlab::Workhorse.verify_api_request!(request.headers)
rescue => e
Gitlab::ErrorTracking.track_exception(e)
forbidden!
end
def require_pages_enabled!
not_found! unless user_project.pages_available?
end

View file

@ -3,6 +3,8 @@
module API
module Helpers
module HeadersHelpers
include Gitlab::NoCacheHeaders
def set_http_headers(header_data)
header_data.each do |key, value|
if value.is_a?(Enumerable)
@ -12,6 +14,12 @@ module API
header "X-Gitlab-#{key.to_s.split('_').collect(&:capitalize).join('-')}", value.to_s
end
end
def no_cache_headers
DEFAULT_GITLAB_NO_CACHE_HEADERS.each do |k, v|
header k, v
end
end
end
end
end

View file

@ -201,12 +201,14 @@ module Banzai
gather_references(nodes)
end
# Gathers the references for the given HTML nodes.
# Gathers the references for the given HTML nodes. Returns visible
# references and a list of nodes which are not visible to the user
def gather_references(nodes)
nodes = nodes_user_can_reference(current_user, nodes)
nodes = nodes_visible_to_user(current_user, nodes)
visible = nodes_visible_to_user(current_user, nodes)
not_visible = nodes - visible
referenced_by(nodes)
{ visible: referenced_by(visible), not_visible: not_visible }
end
# Returns a Hash containing the projects for a given list of HTML nodes.

View file

@ -11,6 +11,7 @@ module Gitlab
# the resulting HTML through HTML pipeline filters.
module Asciidoc
MAX_INCLUDE_DEPTH = 5
MAX_INCLUDES = 32
DEFAULT_ADOC_ATTRS = {
'showtitle' => true,
'sectanchors' => true,
@ -40,6 +41,7 @@ module Gitlab
extensions: extensions }
context[:pipeline] = :ascii_doc
context[:max_includes] = [MAX_INCLUDES, context[:max_includes]].compact.min
plantuml_setup

View file

@ -13,7 +13,9 @@ module Gitlab
super(logger: Gitlab::AppLogger)
@context = context
@repository = context[:project].try(:repository)
@repository = context[:repository] || context[:project].try(:repository)
@max_includes = context[:max_includes].to_i
@included = []
# Note: Asciidoctor calls #freeze on extensions, so we can't set new
# instance variables after initialization.
@ -28,8 +30,11 @@ module Gitlab
def include_allowed?(target, reader)
doc = reader.document
return false if doc.attributes.fetch('max-include-depth').to_i < 1
max_include_depth = doc.attributes.fetch('max-include-depth').to_i
return false if max_include_depth < 1
return false if target_uri?(target)
return false if included.size >= max_includes
true
end
@ -62,7 +67,7 @@ module Gitlab
private
attr_accessor :context, :repository, :cache
attr_reader :context, :repository, :cache, :max_includes, :included
# Gets a Blob at a path for a specific revision.
# This method will check that the Blob exists and contains readable text.
@ -77,6 +82,8 @@ module Gitlab
raise 'Blob not found' unless blob
raise 'File is not readable' unless blob.readable_text?
included << filename
blob
end

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# rubocop:disable Style/Documentation
class RecalculateProjectAuthorizations
def perform(user_ids)
user_ids.each do |user_id|
user = User.find_by(id: user_id)
next unless user
service = Users::RefreshAuthorizedProjectsService.new(
user,
incorrect_auth_found_callback:
->(project_id, access_level) do
logger.info(message: 'Removing ProjectAuthorizations',
user_id: user.id,
project_id: project_id,
access_level: access_level)
end,
missing_auth_found_callback:
->(project_id, access_level) do
logger.info(message: 'Creating ProjectAuthorizations',
user_id: user.id,
project_id: project_id,
access_level: access_level)
end
)
service.execute
end
end
private
def logger
@logger ||= Gitlab::BackgroundMigration::Logger.build
end
end
end
end

View file

@ -0,0 +1,56 @@
# frozen_string_literal: true
module Gitlab
module Git
class CrossRepoComparer
attr_reader :source_repo, :target_repo
def initialize(source_repo, target_repo)
@source_repo = source_repo
@target_repo = target_repo
end
def compare(source_ref, target_ref, straight:)
ensuring_ref_in_source(target_ref) do |target_commit_id|
Gitlab::Git::Compare.new(
source_repo,
target_commit_id,
source_ref,
straight: straight
)
end
end
private
def ensuring_ref_in_source(ref, &blk)
return yield ref if source_repo == target_repo
# If the commit doesn't exist in the target, there's nothing we can do
commit_id = target_repo.commit(ref)&.sha
return unless commit_id
# The commit pointed to by ref may exist in the source even when they
# are different repositories. This is particularly true of close forks,
# but may also be the case if a temporary ref for this comparison has
# already been created in the past, and the result hasn't been GC'd yet.
return yield commit_id if source_repo.commit(commit_id)
# Worst case: the commit is not in the source repo so we need to fetch
# it. Use a temporary ref and clean up afterwards
with_commit_in_source_tmp(commit_id, &blk)
end
# Fetch the ref into source_repo from target_repo, using a temporary ref
# name that will be deleted once the method completes. This is a no-op if
# fetching the source branch fails
def with_commit_in_source_tmp(commit_id, &blk)
tmp_ref = "refs/tmp/#{SecureRandom.hex}"
yield commit_id if source_repo.fetch_source_branch!(target_repo, commit_id, tmp_ref)
ensure
source_repo.delete_refs(tmp_ref) # best-effort
end
end
end
end

View file

@ -746,29 +746,9 @@ module Gitlab
end
def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
reachable_ref =
if source_repository == self
source_branch_name
else
# If a tmp ref was created before for a separate repo comparison (forks),
# we're able to short-circuit the tmp ref re-creation:
# 1. Take the SHA from the source repo
# 2. Read that in the current "target" repo
# 3. If that SHA is still known (readable), it means GC hasn't
# cleaned it up yet, so we can use it instead re-writing the tmp ref.
source_commit_id = source_repository.commit(source_branch_name)&.sha
commit(source_commit_id)&.sha if source_commit_id
end
return compare(target_branch_name, reachable_ref, straight: straight) if reachable_ref
tmp_ref = "refs/tmp/#{SecureRandom.hex}"
return unless fetch_source_branch!(source_repository, source_branch_name, tmp_ref)
compare(target_branch_name, tmp_ref, straight: straight)
ensure
delete_refs(tmp_ref) if tmp_ref
CrossRepoComparer
.new(source_repository, self)
.compare(source_branch_name, target_branch_name, straight: straight)
end
def write_ref(ref_path, ref, old_ref: nil)
@ -1045,13 +1025,6 @@ module Gitlab
private
def compare(base_ref, head_ref, straight:)
Gitlab::Git::Compare.new(self,
base_ref,
head_ref,
straight: straight)
end
def empty_diff_stats
Gitlab::Git::DiffStatsCollection.new([])
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
module Gitlab
module NoCacheHeaders
DEFAULT_GITLAB_NO_CACHE_HEADERS = {
'Cache-Control' => "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store, no-cache",
'Pragma' => 'no-cache', # HTTP 1.0 compatibility
'Expires' => 'Fri, 01 Jan 1990 00:00:00 GMT'
}.freeze
def no_cache_headers
raise "#no_cache_headers is not implemented for this object"
end
end
end

View file

@ -68,12 +68,9 @@ module Gitlab
.select([namespaces[:id], members[:access_level]])
.except(:order)
if Feature.enabled?(:share_group_with_group)
# Namespaces shared with any of the group
cte << Group.select([namespaces[:id], 'group_group_links.group_access AS access_level'])
.joins(join_group_group_links)
.joins(join_members_on_group_group_links)
end
cte << Group.select([namespaces[:id], 'group_group_links.group_access AS access_level'])
.joins(join_group_group_links)
.joins(join_members_on_group_group_links)
# Sub groups of any groups the user is a member of.
cte << Group.select([
@ -114,6 +111,8 @@ module Gitlab
members = Member.arel_table
cond = group_group_links[:shared_with_group_id].eq(members[:source_id])
.and(members[:source_type].eq('Namespace'))
.and(members[:requested_at].eq(nil))
.and(members[:user_id].eq(user.id))
Arel::Nodes::InnerJoin.new(members, Arel::Nodes::On.new(cond))
end

View file

@ -6,11 +6,16 @@ module Gitlab
REFERABLES = %i(user issue label milestone mentioned_user mentioned_group mentioned_project
merge_request snippet commit commit_range directly_addressed_user epic).freeze
attr_accessor :project, :current_user, :author
# This counter is increased by a number of references filtered out by
# banzai reference exctractor. Note that this counter is stateful and
# not idempotent and is increased whenever you call `references`.
attr_reader :stateful_not_visible_counter
def initialize(project, current_user = nil)
@project = project
@current_user = current_user
@references = {}
@stateful_not_visible_counter = 0
super()
end
@ -20,11 +25,15 @@ module Gitlab
end
def references(type)
super(type, project, current_user)
refs = super(type, project, current_user)
@stateful_not_visible_counter += refs[:not_visible].count
refs[:visible]
end
def reset_memoized_values
@references = {}
@stateful_not_visible_counter = 0
super()
end

View file

@ -93,6 +93,7 @@
"jszip": "^3.1.3",
"jszip-utils": "^0.0.2",
"katex": "^0.10.0",
"lodash": "^4.17.15",
"marked": "^0.3.12",
"mermaid": "^8.4.2",
"monaco-editor": "^0.15.6",

View file

@ -59,5 +59,48 @@ module QA
a_hash_including(message: '202 Accepted')
)
end
describe 'raw file access' do
let(:svg_file) do
<<-SVG
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert("surprise");
</script>
</svg>
SVG
end
it 'sets no-cache headers as expected' do
create_project_request = Runtime::API::Request.new(@api_client, '/projects')
post create_project_request.url, path: project_name, name: project_name
create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg")
post create_file_request.url, branch: 'master', content: svg_file, commit_message: 'Add test.svg'
get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: 'master')
3.times do
response = get get_file_request.url
# Subsequent responses aren't cached, so headers should match from
# request to request, especially a 200 response rather than a 304
# (indicating a cached response.) Further, :content_disposition
# should include `attachment` for all responses.
#
expect(response.headers[:cache_control]).to include("no-store")
expect(response.headers[:cache_control]).to include("no-cache")
expect(response.headers[:pragma]).to eq("no-cache")
expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
expect(response.headers[:content_disposition]).to include("attachment")
expect(response.headers[:content_disposition]).not_to include("inline")
expect(response.headers[:content_type]).to include("image/svg+xml")
end
end
end
end
end

View file

@ -23,6 +23,47 @@ describe DashboardController do
end
end
describe "GET activity as JSON" do
render_views
let(:user) { create(:user) }
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
before do
create(:event, :created, project: project, target: create(:issue))
sign_in(user)
request.cookies[:event_filter] = 'all'
end
context 'when user has permission to see the event' do
before do
project.add_developer(user)
end
it 'returns count' do
get :activity, params: { format: :json }
expect(json_response['count']).to eq(1)
end
end
context 'when user has no permission to see the event' do
it 'filters out invisible event' do
get :activity, params: { format: :json }
expect(json_response['html']).to include(_('No activities found'))
end
it 'filters out invisible event when calculating the count' do
get :activity, params: { format: :json }
expect(json_response['count']).to eq(0)
end
end
end
it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first
it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics

View file

@ -47,7 +47,7 @@ describe GroupsController do
it 'assigns events for all the projects in the group', :sidekiq_might_not_need_inline do
subject
expect(assigns(:events)).to contain_exactly(event)
expect(assigns(:events).map(&:id)).to contain_exactly(event.id)
end
end
end
@ -119,12 +119,12 @@ describe GroupsController do
describe 'GET #activity' do
render_views
before do
sign_in(user)
project
end
context 'as json' do
before do
sign_in(user)
project
end
it 'includes events from all projects in group and subgroups', :sidekiq_might_not_need_inline do
2.times do
project = create(:project, group: group)
@ -141,6 +141,31 @@ describe GroupsController do
expect(assigns(:projects).limit_value).to be_nil
end
end
context 'when user has no permission to see the event' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:project_with_restricted_access) do
create(:project, :public, issues_access_level: ProjectFeature::PRIVATE, group: group)
end
before do
create(:event, project: project)
create(:event, :created, project: project_with_restricted_access, target: create(:issue))
group.add_guest(user)
sign_in(user)
end
it 'filters out invisible event' do
get :activity, params: { id: group.to_param }, format: :json
expect(json_response['count']).to eq(1)
end
end
end
describe 'POST #create' do

View file

@ -64,6 +64,46 @@ describe ProjectsController do
end
end
describe "GET #activity as JSON" do
render_views
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
before do
create(:event, :created, project: project, target: create(:issue))
sign_in(user)
request.cookies[:event_filter] = 'all'
end
context 'when user has permission to see the event' do
before do
project.add_developer(user)
end
it 'returns count' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['count']).to eq(1)
end
end
context 'when user has no permission to see the event' do
it 'filters out invisible event' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['html']).to eq("\n")
end
it 'filters out invisible event when calculating the count' do
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
expect(json_response['count']).to eq(0)
end
end
end
describe "GET show" do
context "user not project member" do
before do

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :project_authorization do
user
project
access_level { Gitlab::Access::REPORTER }
end
end

View file

@ -935,14 +935,14 @@ describe ProjectsHelper do
helper.instance_variable_set(:@project, project)
end
subject { helper.grafana_integration_token }
subject { helper.grafana_integration_masked_token }
it { is_expected.to eq(nil) }
context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) }
it { is_expected.to eq(grafana_integration.token) }
it { is_expected.to eq(grafana_integration.masked_token) }
end
end

View file

@ -1,5 +1,10 @@
import bp from '~/breakpoints';
import { isMobile, getTopFrequentItems, updateExistingFrequentItem } from '~/frequent_items/utils';
import {
isMobile,
getTopFrequentItems,
updateExistingFrequentItem,
sanitizeItem,
} from '~/frequent_items/utils';
import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants';
import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data';
@ -86,4 +91,16 @@ describe('Frequent Items utils spec', () => {
expect(result.frequency).toBe(mockedProject.frequency);
});
});
describe('sanitizeItem', () => {
it('strips HTML tags for name and namespace', () => {
const input = {
name: '<br><b>test</b>',
namespace: '<br>test',
id: 1,
};
expect(sanitizeItem(input)).toEqual({ name: 'test', namespace: 'test', id: 1 });
});
});
});

View file

@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
it 'returns empty array' do
link['data-group'] = project.group.id.to_s
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
end
it 'returns groups' do
expect(subject.gather_references([link])).to eq([group])
expect_gathered_references(subject.gather_references([link]), [group], 0)
end
end
@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
it 'returns an empty Array' do
link['data-group'] = 'test-non-existing'
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
end

View file

@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
it 'returns empty Array' do
link['data-project'] = project.id.to_s
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
end
it 'returns an Array of referenced projects' do
expect(subject.gather_references([link])).to eq([project])
expect_gathered_references(subject.gather_references([link]), [project], 0)
end
end
@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
it 'returns an empty Array' do
link['data-project'] = 'inexisting-project-id'
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
end

View file

@ -22,7 +22,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
end
it 'returns empty list of users' do
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 0)
end
end
end
@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
end
it 'returns empty list of users' do
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 0)
end
end
end
@ -44,7 +44,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
it 'returns an Array of users' do
link['data-user'] = user.id.to_s
expect(subject.referenced_by([link])).to eq([user])
expect_gathered_references(subject.gather_references([link]), [user], 0)
end
end
end

View file

@ -17,7 +17,7 @@ describe Banzai::ReferenceParser::ProjectParser do
it 'returns an Array of projects' do
link['data-project'] = project.id.to_s
expect(subject.gather_references([link])).to eq([project])
expect_gathered_references(subject.gather_references([link]), [project], 0)
end
end
@ -25,7 +25,7 @@ describe Banzai::ReferenceParser::ProjectParser do
it 'returns an empty Array' do
link['data-project'] = ''
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
end
@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::ProjectParser do
link['data-project'] = private_project.id.to_s
expect(subject.gather_references([link])).to eq([])
expect_gathered_references(subject.gather_references([link]), [], 1)
end
it 'returns an Array when authorized' do
@ -43,7 +43,7 @@ describe Banzai::ReferenceParser::ProjectParser do
link['data-project'] = private_project.id.to_s
expect(subject.gather_references([link])).to eq([private_project])
expect_gathered_references(subject.gather_references([link]), [private_project], 0)
end
end
end

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'spec_helper'
require 'nokogiri'
describe Gitlab::Asciidoc::IncludeProcessor do
let_it_be(:project) { create(:project, :repository) }
let(:processor_context) do
{
project: project,
max_includes: max_includes,
ref: ref
}
end
let(:ref) { project.repository.root_ref }
let(:max_includes) { 10 }
let(:reader) { Asciidoctor::PreprocessorReader.new(document, lines, 'file.adoc') }
let(:document) { Asciidoctor::Document.new(lines) }
subject(:processor) { described_class.new(processor_context) }
let(:a_blob) { double(:Blob, readable_text?: true, data: a_data) }
let(:a_data) { StringIO.new('include::b.adoc[]') }
let(:lines) { [':max-include-depth: 1000'] + Array.new(10, 'include::a.adoc[]') }
before do
allow(project.repository).to receive(:blob_at).with(ref, 'a.adoc').and_return(a_blob)
end
describe '#include_allowed?' do
it 'allows the first include' do
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy
end
it 'disallows the Nth + 1 include' do
max_includes.times { processor.send(:read_blob, ref, 'a.adoc') }
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_falsey
end
end
end

View file

@ -425,6 +425,24 @@ module Gitlab
create_file(current_file, "= AsciiDoc\n")
end
def many_includes(target)
Array.new(10, "include::#{target}[]").join("\n")
end
context 'cyclic imports' do
before do
create_file('doc/api/a.adoc', many_includes('b.adoc'))
create_file('doc/api/b.adoc', many_includes('a.adoc'))
end
let(:include_path) { 'a.adoc' }
let(:requested_path) { 'doc/api/README.md' }
it 'completes successfully' do
is_expected.to include('<p>Include this:</p>')
end
end
context 'with path to non-existing file' do
let(:include_path) { 'not-exists.adoc' }

View file

@ -0,0 +1,243 @@
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizations, :migration, schema: 20200204113223 do
let(:users_table) { table(:users) }
let(:namespaces_table) { table(:namespaces) }
let(:projects_table) { table(:projects) }
let(:project_authorizations_table) { table(:project_authorizations) }
let(:members_table) { table(:members) }
let(:group_group_links) { table(:group_group_links) }
let(:project_group_links) { table(:project_group_links) }
let(:user) { users_table.create!(id: 1, email: 'user@example.com', projects_limit: 10) }
let(:group) { namespaces_table.create!(type: 'Group', name: 'group', path: 'group') }
subject { described_class.new.perform([user.id]) }
context 'missing authorization' do
context 'personal project' do
before do
user_namespace = namespaces_table.create!(owner_id: user.id, name: 'User', path: 'user')
projects_table.create!(id: 1,
name: 'personal-project',
path: 'personal-project',
visibility_level: 0,
namespace_id: user_namespace.id)
end
it 'creates correct authorization' do
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
expect(project_authorizations_table.all).to(
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 40)]))
end
end
context 'group membership' do
before do
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: group.id)
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 20, notification_level: 3)
end
it 'creates correct authorization' do
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
expect(project_authorizations_table.all).to(
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
end
end
context 'inherited group membership' do
before do
sub_group = namespaces_table.create!(type: 'Group', name: 'subgroup',
path: 'subgroup', parent_id: group.id)
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: sub_group.id)
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 20, notification_level: 3)
end
it 'creates correct authorization' do
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
expect(project_authorizations_table.all).to(
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
end
end
context 'project membership' do
before do
project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: group.id)
members_table.create!(user_id: user.id, source_id: project.id, source_type: 'Project',
type: 'ProjectMember', access_level: 20, notification_level: 3)
end
it 'creates correct authorization' do
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
expect(project_authorizations_table.all).to(
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
end
end
context 'shared group' do
before do
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 30, notification_level: 3)
shared_group = namespaces_table.create!(type: 'Group', name: 'shared group',
path: 'shared-group')
projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0,
namespace_id: shared_group.id)
group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id,
group_access: 20)
end
it 'creates correct authorization' do
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
expect(project_authorizations_table.all).to(
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
end
end
context 'shared project' do
before do
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 30, notification_level: 3)
another_group = namespaces_table.create!(type: 'Group', name: 'another group', path: 'another-group')
shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project',
visibility_level: 0, namespace_id: another_group.id)
project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20)
end
it 'creates correct authorization' do
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
expect(project_authorizations_table.all).to(
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
end
end
end
context 'unapproved access requests' do
context 'group membership' do
before do
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: group.id)
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 20, requested_at: Time.now, notification_level: 3)
end
it 'does not create authorization' do
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
end
end
context 'inherited group membership' do
before do
sub_group = namespaces_table.create!(type: 'Group', name: 'subgroup', path: 'subgroup',
parent_id: group.id)
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: sub_group.id)
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 20, requested_at: Time.now, notification_level: 3)
end
it 'does not create authorization' do
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
end
end
context 'project membership' do
before do
project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: group.id)
members_table.create!(user_id: user.id, source_id: project.id, source_type: 'Project',
type: 'ProjectMember', access_level: 20, requested_at: Time.now, notification_level: 3)
end
it 'does not create authorization' do
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
end
end
context 'shared group' do
before do
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 30, requested_at: Time.now, notification_level: 3)
shared_group = namespaces_table.create!(type: 'Group', name: 'shared group',
path: 'shared-group')
projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0,
namespace_id: shared_group.id)
group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id,
group_access: 20)
end
it 'does not create authorization' do
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
end
end
context 'shared project' do
before do
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 30, requested_at: Time.now, notification_level: 3)
another_group = namespaces_table.create!(type: 'Group', name: 'another group', path: 'another-group')
shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project',
visibility_level: 0, namespace_id: another_group.id)
project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20)
end
it 'does not create authorization' do
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
end
end
end
context 'incorrect authorization' do
before do
project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: group.id)
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
type: 'GroupMember', access_level: 30, notification_level: 3)
project_authorizations_table.create!(user_id: user.id, project_id: project.id,
access_level: 10)
end
it 'fixes authorization' do
expect { subject }.not_to change { project_authorizations_table.count }.from(1)
expect(project_authorizations_table.all).to(
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 30)]))
end
end
context 'unwanted authorization' do
before do
project = projects_table.create!(name: 'group-project', path: 'group-project',
visibility_level: 0, namespace_id: group.id)
project_authorizations_table.create!(user_id: user.id, project_id: project.id,
access_level: 10)
end
it 'deletes authorization' do
expect { subject }.to change { project_authorizations_table.count }.from(1).to(0)
end
end
context 'deleted user' do
let(:nonexistent_user_id) { User.maximum(:id).to_i + 999 }
it 'does not fail' do
expect { described_class.new.perform([nonexistent_user_id]) }.not_to raise_error
end
end
end

View file

@ -0,0 +1,117 @@
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Git::CrossRepoComparer do
let(:source_project) { create(:project, :repository) }
let(:target_project) { create(:project, :repository) }
let(:source_repo) { source_project.repository.raw_repository }
let(:target_repo) { target_project.repository.raw_repository }
let(:source_branch) { 'feature' }
let(:target_branch) { 'master' }
let(:straight) { false }
let(:source_commit) { source_repo.commit(source_branch) }
let(:target_commit) { source_repo.commit(target_branch) }
subject(:result) { described_class.new(source_repo, target_repo).compare(source_branch, target_branch, straight: straight) }
describe '#compare' do
context 'within a single repository' do
let(:target_project) { source_project }
context 'a non-straight comparison' do
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect_compare(result, from: source_commit, to: target_commit)
expect(result.straight).to eq(false)
end
end
context 'a straight comparison' do
let(:straight) { true }
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect_compare(result, from: source_commit, to: target_commit)
expect(result.straight).to eq(true)
end
end
end
context 'across two repositories' do
context 'target ref exists in source repo' do
it 'compares without fetching from another repo' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
expect_compare(result, from: source_commit, to: target_commit)
end
end
context 'target ref does not exist in source repo' do
it 'compares in the source repo by fetching from the target to a temporary ref' do
new_commit_id = create_commit(target_project.owner, target_repo, target_branch)
new_commit = target_repo.commit(new_commit_id)
# This is how the temporary ref is generated
expect(SecureRandom).to receive(:hex).at_least(:once).and_return('foo')
expect(source_repo)
.to receive(:fetch_source_branch!)
.with(target_repo, new_commit_id, 'refs/tmp/foo')
.and_call_original
expect(source_repo).to receive(:delete_refs).with('refs/tmp/foo').and_call_original
expect_compare(result, from: source_commit, to: new_commit)
end
end
context 'source ref does not exist in source repo' do
let(:source_branch) { 'does-not-exist' }
it 'returns an empty comparison' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
expect(result).to be_a(::Gitlab::Git::Compare)
expect(result.commits.size).to eq(0)
end
end
context 'target ref does not exist in target repo' do
let(:target_branch) { 'does-not-exist' }
it 'returns nil' do
expect(source_repo).not_to receive(:fetch_source_branch!)
expect(source_repo).not_to receive(:delete_refs)
is_expected.to be_nil
end
end
end
end
def expect_compare(of, from:, to:)
expect(of).to be_a(::Gitlab::Git::Compare)
expect(from).to be_a(::Gitlab::Git::Commit)
expect(to).to be_a(::Gitlab::Git::Commit)
expect(of.commits).not_to be_empty
expect(of.head).to eq(from)
expect(of.base).to eq(to)
end
def create_commit(user, repo, branch)
action = { action: :create, file_path: '/FILE', content: 'content' }
result = repo.multi_action(user, branch_name: branch, message: 'Commit', actions: [action])
result.newrev
end
end

View file

@ -1962,66 +1962,15 @@ describe Gitlab::Git::Repository, :seed_helper do
end
describe '#compare_source_branch' do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') }
it 'delegates to Gitlab::Git::CrossRepoComparer' do
expect_next_instance_of(::Gitlab::Git::CrossRepoComparer) do |instance|
expect(instance.source_repo).to eq(:source_repository)
expect(instance.target_repo).to eq(repository)
context 'within same repository' do
it 'does not create a temp ref' do
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', repository, 'feature', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
expect(instance).to receive(:compare).with('feature', 'master', straight: :straight)
end
it 'returns empty commits when source ref does not exist' do
compare = repository.compare_source_branch('master', repository, 'non-existent-branch', straight: false)
expect(compare.commits).to be_empty
end
end
context 'with different repositories' do
context 'when ref is known by source repo, but not by target' do
before do
mutable_repository.write_ref('another-branch', 'feature')
end
it 'creates temp ref' do
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
end
context 'when ref is known by source and target repos' do
before do
mutable_repository.write_ref('another-branch', 'feature')
repository.write_ref('another-branch', 'feature')
end
it 'does not create a temp ref' do
expect(repository).not_to receive(:fetch_source_branch!)
expect(repository).not_to receive(:delete_refs)
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
expect(compare).to be_a(Gitlab::Git::Compare)
expect(compare.commits.count).to be > 0
end
end
context 'when ref is unknown by source repo' do
it 'returns nil when source ref does not exist' do
expect(repository).to receive(:fetch_source_branch!).and_call_original
expect(repository).to receive(:delete_refs).and_call_original
compare = repository.compare_source_branch('master', mutable_repository, 'non-existent-branch', straight: false)
expect(compare).to be_nil
end
end
repository.compare_source_branch('master', :source_repository, 'feature', straight: :straight)
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::NoCacheHeaders do
class NoCacheTester
include Gitlab::NoCacheHeaders
end
describe "#no_cache_headers" do
subject { NoCacheTester.new }
it "raises a RuntimeError" do
expect { subject.no_cache_headers }.to raise_error(RuntimeError)
end
end
end

View file

@ -97,87 +97,68 @@ describe Gitlab::ProjectAuthorizations do
create(:group_group_link, shared_group: shared_group, shared_with_group: group)
end
context 'when feature flag share_group_with_group is enabled' do
before do
stub_feature_flags(share_group_with_group: true)
end
context 'group user' do
let(:user) { group_user }
context 'group user' do
let(:user) { group_user }
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER)
expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER)
end
end
context 'parent group user' do
let(:user) { parent_group_user }
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
end
context 'child group user' do
let(:user) { child_group_user }
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER)
expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER)
end
end
context 'when feature flag share_group_with_group is disabled' do
before do
stub_feature_flags(share_group_with_group: false)
context 'parent group user' do
let(:user) { parent_group_user }
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
end
context 'group user' do
let(:user) { group_user }
context 'child group user' do
let(:user) { child_group_user }
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
end
context 'parent group user' do
let(:user) { parent_group_user }
context 'user without accepted access request' do
let!(:user) { create(:user) }
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
it 'does not have access to group and its projects' do
create(:group_member, :developer, :access_request, user: user, group: group)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
end
context 'child group user' do
let(:user) { child_group_user }
context 'unrelated project owner' do
let(:common_id) { [Project.maximum(:id).to_i, Namespace.maximum(:id).to_i].max + 999 }
let!(:group) { create(:group, id: common_id) }
let!(:unrelated_project) { create(:project, id: common_id) }
let(:user) { unrelated_project.owner }
it 'creates proper authorizations' do
mapping = map_access_levels(authorizations)
it 'does not have access to group and its projects' do
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to be_nil
expect(mapping[project_child.id]).to be_nil
end
end
end

View file

@ -3,7 +3,7 @@
require 'spec_helper'
describe Gitlab::ReferenceExtractor do
let(:project) { create(:project) }
let_it_be(:project) { create(:project) }
before do
project.add_developer(project.creator)
@ -293,4 +293,34 @@ describe Gitlab::ReferenceExtractor do
end
end
end
describe '#references' do
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
let(:text) { "Ref. #{issue.to_reference}" }
subject { described_class.new(project, user) }
before do
subject.analyze(text)
end
context 'when references are visible' do
before do
project.add_developer(user)
end
it 'returns visible references of given type' do
expect(subject.references(:issue)).to eq([issue])
end
it 'does not increase stateful_not_visible_counter' do
expect { subject.references(:issue) }.not_to change { subject.stateful_not_visible_counter }
end
end
it 'increases stateful_not_visible_counter' do
expect { subject.references(:issue) }.to change { subject.stateful_not_visible_counter }.by(1)
end
end
end

View file

@ -0,0 +1,57 @@
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200204113223_schedule_recalculate_project_authorizations.rb')
describe ScheduleRecalculateProjectAuthorizations, :migration, :sidekiq do
let(:users_table) { table(:users) }
let(:namespaces_table) { table(:namespaces) }
let(:projects_table) { table(:projects) }
let(:project_authorizations_table) { table(:project_authorizations) }
let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 1) }
let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 1) }
let(:group) { namespaces_table.create!(id: 1, type: 'Group', name: 'group', path: 'group') }
let(:project) do
projects_table.create!(id: 1, name: 'project', path: 'project',
visibility_level: 0, namespace_id: group.id)
end
before do
stub_const("#{described_class}::BATCH_SIZE", 1)
project_authorizations_table.create!(user_id: user1.id, project_id: project.id, access_level: 30)
project_authorizations_table.create!(user_id: user2.id, project_id: project.id, access_level: 30)
end
it 'schedules background migration' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
expect(described_class::MIGRATION).to be_scheduled_migration([user1.id])
expect(described_class::MIGRATION).to be_scheduled_migration([user2.id])
end
end
end
it 'ignores projects with higher id than maximum group id' do
another_user = users_table.create!(name: 'another user', email: 'another-user@example.com',
projects_limit: 1)
ignored_project = projects_table.create!(id: 2, name: 'ignored-project', path: 'ignored-project',
visibility_level: 0, namespace_id: group.id)
project_authorizations_table.create!(user_id: another_user.id, project_id: ignored_project.id,
access_level: 30)
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
expect(described_class::MIGRATION).to be_scheduled_migration([user1.id])
expect(described_class::MIGRATION).to be_scheduled_migration([user2.id])
end
end
end
end

View file

@ -19,6 +19,74 @@ describe GenericCommitStatus do
it { is_expected.not_to allow_value('javascript:alert(1)').for(:target_url) }
end
describe '#name_uniqueness_across_types' do
let(:attributes) { {} }
let(:commit_status) { described_class.new(attributes) }
let(:status_name) { 'test-job' }
subject(:errors) { commit_status.errors[:name] }
shared_examples 'it does not have uniqueness errors' do
it 'does not return errors' do
commit_status.valid?
is_expected.to be_empty
end
end
context 'without attributes' do
it_behaves_like 'it does not have uniqueness errors'
end
context 'with only a pipeline' do
let(:attributes) { { pipeline: pipeline } }
context 'without name' do
it_behaves_like 'it does not have uniqueness errors'
end
end
context 'with only a name' do
let(:attributes) { { name: status_name } }
context 'without pipeline' do
it_behaves_like 'it does not have uniqueness errors'
end
end
context 'with pipeline and name' do
let(:attributes) do
{
pipeline: pipeline,
name: status_name
}
end
context 'without other statuses' do
it_behaves_like 'it does not have uniqueness errors'
end
context 'with generic statuses' do
before do
create(:generic_commit_status, pipeline: pipeline, name: status_name)
end
it_behaves_like 'it does not have uniqueness errors'
end
context 'with ci_build statuses' do
before do
create(:ci_build, pipeline: pipeline, name: status_name)
end
it 'returns name error' do
expect(commit_status).to be_invalid
is_expected.to include('has already been taken')
end
end
end
end
describe '#context' do
subject { generic_commit_status.context }
@ -79,6 +147,12 @@ describe GenericCommitStatus do
it { is_expected.not_to be_nil }
end
describe '#stage_idx' do
subject { generic_commit_status.stage_idx }
it { is_expected.not_to be_nil }
end
end
describe '#present' do

View file

@ -9,7 +9,7 @@ describe GrafanaIntegration do
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:token) }
it { is_expected.to validate_presence_of(:encrypted_token) }
it 'disallows invalid urls for grafana_url' do
unsafe_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>}
@ -66,4 +66,24 @@ describe GrafanaIntegration do
end
end
end
describe 'attribute encryption' do
subject(:grafana_integration) { create(:grafana_integration, token: 'super-secret') }
context 'token' do
it 'encrypts original value into encrypted_token attribute' do
expect(grafana_integration.encrypted_token).not_to be_nil
end
it 'locks access to raw value in private method', :aggregate_failures do
expect { grafana_integration.token }.to raise_error(NoMethodError, /private method .token. called/)
expect(grafana_integration.send(:token)).to eql('super-secret')
end
it 'prevents overriding token value with its encrypted or masked version', :aggregate_failures do
expect { grafana_integration.update(token: grafana_integration.encrypted_token) }.not_to change { grafana_integration.reload.send(:token) }
expect { grafana_integration.update(token: grafana_integration.masked_token) }.not_to change { grafana_integration.reload.send(:token) }
end
end
end
end

View file

@ -350,12 +350,12 @@ describe Note do
end
describe "cross_reference_not_visible_for?" do
let(:private_user) { create(:user) }
let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
let(:private_issue) { create(:issue, project: private_project) }
let_it_be(:private_user) { create(:user) }
let_it_be(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
let_it_be(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let_it_be(:ext_proj) { create(:project, :public) }
let_it_be(:ext_issue) { create(:issue, project: ext_proj) }
shared_examples "checks references" do
it "returns true" do
@ -393,10 +393,24 @@ describe Note do
it_behaves_like "checks references"
end
context "when there are two references in note" do
context "when there is a reference to a label" do
let_it_be(:private_label) { create(:label, project: private_project) }
let(:note) do
create :note,
noteable: ext_issue, project: ext_proj,
note: "added label #{private_label.to_reference(ext_proj)}",
system: true
end
let!(:system_note_metadata) { create(:system_note_metadata, note: note, action: :label) }
it_behaves_like "checks references"
end
context "when there are two references in note" do
let_it_be(:ext_issue2) { create(:issue, project: ext_proj) }
let(:note) do
create :note,
noteable: ext_issue2, project: ext_proj,
note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \
"public issue #{ext_issue.to_reference(ext_proj)}",
system: true

View file

@ -141,6 +141,34 @@ describe GlobalPolicy do
it { is_expected.to be_allowed(:access_api) }
end
end
context 'inactive user' do
before do
current_user.update!(confirmed_at: nil, confirmation_sent_at: 5.days.ago)
end
context 'when within the confirmation grace period' do
before do
allow(User).to receive(:allow_unconfirmed_access_for).and_return(10.days)
end
it { is_expected.to be_allowed(:access_api) }
end
context 'when confirmation grace period is expired' do
before do
allow(User).to receive(:allow_unconfirmed_access_for).and_return(2.days)
end
it { is_expected.not_to be_allowed(:access_api) }
end
it 'when `inactive_policy_condition` feature flag is turned off' do
stub_feature_flags(inactive_policy_condition: false)
is_expected.to be_allowed(:access_api)
end
end
end
describe 'receive notifications' do
@ -202,6 +230,20 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:access_git) }
end
describe 'inactive user' do
before do
current_user.update!(confirmed_at: nil)
end
it { is_expected.not_to be_allowed(:access_git) }
it 'when `inactive_policy_condition` feature flag is turned off' do
stub_feature_flags(inactive_policy_condition: false)
is_expected.to be_allowed(:access_git)
end
end
context 'when terms are enforced' do
before do
enforce_terms
@ -298,6 +340,20 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:use_slash_commands) }
end
describe 'inactive user' do
before do
current_user.update!(confirmed_at: nil)
end
it { is_expected.not_to be_allowed(:use_slash_commands) }
it 'when `inactive_policy_condition` feature flag is turned off' do
stub_feature_flags(inactive_policy_condition: false)
is_expected.to be_allowed(:use_slash_commands)
end
end
context 'when access locked' do
before do
current_user.lock_access!

View file

@ -164,6 +164,7 @@ describe API::CommitStatuses do
expect(response).to have_gitlab_http_status(201)
expect(job.status).to eq('pending')
expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
end
end
@ -331,6 +332,29 @@ describe API::CommitStatuses do
end
end
context 'when updating a protected ref' do
before do
create(:protected_branch, project: project, name: 'master')
post api(post_url, user), params: { state: 'running', ref: 'master' }
end
context 'with user as developer' do
let(:user) { developer }
it 'does not create commit status' do
expect(response).to have_gitlab_http_status(403)
end
end
context 'with user as maintainer' do
let(:user) { create_user(:maintainer) }
it 'creates commit status' do
expect(response).to have_gitlab_http_status(201)
end
end
end
context 'when commit SHA is invalid' do
let(:sha) { 'invalid_sha' }
@ -372,6 +396,22 @@ describe API::CommitStatuses do
.to include 'is blocked: Only allowed schemes are http, https'
end
end
context 'when trying to update a status of a different type' do
let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
let!(:ci_build) { create(:ci_build, pipeline: pipeline, name: 'test-job') }
let(:params) { { state: 'pending', name: 'test-job' } }
before do
post api(post_url, developer), params: params
end
it 'responds with bad request status and validation errors' do
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['name'])
.to include 'has already been taken'
end
end
end
context 'reporter user' do

View file

@ -8,6 +8,7 @@ describe API::Commits do
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
let(:branch_with_dot) { project.repository.find_branch('ends-with.json') }
let(:branch_with_slash) { project.repository.find_branch('improve/awesome') }
@ -964,6 +965,56 @@ describe API::Commits do
end
end
shared_examples_for 'ref with pipeline' do
let!(:pipeline) do
project
.ci_pipelines
.create!(source: :push, ref: 'master', sha: commit.sha, protected: false)
end
it 'includes status as "created" and a last_pipeline object' do
get api(route, current_user)
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(json_response['status']).to eq('created')
expect(json_response['last_pipeline']['id']).to eq(pipeline.id)
expect(json_response['last_pipeline']['ref']).to eq(pipeline.ref)
expect(json_response['last_pipeline']['sha']).to eq(pipeline.sha)
expect(json_response['last_pipeline']['status']).to eq(pipeline.status)
end
context 'when pipeline succeeds' do
before do
pipeline.update!(status: 'success')
end
it 'includes a "success" status' do
get api(route, current_user)
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(json_response['status']).to eq('success')
end
end
end
shared_examples_for 'ref with unaccessible pipeline' do
let!(:pipeline) do
project
.ci_pipelines
.create!(source: :push, ref: 'master', sha: commit.sha, protected: false)
end
it 'does not include last_pipeline' do
get api(route, current_user)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(response).to have_gitlab_http_status(200)
expect(json_response['last_pipeline']).to be_nil
end
end
context 'when stat param' do
let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}" }
@ -993,6 +1044,15 @@ describe API::Commits do
let(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref commit'
it_behaves_like 'ref with pipeline'
context 'with private builds' do
before do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
it_behaves_like 'ref with unaccessible pipeline'
end
end
context 'when unauthenticated', 'and project is private' do
@ -1006,6 +1066,17 @@ describe API::Commits do
let(:current_user) { user }
it_behaves_like 'ref commit'
it_behaves_like 'ref with pipeline'
context 'when builds are disabled' do
before do
project
.project_feature
.update!(builds_access_level: ProjectFeature::DISABLED)
end
it_behaves_like 'ref with unaccessible pipeline'
end
context 'when branch contains a dot' do
let(:commit) { project.repository.commit(branch_with_dot.name) }
@ -1041,35 +1112,53 @@ describe API::Commits do
it_behaves_like 'ref commit'
end
end
end
context 'when the ref has a pipeline' do
let!(:pipeline) { project.ci_pipelines.create(source: :push, ref: 'master', sha: commit.sha, protected: false) }
context 'when authenticated', 'as a developer' do
let(:current_user) { developer }
it 'includes a "created" status' do
get api(route, current_user)
it_behaves_like 'ref commit'
it_behaves_like 'ref with pipeline'
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(json_response['status']).to eq('created')
expect(json_response['last_pipeline']['id']).to eq(pipeline.id)
expect(json_response['last_pipeline']['ref']).to eq(pipeline.ref)
expect(json_response['last_pipeline']['sha']).to eq(pipeline.sha)
expect(json_response['last_pipeline']['status']).to eq(pipeline.status)
context 'with private builds' do
before do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
context 'when pipeline succeeds' do
before do
pipeline.update(status: 'success')
end
it_behaves_like 'ref with pipeline'
end
end
it 'includes a "success" status' do
get api(route, current_user)
context 'when authenticated', 'as a guest' do
let(:current_user) { guest }
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/commit/detail')
expect(json_response['status']).to eq('success')
end
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
let(:message) { '403 Forbidden' }
end
end
context 'when authenticated', 'as a non member' do
let(:current_user) { create(:user) }
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
let(:message) { '403 Forbidden' }
end
end
context 'when authenticated', 'as non_member and project is public' do
let(:current_user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref with pipeline'
context 'with private builds' do
before do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
it_behaves_like 'ref with unaccessible pipeline'
end
end
end

View file

@ -447,6 +447,18 @@ describe API::Files do
expect(response).to have_gitlab_http_status(200)
end
it 'sets no-cache headers' do
url = route('.gitignore') + "/raw"
expect(Gitlab::Workhorse).to receive(:send_git_blob)
get api(url, current_user), params: params
expect(response.headers["Cache-Control"]).to include("no-store")
expect(response.headers["Cache-Control"]).to include("no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
context 'when mandatory params are not given' do
it_behaves_like '400 response' do
let(:request) { get api(route("any%2Ffile"), current_user) }

View file

@ -30,26 +30,40 @@ describe 'OAuth tokens' do
end
end
context "when user is blocked" do
it "does not create an access token" do
user = create(:user)
shared_examples 'does not create an access token' do
let(:user) { create(:user) }
it { expect(response).to have_gitlab_http_status(401) }
end
context 'when user is blocked' do
before do
user.block
request_oauth_token(user)
expect(response).to have_gitlab_http_status(401)
end
include_examples 'does not create an access token'
end
context "when user is ldap_blocked" do
it "does not create an access token" do
user = create(:user)
context 'when user is ldap_blocked' do
before do
user.ldap_block
request_oauth_token(user)
expect(response).to have_gitlab_http_status(401)
end
include_examples 'does not create an access token'
end
context 'when user account is not confirmed' do
before do
user.update!(confirmed_at: nil)
request_oauth_token(user)
end
include_examples 'does not create an access token'
end
end
end

View file

@ -1509,7 +1509,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
authorize_artifacts
expect(response).to have_gitlab_http_status(500)
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'authorization token is invalid' do
@ -1639,6 +1639,18 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
context 'Is missing GitLab Workhorse token headers' do
let(:jwt_token) { JWT.encode({ 'iss' => 'invalid-header' }, Gitlab::Workhorse.secret, 'HS256') }
it 'fails to post artifacts without GitLab-Workhorse' do
expect(Gitlab::ErrorTracking).to receive(:track_exception).once
upload_artifacts(file_upload, headers_with_token)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when setting an expire date' do
let(:default_artifacts_expire_in) {}
let(:post_data) do

View file

@ -3,8 +3,8 @@
require 'spec_helper'
describe Projects::GroupLinks::DestroyService, '#execute' do
let(:group_link) { create :project_group_link }
let(:project) { group_link.project }
let(:project) { create(:project, :private) }
let!(:group_link) { create(:project_group_link, project: project) }
let(:user) { create :user }
let(:subject) { described_class.new(project, user) }
@ -15,4 +15,39 @@ describe Projects::GroupLinks::DestroyService, '#execute' do
it 'returns false if group_link is blank' do
expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
end
describe 'todos cleanup' do
context 'when project is private' do
it 'triggers todos cleanup' do
expect(TodosDestroyer::ProjectPrivateWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, project.id)
expect(project.private?).to be true
subject.execute(group_link)
end
end
context 'when project is public or internal' do
shared_examples_for 'removes confidential todos' do
it 'does not trigger todos cleanup' do
expect(TodosDestroyer::ProjectPrivateWorker).not_to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, project.id)
expect(TodosDestroyer::ConfidentialIssueWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, nil, project.id)
expect(project.private?).to be false
subject.execute(group_link)
end
end
context 'when project is public' do
let(:project) { create(:project, :public) }
it_behaves_like 'removes confidential todos'
end
context 'when project is internal' do
let(:project) { create(:project, :public) }
it_behaves_like 'removes confidential todos'
end
end
end
end

View file

@ -10,6 +10,10 @@ describe Projects::ImportExport::ExportService do
let(:service) { described_class.new(project, user) }
let!(:after_export_strategy) { Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy.new }
before do
project.add_maintainer(user)
end
it 'saves the version' do
expect(Gitlab::ImportExport::VersionSaver).to receive(:new).and_call_original
@ -133,5 +137,18 @@ describe Projects::ImportExport::ExportService do
expect(service).not_to receive(:execute_after_export_action)
end
end
context 'when user does not have admin_project permission' do
let!(:another_user) { create(:user) }
subject(:service) { described_class.new(project, another_user) }
it 'fails' do
expected_message =
"User with ID: %s does not have permission to Project %s with ID: %s." %
[another_user.id, project.name, project.id]
expect { service.execute }.to raise_error(Gitlab::ImportExport::Error).with_message(expected_message)
end
end
end
end

View file

@ -210,7 +210,7 @@ describe Projects::Operations::UpdateService do
integration = project.reload.grafana_integration
expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
expect(integration.token).to eq(expected_attrs[:token])
expect(integration.send(:token)).to eq(expected_attrs[:token])
end
end
@ -226,7 +226,7 @@ describe Projects::Operations::UpdateService do
integration = project.reload.grafana_integration
expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
expect(integration.token).to eq(expected_attrs[:token])
expect(integration.send(:token)).to eq(expected_attrs[:token])
end
context 'with all grafana attributes blank in params' do

View file

@ -5,7 +5,7 @@ require "spec_helper"
describe Projects::UpdatePagesService do
set(:project) { create(:project, :repository) }
set(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) }
set(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') }
let(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') }
let(:invalid_file) { fixture_file_upload('spec/fixtures/dk.png') }
let(:file) { fixture_file_upload("spec/fixtures/pages.zip") }
@ -242,6 +242,32 @@ describe Projects::UpdatePagesService do
end
end
context 'when file size is spoofed' do
let(:metadata) { spy('metadata') }
include_context 'pages zip with spoofed size'
before do
file = fixture_file_upload(fake_zip_path, 'pages.zip')
metafile = fixture_file_upload('spec/fixtures/pages.zip.meta')
create(:ci_job_artifact, :archive, file: file, job: build)
create(:ci_job_artifact, :metadata, file: metafile, job: build)
allow(build).to receive(:artifacts_metadata_entry)
.and_return(metadata)
allow(metadata).to receive(:total_size).and_return(100)
end
it 'raises an error' do
expect do
subject.execute
end.to raise_error(Projects::UpdatePagesService::FailedToExtractError,
'Entry public/index.html should be 1B but is larger when inflated')
expect(deploy_status).to be_script_failure
end
end
def deploy_status
GenericCommitStatus.find_by(name: 'pages:deploy')
end

View file

@ -22,6 +22,42 @@ describe Users::RefreshAuthorizedProjectsService do
service.execute
end
context 'callbacks' do
let(:callback) { double('callback') }
context 'incorrect_auth_found_callback callback' do
let(:user) { create(:user) }
let(:service) do
described_class.new(user,
incorrect_auth_found_callback: callback)
end
it 'is called' do
access_level = Gitlab::Access::DEVELOPER
create(:project_authorization, user: user, project: project, access_level: access_level)
expect(callback).to receive(:call).with(project.id, access_level).once
service.execute
end
end
context 'missing_auth_found_callback callback' do
let(:service) do
described_class.new(user,
missing_auth_found_callback: callback)
end
it 'is called' do
ProjectAuthorization.delete_all
expect(callback).to receive(:call).with(project.id, Gitlab::Access::MAINTAINER).once
service.execute
end
end
end
end
describe '#execute_without_lease' do

Some files were not shown because too many files have changed in this diff Show more