diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/api/commit_statuses.rb | 2 | ||||
-rw-r--r-- | lib/api/commits.rb | 2 | ||||
-rw-r--r-- | lib/api/entities.rb | 12 | ||||
-rw-r--r-- | lib/api/files.rb | 1 | ||||
-rw-r--r-- | lib/api/helpers.rb | 8 | ||||
-rw-r--r-- | lib/api/helpers/headers_helpers.rb | 8 | ||||
-rw-r--r-- | lib/banzai/reference_parser/base_parser.rb | 8 | ||||
-rw-r--r-- | lib/gitlab/asciidoc.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/asciidoc/include_processor.rb | 13 | ||||
-rw-r--r-- | lib/gitlab/git/cross_repo_comparer.rb | 56 | ||||
-rw-r--r-- | lib/gitlab/git/repository.rb | 33 | ||||
-rw-r--r-- | lib/gitlab/no_cache_headers.rb | 15 | ||||
-rw-r--r-- | lib/gitlab/reference_extractor.rb | 11 |
13 files changed, 132 insertions, 39 deletions
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index d108c811f4b..e0a6dc41b65 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -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, diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 63a7fdfa3ab..9dcf9b015aa 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -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 diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 32a0fb9dd60..694c48f644a 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -456,8 +456,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 diff --git a/lib/api/files.rb b/lib/api/files.rb index 0b438fb5bbc..feed22d188c 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -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 diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 49b86489a8b..4dcbd26775d 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -256,11 +256,19 @@ 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 + forbidden! + end + def require_pages_enabled! not_found! unless user_project.pages_available? end diff --git a/lib/api/helpers/headers_helpers.rb b/lib/api/helpers/headers_helpers.rb index 7553af9d156..908c57bb04e 100644 --- a/lib/api/helpers/headers_helpers.rb +++ b/lib/api/helpers/headers_helpers.rb @@ -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 diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb index 8419769085a..ffc6bdc612d 100644 --- a/lib/banzai/reference_parser/base_parser.rb +++ b/lib/banzai/reference_parser/base_parser.rb @@ -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. diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index da65caa6c9c..8d072422e17 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -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 diff --git a/lib/gitlab/asciidoc/include_processor.rb b/lib/gitlab/asciidoc/include_processor.rb index c6fbf540e9c..c3dca21b4b5 100644 --- a/lib/gitlab/asciidoc/include_processor.rb +++ b/lib/gitlab/asciidoc/include_processor.rb @@ -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 diff --git a/lib/gitlab/git/cross_repo_comparer.rb b/lib/gitlab/git/cross_repo_comparer.rb new file mode 100644 index 00000000000..3958373f7cb --- /dev/null +++ b/lib/gitlab/git/cross_repo_comparer.rb @@ -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 diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 4971a18e270..ea44a2f40c0 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -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 diff --git a/lib/gitlab/no_cache_headers.rb b/lib/gitlab/no_cache_headers.rb new file mode 100644 index 00000000000..f80ca2c1369 --- /dev/null +++ b/lib/gitlab/no_cache_headers.rb @@ -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 diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index ea2b03b42c1..48276465efd 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -6,11 +6,16 @@ module Gitlab REFERABLES = %i(user issue label milestone 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 |