<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import { GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui'; import { GlFormInput, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { __, sprintf, n__ } from '~/locale'; import { __, sprintf, n__ } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -17,6 +17,7 @@ export default { ...@@ -17,6 +17,7 @@ export default {
GlFormInput, GlFormInput,
GlLink, GlLink,
GlLoadingIcon, GlLoadingIcon,
GlSprintf,
TooltipOnTruncate, TooltipOnTruncate,
Icon, Icon,
Stacktrace, Stacktrace,
...@@ -51,16 +52,6 @@ export default { ...@@ -51,16 +52,6 @@ export default {
computed: { computed: {
...mapState('details', ['error', 'loading', 'loadingStacktrace', 'stacktraceData']), ...mapState('details', ['error', 'loading', 'loadingStacktrace', 'stacktraceData']),
...mapGetters('details', ['stacktrace']), ...mapGetters('details', ['stacktrace']),
reported() {
return sprintf(
__('Reported %{timeAgo} by %{reportedBy}'),
{
reportedBy: `<strong>${this.error.culprit}</strong>`,
timeAgo: this.timeFormatted(this.stacktraceData.date_received),
},
false,
);
},
firstReleaseLink() { firstReleaseLink() {
return `${this.error.external_base_url}/releases/${this.error.first_release_short_version}`; return `${this.error.external_base_url}/releases/${this.error.first_release_short_version}`;
}, },
...@@ -120,7 +111,16 @@ export default { ...@@ -120,7 +111,16 @@ export default {
</div> </div>
<div v-else-if="showDetails" class="error-details"> <div v-else-if="showDetails" class="error-details">
<div class="top-area align-items-center justify-content-between py-3"> <div class="top-area align-items-center justify-content-between py-3">
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span> <div v-if="!loadingStacktrace && stacktrace" data-qa-selector="reported_text">
<gl-sprintf :message="__('Reported %{timeAgo} by %{reportedBy}')">
<template #reportedBy>
<strong>{{ error.culprit }}</strong>
</template>
<template #timeAgo>
{{ timeFormatted(stacktraceData.date_received) }}
</template>
</gl-sprintf>
</div>
<form ref="sentryIssueForm" :action="projectIssuesPath" method="POST"> <form ref="sentryIssueForm" :action="projectIssuesPath" method="POST">
<gl-form-input class="hidden" name="issue[title]" :value="issueTitle" /> <gl-form-input class="hidden" name="issue[title]" :value="issueTitle" />
<input name="issue[description]" :value="issueDescription" type="hidden" /> <input name="issue[description]" :value="issueDescription" type="hidden" />
... ...
......
...@@ -8,7 +8,7 @@ module Types ...@@ -8,7 +8,7 @@ module Types
field :head_sha, GraphQL::STRING_TYPE, null: false, field :head_sha, GraphQL::STRING_TYPE, null: false,
description: 'SHA of the HEAD at the time the comment was made' description: 'SHA of the HEAD at the time the comment was made'
field :base_sha, GraphQL::STRING_TYPE, null: false, field :base_sha, GraphQL::STRING_TYPE, null: true,
description: 'Merge base of the branch the comment was made on' description: 'Merge base of the branch the comment was made on'
field :start_sha, GraphQL::STRING_TYPE, null: false, field :start_sha, GraphQL::STRING_TYPE, null: false,
description: 'SHA of the branch being compared against' description: 'SHA of the branch being compared against'
... ...
......
...@@ -497,18 +497,29 @@ class Group < Namespace ...@@ -497,18 +497,29 @@ class Group < Namespace
group_group_links_query = GroupGroupLink.where(shared_group_id: self_and_ancestors_ids) group_group_links_query = GroupGroupLink.where(shared_group_id: self_and_ancestors_ids)
cte = Gitlab::SQL::CTE.new(:group_group_links_cte, group_group_links_query) cte = Gitlab::SQL::CTE.new(:group_group_links_cte, group_group_links_query)
cte_alias = cte.table.alias(GroupGroupLink.table_name)
link = GroupGroupLink link = GroupGroupLink
.with(cte.to_arel) .with(cte.to_arel)
.select(smallest_value_arel([cte_alias[:group_access], group_member_table[:access_level]],
'group_access'))
.from([group_member_table, cte.alias_to(group_group_link_table)]) .from([group_member_table, cte.alias_to(group_group_link_table)])
.where(group_member_table[:user_id].eq(user.id)) .where(group_member_table[:user_id].eq(user.id))
.where(group_member_table[:requested_at].eq(nil))
.where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id])) .where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id]))
.where(group_member_table[:source_type].eq('Namespace'))
.reorder(Arel::Nodes::Descending.new(group_group_link_table[:group_access])) .reorder(Arel::Nodes::Descending.new(group_group_link_table[:group_access]))
.first .first
link&.group_access link&.group_access
end end
def smallest_value_arel(args, column_alias)
Arel::Nodes::As.new(
Arel::Nodes::NamedFunction.new('LEAST', args),
Arel::Nodes::SqlLiteral.new(column_alias))
end
def self.groups_including_descendants_by(group_ids) def self.groups_including_descendants_by(group_ids)
Gitlab::ObjectHierarchy Gitlab::ObjectHierarchy
.new(Group.where(id: group_ids)) .new(Group.where(id: group_ids))
... ...
......
...@@ -128,7 +128,11 @@ module Ci ...@@ -128,7 +128,11 @@ module Ci
def all_related_merge_requests def all_related_merge_requests
strong_memoize(:all_related_merge_requests) do strong_memoize(:all_related_merge_requests) do
pipeline.ref ? pipeline.all_merge_requests_by_recency.to_a : [] if pipeline.ref && can?(current_user, :read_merge_request, pipeline.project)
pipeline.all_merge_requests_by_recency.to_a
else
[]
end
end end
end end
end end
... ...
......
...@@ -16,17 +16,14 @@ module Projects ...@@ -16,17 +16,14 @@ module Projects
@lfs_download_object = lfs_download_object @lfs_download_object = lfs_download_object
end end
# rubocop: disable CodeReuse/ActiveRecord
def execute def execute
return unless project&.lfs_enabled? && lfs_download_object return unless project&.lfs_enabled? && lfs_download_object
return error("LFS file with oid #{lfs_oid} has invalid attributes") unless lfs_download_object.valid? return error("LFS file with oid #{lfs_oid} has invalid attributes") unless lfs_download_object.valid?
return if LfsObject.exists?(oid: lfs_oid)
wrap_download_errors do wrap_download_errors do
download_lfs_file! download_lfs_file!
end end
end end
# rubocop: enable CodeReuse/ActiveRecord
private private
...@@ -39,14 +36,24 @@ module Projects ...@@ -39,14 +36,24 @@ module Projects
def download_lfs_file! def download_lfs_file!
with_tmp_file do |tmp_file| with_tmp_file do |tmp_file|
download_and_save_file!(tmp_file) download_and_save_file!(tmp_file)
project.all_lfs_objects << LfsObject.new(oid: lfs_oid,
size: lfs_size, project.lfs_objects << find_or_create_lfs_object(tmp_file)
file: tmp_file)
success success
end end
end end
def find_or_create_lfs_object(tmp_file)
lfs_obj = LfsObject.safe_find_or_create_by!(
oid: lfs_oid,
size: lfs_size
)
lfs_obj.update!(file: tmp_file) unless lfs_obj.file.file
lfs_obj
end
def download_and_save_file!(file) def download_and_save_file!(file)
digester = Digest::SHA256.new digester = Digest::SHA256.new
response = Gitlab::HTTP.get(lfs_sanitized_url, download_headers) do |fragment| response = Gitlab::HTTP.get(lfs_sanitized_url, download_headers) do |fragment|
... ...
......
...@@ -26,12 +26,12 @@ module Projects ...@@ -26,12 +26,12 @@ module Projects
return [] return []
end end
# Getting all Lfs pointers already in the database and linking them to the project # Downloading the required information and gathering it inside an
linked_oids = LfsLinkService.new(project).execute(lfs_pointers_in_repository.keys) # LfsDownloadObject for each oid
# Retrieving those oids not present in the database which we need to download #
missing_oids = lfs_pointers_in_repository.except(*linked_oids) LfsDownloadLinkListService
# Downloading the required information and gathering it inside a LfsDownloadObject for each oid .new(project, remote_uri: current_endpoint_uri)
LfsDownloadLinkListService.new(project, remote_uri: current_endpoint_uri).execute(missing_oids) .execute(lfs_pointers_in_repository)
rescue LfsDownloadLinkListService::DownloadLinksError => e rescue LfsDownloadLinkListService::DownloadLinksError => e
raise LfsObjectDownloadListError, "The LFS objects download list couldn't be imported. Error: #{e.message}" raise LfsObjectDownloadListError, "The LFS objects download list couldn't be imported. Error: #{e.message}"
end end
... ...
......
...@@ -11,8 +11,14 @@ class WebHookService ...@@ -11,8 +11,14 @@ class WebHookService
end end
end end
GITLAB_EVENT_HEADER = 'X-Gitlab-Event'
attr_accessor :hook, :data, :hook_name, :request_options attr_accessor :hook, :data, :hook_name, :request_options
def self.hook_to_event(hook_name)
hook_name.to_s.singularize.titleize
end
def initialize(hook, data, hook_name) def initialize(hook, data, hook_name)
@hook = hook @hook = hook
@data = data @data = data
...@@ -110,7 +116,7 @@ class WebHookService ...@@ -110,7 +116,7 @@ class WebHookService
@headers ||= begin @headers ||= begin
{ {
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
'X-Gitlab-Event' => hook_name.singularize.titleize GITLAB_EVENT_HEADER => self.class.hook_to_event(hook_name)
}.tap do |hash| }.tap do |hash|
hash['X-Gitlab-Token'] = Gitlab::Utils.remove_line_breaks(hook.token) if hook.token.present? hash['X-Gitlab-Token'] = Gitlab::Utils.remove_line_breaks(hook.token) if hook.token.present?
end end
... ...
......
---
title: Respect member access level for group shares
merge_request:
author:
type: security
---
title: Prevent an endless checking loop for two merge requests targeting each other
merge_request:
author:
type: security
---
title: Check merge requests read permissions before showing them in the pipeline widget
merge_request:
author:
type: security
---
title: Remove OID filtering during LFS imports
merge_request:
author:
type: security
---
title: Protect against denial of service using pipeline webhook recursion
merge_request:
author:
type: security
---
title: Don't require base_sha in DiffRefsType
merge_request:
author:
type: security
---
title: Sanitize output by dependency linkers
merge_request:
author:
type: security
---
title: Escape special chars in Sentry error header
merge_request:
author:
type: security
...@@ -1124,7 +1124,7 @@ type DiffRefs { ...@@ -1124,7 +1124,7 @@ type DiffRefs {
""" """
Merge base of the branch the comment was made on Merge base of the branch the comment was made on
""" """
baseSha: String! baseSha: String
""" """
SHA of the HEAD at the time the comment was made SHA of the HEAD at the time the comment was made
... ...
......
...@@ -7118,13 +7118,9 @@ ...@@ -7118,13 +7118,9 @@
   
], ],
"type": { "type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR", "kind": "SCALAR",
"name": "String", "name": "String",
"ofType": null "ofType": null
}
}, },
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
... ...
......
...@@ -195,7 +195,7 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph ...@@ -195,7 +195,7 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| Name | Type | Description | | Name | Type | Description |
| --- | ---- | ---------- | | --- | ---- | ---------- |
| `headSha` | String! | SHA of the HEAD at the time the comment was made | | `headSha` | String! | SHA of the HEAD at the time the comment was made |
| `baseSha` | String! | Merge base of the branch the comment was made on | | `baseSha` | String | Merge base of the branch the comment was made on |
| `startSha` | String! | SHA of the branch being compared against | | `startSha` | String! | SHA of the branch being compared against |
### Discussion ### Discussion
... ...
......
...@@ -4,6 +4,8 @@ module API ...@@ -4,6 +4,8 @@ module API
class Triggers < Grape::API class Triggers < Grape::API
include PaginationParams include PaginationParams
HTTP_GITLAB_EVENT_HEADER = "HTTP_#{WebHookService::GITLAB_EVENT_HEADER}".underscore.upcase
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
...@@ -19,6 +21,8 @@ module API ...@@ -19,6 +21,8 @@ module API
post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42283') Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42283')
forbidden! if gitlab_pipeline_hook_request?
# validate variables # validate variables
params[:variables] = params[:variables].to_h params[:variables] = params[:variables].to_h
unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) } unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) }
...@@ -128,5 +132,11 @@ module API ...@@ -128,5 +132,11 @@ module API
destroy_conditionally!(trigger) destroy_conditionally!(trigger)
end end
end end
helpers do
def gitlab_pipeline_hook_request?
request.get_header(HTTP_GITLAB_EVENT_HEADER) == WebHookService.hook_to_event(:pipeline_hooks)
end
end
end end
end end
...@@ -7,6 +7,8 @@ module Gitlab ...@@ -7,6 +7,8 @@ module Gitlab
GIT_INVALID_URL_REGEX = /^git\+#{URL_REGEX}/.freeze GIT_INVALID_URL_REGEX = /^git\+#{URL_REGEX}/.freeze
REPO_REGEX = %r{[^/'" ]+/[^/'" ]+}.freeze REPO_REGEX = %r{[^/'" ]+/[^/'" ]+}.freeze
include ActionView::Helpers::SanitizeHelper
class_attribute :file_type class_attribute :file_type
def self.support?(blob_name) def self.support?(blob_name)
...@@ -62,7 +64,10 @@ module Gitlab ...@@ -62,7 +64,10 @@ module Gitlab
end end
def link_tag(name, url) def link_tag(name, url)
%{<a href="#{ERB::Util.html_escape_once(url)}" rel="nofollow noreferrer noopener" target="_blank">#{ERB::Util.html_escape_once(name)}</a>}.html_safe sanitize(
%{<a href="#{ERB::Util.html_escape_once(url)}" rel="nofollow noreferrer noopener" target="_blank">#{ERB::Util.html_escape_once(name)}</a>},
attributes: %w[href rel target]
)
end end
# Links package names based on regex. # Links package names based on regex.
... ...
......