summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/repositories.rb4
-rw-r--r--lib/banzai/filter/inline_observability_filter.rb16
-rw-r--r--lib/extracts_ref.rb14
-rw-r--r--lib/gitlab/background_migration/nullify_last_error_from_project_mirror_data.rb17
-rw-r--r--lib/gitlab/regex.rb58
-rw-r--r--lib/gitlab/unicode.rb6
-rw-r--r--lib/gitlab/untrusted_regexp.rb11
-rw-r--r--lib/gitlab/url_sanitizer.rb90
-rw-r--r--lib/rouge/formatters/html_gitlab.rb9
9 files changed, 160 insertions, 65 deletions
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 70535496b12..6f8d34ea387 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -203,6 +203,10 @@ module API
render_api_error!("Target project id:#{params[:from_project_id]} is not a fork of project id:#{params[:id]}", 400)
end
+ unless can?(current_user, :read_code, target_project)
+ forbidden!("You don't have access to this fork's parent project")
+ end
+
cache_key = compare_cache_key(current_user, user_project, target_project, declared_params)
cache_action(cache_key, expires_in: 1.minute) do
diff --git a/lib/banzai/filter/inline_observability_filter.rb b/lib/banzai/filter/inline_observability_filter.rb
index 334c04f2b59..50d4aac70cc 100644
--- a/lib/banzai/filter/inline_observability_filter.rb
+++ b/lib/banzai/filter/inline_observability_filter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'uri'
+
module Banzai
module Filter
class InlineObservabilityFilter < ::Banzai::Filter::InlineEmbedsFilter
@@ -15,7 +17,8 @@ module Banzai
doc.document.create_element(
'div',
class: 'js-render-observability',
- 'data-frame-url': url
+ 'data-frame-url': url,
+ 'data-observability-url': Gitlab::Observability.observability_url
)
end
@@ -28,8 +31,15 @@ module Banzai
# obtained from the target link
def element_to_embed(node)
url = node['href']
-
- create_element(url)
+ uri = URI.parse(url)
+ observability_uri = URI.parse(Gitlab::Observability.observability_url)
+
+ if uri.scheme == observability_uri.scheme &&
+ uri.port == observability_uri.port &&
+ uri.host.casecmp?(observability_uri.host) &&
+ uri.path.downcase.exclude?("auth/start")
+ create_element(url)
+ end
end
private
diff --git a/lib/extracts_ref.rb b/lib/extracts_ref.rb
index dba1aad639c..49c9772f760 100644
--- a/lib/extracts_ref.rb
+++ b/lib/extracts_ref.rb
@@ -5,7 +5,8 @@
# Can be extended for different types of repository object, e.g. Project or Snippet
module ExtractsRef
InvalidPathError = Class.new(StandardError)
-
+ BRANCH_REF_TYPE = 'heads'
+ TAG_REF_TYPE = 'tags'
# Given a string containing both a Git tree-ish, such as a branch or tag, and
# a filesystem path joined by forward slashes, attempts to separate the two.
#
@@ -91,7 +92,7 @@ module ExtractsRef
def ref_type
return unless params[:ref_type].present?
- params[:ref_type] == 'tags' ? 'tags' : 'heads'
+ params[:ref_type] == TAG_REF_TYPE ? TAG_REF_TYPE : BRANCH_REF_TYPE
end
private
@@ -154,4 +155,13 @@ module ExtractsRef
def repository_container
raise NotImplementedError
end
+
+ def ambiguous_ref?(project, ref)
+ return true if project.repository.ambiguous_ref?(ref)
+
+ return false unless ref&.starts_with?('refs/')
+
+ unprefixed_ref = ref.sub(%r{^refs/(heads|tags)/}, '')
+ project.repository.commit(unprefixed_ref).present?
+ end
end
diff --git a/lib/gitlab/background_migration/nullify_last_error_from_project_mirror_data.rb b/lib/gitlab/background_migration/nullify_last_error_from_project_mirror_data.rb
new file mode 100644
index 00000000000..6ea5c17353b
--- /dev/null
+++ b/lib/gitlab/background_migration/nullify_last_error_from_project_mirror_data.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Nullifies last_error value from project_mirror_data table as they
+ # potentially included sensitive data.
+ # https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/3041
+ class NullifyLastErrorFromProjectMirrorData < BatchedMigrationJob
+ feature_category :source_code_management
+ operation_name :update_all
+
+ def perform
+ each_sub_batch { |rel| rel.update_all(last_error: nil) }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 93d23add5eb..943218a9972 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -448,6 +448,17 @@ module Gitlab
)
}mx.freeze
+ # Code blocks:
+ # ```
+ # Anything, including `>>>` blocks which are ignored by this filter
+ # ```
+ MARKDOWN_CODE_BLOCK_REGEX_UNTRUSTED =
+ '(?P<code>' \
+ '^```\n' \
+ '(?:\n|.)*?' \
+ '\n```\ *$' \
+ ')'.freeze
+
MARKDOWN_HTML_BLOCK_REGEX = %r{
(?<html>
# HTML block:
@@ -461,27 +472,19 @@ module Gitlab
)
}mx.freeze
- MARKDOWN_HTML_COMMENT_LINE_REGEX = %r{
- (?<html_comment_line>
- # HTML comment line:
- # <!-- some commented text -->
-
- ^<!--\ .*?\ -->\ *$
- )
- }mx.freeze
-
- MARKDOWN_HTML_COMMENT_BLOCK_REGEX = %r{
- (?<html_comment_block>
- # HTML comment block:
- # <!-- some commented text
- # additional text
- # -->
+ # HTML comment line:
+ # <!-- some commented text -->
+ MARKDOWN_HTML_COMMENT_LINE_REGEX_UNTRUSTED =
+ '(?P<html_comment_line>' \
+ '^<!--\ .*?\ -->\ *$' \
+ ')'.freeze
- ^<!--.*\n
- .+?
- \n-->\ *$
- )
- }mx.freeze
+ MARKDOWN_HTML_COMMENT_BLOCK_REGEX_UNTRUSTED =
+ '(?P<html_comment_block>' \
+ '^<!--.*?\n' \
+ '(?:\n|.)*?' \
+ '\n.*?-->\ *$' \
+ ')'.freeze
def markdown_code_or_html_blocks
@markdown_code_or_html_blocks ||= %r{
@@ -491,14 +494,13 @@ module Gitlab
}mx.freeze
end
- def markdown_code_or_html_comments
- @markdown_code_or_html_comments ||= %r{
- #{MARKDOWN_CODE_BLOCK_REGEX}
- |
- #{MARKDOWN_HTML_COMMENT_LINE_REGEX}
- |
- #{MARKDOWN_HTML_COMMENT_BLOCK_REGEX}
- }mx.freeze
+ def markdown_code_or_html_comments_untrusted
+ @markdown_code_or_html_comments_untrusted ||=
+ "#{MARKDOWN_CODE_BLOCK_REGEX_UNTRUSTED}" \
+ "|" \
+ "#{MARKDOWN_HTML_COMMENT_LINE_REGEX_UNTRUSTED}" \
+ "|" \
+ "#{MARKDOWN_HTML_COMMENT_BLOCK_REGEX_UNTRUSTED}"
end
# Based on Jira's project key format
diff --git a/lib/gitlab/unicode.rb b/lib/gitlab/unicode.rb
index b49c5647dab..f291ea1b4ee 100644
--- a/lib/gitlab/unicode.rb
+++ b/lib/gitlab/unicode.rb
@@ -9,6 +9,12 @@ module Gitlab
# https://idiosyncratic-ruby.com/41-proper-unicoding.html
BIDI_REGEXP = /\p{Bidi Control}/.freeze
+ # Regular expression for identifying space characters
+ #
+ # In web browsers space characters can be confused with simple
+ # spaces which may be misleading
+ SPACE_REGEXP = /\p{Space_Separator}/.freeze
+
class << self
# Warning message used to highlight bidi characters in the GUI
def bidi_warning
diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb
index 96e74f00c78..7c7bda3a8f9 100644
--- a/lib/gitlab/untrusted_regexp.rb
+++ b/lib/gitlab/untrusted_regexp.rb
@@ -47,6 +47,17 @@ module Gitlab
RE2.Replace(text, regexp, rewrite)
end
+ # #scan returns an array of the groups captured, rather than MatchData.
+ # Use this to give the capture group name and grab the proper value
+ def extract_named_group(name, match)
+ return unless match
+
+ match_position = regexp.named_capturing_groups[name.to_s]
+ raise RegexpError, "Invalid named capture group: #{name}" unless match_position
+
+ match[match_position - 1]
+ end
+
def ==(other)
self.source == other.source
end
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index e3bf11b00b4..79e124a58f5 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -2,15 +2,37 @@
module Gitlab
class UrlSanitizer
+ include Gitlab::Utils::StrongMemoize
+
ALLOWED_SCHEMES = %w[http https ssh git].freeze
ALLOWED_WEB_SCHEMES = %w[http https].freeze
+ SCHEMIFIED_SCHEME = 'glschemelessuri'
+ SCHEMIFY_PLACEHOLDER = "#{SCHEMIFIED_SCHEME}://".freeze
+ # URI::DEFAULT_PARSER.make_regexp will only match URLs with schemes or
+ # relative URLs. This section will match schemeless URIs with userinfo
+ # e.g. user:pass@gitlab.com but will not match scp-style URIs e.g.
+ # user@server:path/to/file)
+ #
+ # The userinfo part is very loose compared to URI's implementation so we
+ # also match non-escaped userinfo e.g foo:b?r@gitlab.com which should be
+ # encoded as foo:b%3Fr@gitlab.com
+ URI_REGEXP = %r{
+ (?:
+ #{URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)}
+ |
+ (?:(?:(?!@)[%#{URI::REGEXP::PATTERN::UNRESERVED}#{URI::REGEXP::PATTERN::RESERVED}])+(?:@))
+ (?# negative lookahead ensures this isn't an SCP-style URL: [host]:[rel_path|abs_path] server:path/to/file)
+ (?!#{URI::REGEXP::PATTERN::HOST}:(?:#{URI::REGEXP::PATTERN::REL_PATH}|#{URI::REGEXP::PATTERN::ABS_PATH}))
+ #{URI::REGEXP::PATTERN::HOSTPORT}
+ )
+ }x
def self.sanitize(content)
- regexp = URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)
-
- content.gsub(regexp) { |url| new(url).masked_url }
- rescue Addressable::URI::InvalidURIError
- content.gsub(regexp, '')
+ content.gsub(URI_REGEXP) do |url|
+ new(url).masked_url
+ rescue Addressable::URI::InvalidURIError
+ ''
+ end
end
def self.valid?(url, allowed_schemes: ALLOWED_SCHEMES)
@@ -37,34 +59,45 @@ module Gitlab
@url = parse_url(url)
end
+ def credentials
+ @credentials ||= { user: @url.user.presence, password: @url.password.presence }
+ end
+
+ def user
+ credentials[:user]
+ end
+
def sanitized_url
- @sanitized_url ||= safe_url.to_s
+ safe_url = @url.dup
+ safe_url.password = nil
+ safe_url.user = nil
+ reverse_schemify(safe_url.to_s)
end
+ strong_memoize_attr :sanitized_url
def masked_url
url = @url.dup
url.password = "*****" if url.password.present?
url.user = "*****" if url.user.present?
- url.to_s
- end
-
- def credentials
- @credentials ||= { user: @url.user.presence, password: @url.password.presence }
- end
-
- def user
- credentials[:user]
+ reverse_schemify(url.to_s)
end
+ strong_memoize_attr :masked_url
def full_url
- @full_url ||= generate_full_url.to_s
+ return reverse_schemify(@url.to_s) unless valid_credentials?
+
+ url = @url.dup
+ url.password = encode_percent(credentials[:password]) if credentials[:password].present?
+ url.user = encode_percent(credentials[:user]) if credentials[:user].present?
+ reverse_schemify(url.to_s)
end
+ strong_memoize_attr :full_url
private
def parse_url(url)
- url = url.to_s.strip
- match = url.match(%r{\A(?:git|ssh|http(?:s?))\://(?:(.+)(?:@))?(.+)})
+ url = schemify(url.to_s.strip)
+ match = url.match(%r{\A(?:(?:#{SCHEMIFIED_SCHEME}|git|ssh|http(?:s?)):)?//(?:(.+)(?:@))?(.+)}o)
raw_credentials = match[1] if match
if raw_credentials.present?
@@ -83,24 +116,19 @@ module Gitlab
url
end
- def generate_full_url
- return @url unless valid_credentials?
-
- @url.dup.tap do |generated|
- generated.password = encode_percent(credentials[:password]) if credentials[:password].present?
- generated.user = encode_percent(credentials[:user]) if credentials[:user].present?
- end
+ def schemify(url)
+ # Prepend the placeholder scheme unless the URL has a scheme or is relative
+ url.prepend(SCHEMIFY_PLACEHOLDER) unless url.starts_with?(%r{(?:#{URI::REGEXP::PATTERN::SCHEME}:)?//}o)
+ url
end
- def safe_url
- safe_url = @url.dup
- safe_url.password = nil
- safe_url.user = nil
- safe_url
+ def reverse_schemify(url)
+ url.slice!(SCHEMIFY_PLACEHOLDER) if url.starts_with?(SCHEMIFY_PLACEHOLDER)
+ url
end
def valid_credentials?
- credentials && credentials.is_a?(Hash) && credentials.any?
+ credentials.is_a?(Hash) && credentials.values.any?
end
def encode_percent(string)
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index 436739bed12..a7e95a96b8b 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -25,7 +25,10 @@ module Rouge
yield %(<span id="LC#{@line_number}" class="line" lang="#{@tag}">)
line.each do |token, value|
- yield highlight_unicode_control_characters(span(token, value.chomp! || value))
+ value = value.chomp! || value
+ value = replace_space_characters(value)
+
+ yield highlight_unicode_control_characters(span(token, value))
end
yield ellipsis if @ellipsis_indexes.include?(@line_number - 1) && @ellipsis_svg.present?
@@ -42,6 +45,10 @@ module Rouge
%(<span class="gl-px-2 gl-rounded-base gl-mx-2 gl-bg-gray-100 gl-cursor-help has-tooltip" title="Content has been trimmed">#{@ellipsis_svg}</span>)
end
+ def replace_space_characters(text)
+ text.gsub(Gitlab::Unicode::SPACE_REGEXP, ' ')
+ end
+
def highlight_unicode_control_characters(text)
text.gsub(Gitlab::Unicode::BIDI_REGEXP) do |char|
%(<span class="unicode-bidi has-tooltip" data-toggle="tooltip" title="#{Gitlab::Unicode.bidi_warning}">#{char}</span>)