Commit 0637ba1e authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 4720b569
...@@ -42,6 +42,7 @@ module Ci ...@@ -42,6 +42,7 @@ module Ci
metrics: :gzip, metrics: :gzip,
metrics_referee: :gzip, metrics_referee: :gzip,
network_referee: :gzip, network_referee: :gzip,
lsif: :gzip,
# All these file formats use `raw` as we need to store them uncompressed # All these file formats use `raw` as we need to store them uncompressed
# for Frontend to fetch the files and do analysis # for Frontend to fetch the files and do analysis
...@@ -53,8 +54,7 @@ module Ci ...@@ -53,8 +54,7 @@ module Ci
dast: :raw, dast: :raw,
license_management: :raw, license_management: :raw,
license_scanning: :raw, license_scanning: :raw,
performance: :raw, performance: :raw
lsif: :raw
}.freeze }.freeze
TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze
......
...@@ -128,12 +128,12 @@ class Issue < ApplicationRecord ...@@ -128,12 +128,12 @@ class Issue < ApplicationRecord
def self.reference_pattern def self.reference_pattern
@reference_pattern ||= %r{ @reference_pattern ||= %r{
(#{Project.reference_pattern})? (#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<issue>\d+) #{Regexp.escape(reference_prefix)}#{Gitlab::Regex.issue}
}x }x
end end
def self.link_reference_pattern def self.link_reference_pattern
@link_reference_pattern ||= super("issues", /(?<issue>\d+)/) @link_reference_pattern ||= super("issues", Gitlab::Regex.issue)
end end
def self.reference_valid?(reference) def self.reference_valid?(reference)
......
...@@ -48,6 +48,7 @@ class MergeRequest < ApplicationRecord ...@@ -48,6 +48,7 @@ class MergeRequest < ApplicationRecord
# 1. There are arguments - in which case we might be trying to force-reload. # 1. There are arguments - in which case we might be trying to force-reload.
# 2. This association is already loaded. # 2. This association is already loaded.
# 3. The latest diff does not exist. # 3. The latest diff does not exist.
# 4. It doesn't have any merge_request_diffs - it returns an empty MergeRequestDiff
# #
# The second one in particular is important - MergeRequestDiff#merge_request # The second one in particular is important - MergeRequestDiff#merge_request
# is the inverse of MergeRequest#merge_request_diff, which means it may not be # is the inverse of MergeRequest#merge_request_diff, which means it may not be
...@@ -56,7 +57,7 @@ class MergeRequest < ApplicationRecord ...@@ -56,7 +57,7 @@ class MergeRequest < ApplicationRecord
def merge_request_diff def merge_request_diff
fallback = latest_merge_request_diff unless association(:merge_request_diff).loaded? fallback = latest_merge_request_diff unless association(:merge_request_diff).loaded?
fallback || super fallback || super || MergeRequestDiff.new(merge_request_id: id)
end end
belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline" belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline"
...@@ -404,7 +405,7 @@ class MergeRequest < ApplicationRecord ...@@ -404,7 +405,7 @@ class MergeRequest < ApplicationRecord
end end
def commits(limit: nil) def commits(limit: nil)
return merge_request_diff.commits(limit: limit) if persisted? return merge_request_diff.commits(limit: limit) if merge_request_diff.persisted?
commits_arr = if compare_commits commits_arr = if compare_commits
reversed_commits = compare_commits.reverse reversed_commits = compare_commits.reverse
...@@ -421,7 +422,7 @@ class MergeRequest < ApplicationRecord ...@@ -421,7 +422,7 @@ class MergeRequest < ApplicationRecord
end end
def commits_count def commits_count
if persisted? if merge_request_diff.persisted?
merge_request_diff.commits_count merge_request_diff.commits_count
elsif compare_commits elsif compare_commits
compare_commits.size compare_commits.size
...@@ -431,7 +432,7 @@ class MergeRequest < ApplicationRecord ...@@ -431,7 +432,7 @@ class MergeRequest < ApplicationRecord
end end
def commit_shas(limit: nil) def commit_shas(limit: nil)
return merge_request_diff.commit_shas(limit: limit) if persisted? return merge_request_diff.commit_shas(limit: limit) if merge_request_diff.persisted?
shas = shas =
if compare_commits if compare_commits
...@@ -492,11 +493,11 @@ class MergeRequest < ApplicationRecord ...@@ -492,11 +493,11 @@ class MergeRequest < ApplicationRecord
end end
def first_commit def first_commit
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first compare_commits.present? ? compare_commits.first : merge_request_diff.first_commit
end end
def raw_diffs(*args) def raw_diffs(*args)
merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args) compare.present? ? compare.raw_diffs(*args) : merge_request_diff.raw_diffs(*args)
end end
def diffs(diff_options = {}) def diffs(diff_options = {})
...@@ -557,7 +558,7 @@ class MergeRequest < ApplicationRecord ...@@ -557,7 +558,7 @@ class MergeRequest < ApplicationRecord
end end
def diff_base_commit def diff_base_commit
if persisted? if merge_request_diff.persisted?
merge_request_diff.base_commit merge_request_diff.base_commit
else else
branch_merge_base_commit branch_merge_base_commit
...@@ -565,7 +566,7 @@ class MergeRequest < ApplicationRecord ...@@ -565,7 +566,7 @@ class MergeRequest < ApplicationRecord
end end
def diff_start_commit def diff_start_commit
if persisted? if merge_request_diff.persisted?
merge_request_diff.start_commit merge_request_diff.start_commit
else else
target_branch_head target_branch_head
...@@ -573,7 +574,7 @@ class MergeRequest < ApplicationRecord ...@@ -573,7 +574,7 @@ class MergeRequest < ApplicationRecord
end end
def diff_head_commit def diff_head_commit
if persisted? if merge_request_diff.persisted?
merge_request_diff.head_commit merge_request_diff.head_commit
else else
source_branch_head source_branch_head
...@@ -581,7 +582,7 @@ class MergeRequest < ApplicationRecord ...@@ -581,7 +582,7 @@ class MergeRequest < ApplicationRecord
end end
def diff_start_sha def diff_start_sha
if persisted? if merge_request_diff.persisted?
merge_request_diff.start_commit_sha merge_request_diff.start_commit_sha
else else
target_branch_head.try(:sha) target_branch_head.try(:sha)
...@@ -589,7 +590,7 @@ class MergeRequest < ApplicationRecord ...@@ -589,7 +590,7 @@ class MergeRequest < ApplicationRecord
end end
def diff_base_sha def diff_base_sha
if persisted? if merge_request_diff.persisted?
merge_request_diff.base_commit_sha merge_request_diff.base_commit_sha
else else
branch_merge_base_commit.try(:sha) branch_merge_base_commit.try(:sha)
...@@ -597,7 +598,7 @@ class MergeRequest < ApplicationRecord ...@@ -597,7 +598,7 @@ class MergeRequest < ApplicationRecord
end end
def diff_head_sha def diff_head_sha
if persisted? if merge_request_diff.persisted?
merge_request_diff.head_commit_sha merge_request_diff.head_commit_sha
else else
source_branch_head.try(:sha) source_branch_head.try(:sha)
...@@ -758,7 +759,7 @@ class MergeRequest < ApplicationRecord ...@@ -758,7 +759,7 @@ class MergeRequest < ApplicationRecord
end end
def ensure_merge_request_diff def ensure_merge_request_diff
merge_request_diff || create_merge_request_diff merge_request_diff.persisted? || create_merge_request_diff
end end
def create_merge_request_diff def create_merge_request_diff
...@@ -1005,7 +1006,7 @@ class MergeRequest < ApplicationRecord ...@@ -1005,7 +1006,7 @@ class MergeRequest < ApplicationRecord
def closes_issues(current_user = self.author) def closes_issues(current_user = self.author)
if target_branch == project.default_branch if target_branch == project.default_branch
messages = [title, description] messages = [title, description]
messages.concat(commits.map(&:safe_message)) if merge_request_diff messages.concat(commits.map(&:safe_message)) if merge_request_diff.persisted?
Gitlab::ClosingIssueExtractor.new(project, current_user) Gitlab::ClosingIssueExtractor.new(project, current_user)
.closed_by_message(messages.join("\n")) .closed_by_message(messages.join("\n"))
...@@ -1421,7 +1422,7 @@ class MergeRequest < ApplicationRecord ...@@ -1421,7 +1422,7 @@ class MergeRequest < ApplicationRecord
end end
def has_commits? def has_commits?
merge_request_diff && commits_count.to_i > 0 merge_request_diff.persisted? && commits_count.to_i > 0
end end
def has_no_commits? def has_no_commits?
......
...@@ -19,9 +19,9 @@ class IssueTrackerService < Service ...@@ -19,9 +19,9 @@ class IssueTrackerService < Service
# overridden patterns. See ReferenceRegexes.external_pattern # overridden patterns. See ReferenceRegexes.external_pattern
def self.reference_pattern(only_long: false) def self.reference_pattern(only_long: false)
if only_long if only_long
/(\b[A-Z][A-Z0-9_]*-)(?<issue>\d+)/ /(\b[A-Z][A-Z0-9_]*-)#{Gitlab::Regex.issue}/
else else
/(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})(?<issue>\d+)/ /(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})#{Gitlab::Regex.issue}/
end end
end end
......
...@@ -8,7 +8,7 @@ class YoutrackService < IssueTrackerService ...@@ -8,7 +8,7 @@ class YoutrackService < IssueTrackerService
if only_long if only_long
/(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)/ /(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)/
else else
/(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)|(#{Issue.reference_prefix}(?<issue>\d+))/ /(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)|(#{Issue.reference_prefix}#{Gitlab::Regex.issue})/
end end
end end
......
---
title: Drop signatures in email replies
merge_request: 25389
author: Diego Louzán
type: changed
---
title: Upgrade pages to 1.16.0
merge_request: 25238
author:
type: added
...@@ -87,6 +87,7 @@ For instance `RETRIES=1 bin/rspec ...` would retry the failing examples once. ...@@ -87,6 +87,7 @@ For instance `RETRIES=1 bin/rspec ...` would retry the failing examples once.
- [Dropdowns rendering upward or downward due to window size and scroll position](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17660) - [Dropdowns rendering upward or downward due to window size and scroll position](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17660)
- [Lazy loaded images can cause Capybara to misclick](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18713) - [Lazy loaded images can cause Capybara to misclick](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18713)
- [Triggering JS events before the event handlers are set up](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18742) - [Triggering JS events before the event handlers are set up](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18742)
- [Wait for the image to be lazy-loaded when asserting on a Markdown image's src attribute](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25408)
#### Capybara viewport size related issues #### Capybara viewport size related issues
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
def execute(upload_parent:, uploader_class:) def execute(upload_parent:, uploader_class:)
attachments = [] attachments = []
message.attachments.each do |attachment| filter_signature_attachments(message).each do |attachment|
tmp = Tempfile.new("gitlab-email-attachment") tmp = Tempfile.new("gitlab-email-attachment")
begin begin
File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded } File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded }
...@@ -32,6 +32,22 @@ module Gitlab ...@@ -32,6 +32,22 @@ module Gitlab
attachments attachments
end end
private
# If this is a signed message (e.g. S/MIME or PGP), remove the signature
# from the uploaded attachments
def filter_signature_attachments(message)
attachments = message.attachments
if message.content_type&.starts_with?('multipart/signed')
signature_protocol = message.content_type_parameters[:protocol]
attachments.delete_if { |attachment| attachment.content_type.starts_with?(signature_protocol) } if signature_protocol.present?
end
attachments
end
end end
end end
end end
...@@ -112,7 +112,7 @@ module Gitlab ...@@ -112,7 +112,7 @@ module Gitlab
# Based on Jira's project key format # Based on Jira's project key format
# https://confluence.atlassian.com/adminjiraserver073/changing-the-project-key-format-861253229.html # https://confluence.atlassian.com/adminjiraserver073/changing-the-project-key-format-861253229.html
def jira_issue_key_regex def jira_issue_key_regex
@jira_issue_key_regex ||= /[A-Z][A-Z_0-9]+-\d+/ @jira_issue_key_regex ||= /[A-Z][A-Z_0-9]+-\d+\b/
end end
def jira_transition_id_regex def jira_transition_id_regex
...@@ -144,6 +144,10 @@ module Gitlab ...@@ -144,6 +144,10 @@ module Gitlab
def utc_date_regex def utc_date_regex
@utc_date_regex ||= /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z/.freeze @utc_date_regex ||= /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z/.freeze
end end
def issue
@issue ||= /(?<issue>\d+\b)/
end
end end
end end
......
...@@ -77,6 +77,18 @@ describe Projects::MergeRequestsController do ...@@ -77,6 +77,18 @@ describe Projects::MergeRequestsController do
end end
end end
context 'when diff is missing' do
render_views
it 'renders merge request page' do
merge_request.merge_request_diff.destroy
go(format: :html)
expect(response).to be_successful
end
end
it "renders merge request page" do it "renders merge request page" do
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
......
...@@ -141,11 +141,11 @@ FactoryBot.define do ...@@ -141,11 +141,11 @@ FactoryBot.define do
trait :lsif do trait :lsif do
file_type { :lsif } file_type { :lsif }
file_format { :raw } file_format { :gzip }
after(:build) do |artifact, evaluator| after(:build) do |artifact, evaluator|
artifact.file = fixture_file_upload( artifact.file = fixture_file_upload(
Rails.root.join('spec/fixtures/lsif.json.gz'), 'application/octet-stream') Rails.root.join('spec/fixtures/lsif.json.gz'), 'application/x-gzip')
end end
end end
......
...@@ -53,7 +53,7 @@ describe 'User creates snippet', :js do ...@@ -53,7 +53,7 @@ describe 'User creates snippet', :js do
page.within('#new_personal_snippet .md-preview-holder') do page.within('#new_personal_snippet .md-preview-holder') do
expect(page).to have_content('My Snippet') expect(page).to have_content('My Snippet')
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z}) expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z})
# Adds a cache buster for checking if the image exists as Selenium is now handling the cached regquests # Adds a cache buster for checking if the image exists as Selenium is now handling the cached regquests
...@@ -73,7 +73,7 @@ describe 'User creates snippet', :js do ...@@ -73,7 +73,7 @@ describe 'User creates snippet', :js do
click_button('Create snippet') click_button('Create snippet')
wait_for_requests wait_for_requests
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z}) expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") } reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
...@@ -98,7 +98,7 @@ describe 'User creates snippet', :js do ...@@ -98,7 +98,7 @@ describe 'User creates snippet', :js do
expect(page).to have_selector('strong') expect(page).to have_selector('strong')
end end
expect(page).to have_content('Hello World!') expect(page).to have_content('Hello World!')
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z}) expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") } reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
......
# Do not mangle line endings or signature will be invalid
valid_reply_signed_smime.eml eol=crlf
\ No newline at end of file
User-Agent: Microsoft-MacOutlook/10.22.0.200209
Date: Mon, 17 Feb 2020 22:56:47 +0100
Subject: Re: htmltest | test issue (#1)
From: "Louzan Martinez, Diego (ext) (SI BP R&D ZG)"
<diego.louzan.ext@siemens.com>
To: Administrator / htmltest
<dlouzan.dummy+c034670b1623e617e15a3df64223d363@gmail.com>
Message-ID: <012E37D9-2A3F-4AC8-B79A-871F42914D86@siemens.com>
Thread-Topic: htmltest | test issue (#1)
References: <reply-c034670b1623e617e15a3df64223d363@169.254.169.254>
<issue_451@169.254.169.254>
<note_1797@169.254.169.254>
In-Reply-To: <note_1797@169.254.169.254>
Content-type: multipart/signed;
protocol="application/pkcs7-signature";
micalg=sha256;
boundary="B_3664825007_1904734766"
MIME-Version: 1.0
--B_3664825007_1904734766
Content-type: multipart/mixed;
boundary="B_3664825007_384940722"
--B_3664825007_384940722
Content-type: multipart/alternative;
boundary="B_3664825007_1519466360"
--B_3664825007_1519466360
Content-type: text/plain;
charset="UTF-8"
Content-transfer-encoding: quoted-printable
Me too, with an attachment
=20
From: Administrator <dlouzan.dummy@gmail.com>
Reply to: Administrator / htmltest <dlouzan.dummy+c034670b1623e617e15a3df64=
223d363@gmail.com>
Date: Monday, 17 February 2020 at 22:55
To: "Louzan Martinez, Diego (ext) (SOP IT STG XS)" <diego.louzan.ext@siemen=
s.com>
Subject: Re: htmltest | test issue (#1)
=20
Administrator commented:=20
I pity the foo !!!
=E2=80=94=20
Reply to this email directly or view it on GitLab.=20
You're receiving this email because of your account on 169.254.169.254. If =
you'd like to receive fewer emails, you can unsubscribe from this thread or =
adjust your notification settings.=20
--B_3664825007_1519466360
Content-type: text/html;
charset="UTF-8"
Content-transfer-encoding: quoted-printable
<html xmlns:o=3D"urn:schemas-microsoft-com:office:office" xmlns:w=3D"urn:schema=
s-microsoft-com:office:word" xmlns:m=3D"http://schemas.microsoft.com/office/20=
04/12/omml" xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta http-equiv=3DC=
ontent-Type content=3D"text/html; charset=3Dutf-8"><meta name=3DGenerator content=3D=
"Microsoft Word 15 (filtered medium)"><title>GitLab</title><style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
text-decoration:underline;}
span.EmailStyle19
{mso-style-type:personal-reply;
font-family:"Calibri",sans-serif;
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-size:10.0pt;}
@page WordSection1
{size:612.0pt 792.0pt;
margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
{page:WordSection1;}
--></style></head><body lang=3Den-ES link=3Dblue vlink=3Dpurple><div class=3DWordSe=
ction1><p class=3DMsoNormal><span lang=3DEN-US style=3D'mso-fareast-language:EN-US=
'>Me too, with an attachment<o:p></o:p></span></p><p class=3DMsoNormal><span s=
tyle=3D'mso-fareast-language:EN-US'><o:p>&nbsp;</o:p></span></p><div style=3D'bo=
rder:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm'><p class=
=3DMsoNormal><b><span style=3D'font-size:12.0pt;color:black'>From: </span></b><s=
pan style=3D'font-size:12.0pt;color:black'>Administrator &lt;dlouzan.dummy@gma=
il.com&gt;<br><b>Reply to: </b>Administrator / htmltest &lt;dlouzan.dummy+c0=
34670b1623e617e15a3df64223d363@gmail.com&gt;<br><b>Date: </b>Monday, 17 Febr=
uary 2020 at 22:55<br><b>To: </b>&quot;Louzan Martinez, Diego (ext) (SOP IT =
STG XS)&quot; &lt;diego.louzan.ext@siemens.com&gt;<br><b>Subject: </b>Re: ht=
mltest | test issue (#1)<o:p></o:p></span></p></div><div><p class=3DMsoNormal>=
<o:p>&nbsp;</o:p></p></div><div><p><span style=3D'color:#777777'><a href=3D"http=
://localhost:3000/root">Administrator</a> commented: <o:p></o:p></span></p><=
div><p>I pity the foo !!!<o:p></o:p></p></div></div><div style=3D'margin-top:7=
.5pt'><p><span style=3D'font-size:12.0pt;color:#777777'>=E2=80=94 <br>Reply to this =
email directly or <a href=3D"http://localhost:3000/root/htmltest/issues/1#note=
_1797">view it on GitLab</a>. <br>You're receiving this email because of you=
r account on 169.254.169.254. If you'd like to receive fewer emails, you can=
<a href=3D"http://localhost:3000/sent_notifications/c034670b1623e617e15a3df64=
223d363/unsubscribe">unsubscribe</a> from this thread or adjust your notific=
ation settings. <o:p></o:p></span></p></div></div></body></html>
--B_3664825007_1519466360--
--B_3664825007_384940722
Content-type: image/png; name="gitlab_logo.png";
x-mac-creator="4F50494D";
x-mac-type="504E4766"
Content-disposition: attachment;
filename="gitlab_logo.png"
Content-transfer-encoding: base64
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
--B_3664825007_384940722--
--B_3664825007_1904734766
Content-type: application/pkcs7-signature; name="smime.p7s"
Content-transfer-encoding: base64
Content-disposition: attachment;
filename="smime.p7s"
MIIRpwYJKoZIhvcNAQcCoIIRmDCCEZQCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0B
BwGggg8VMIIHojCCBYqgAwIBAgIEZ5a6PTANBgkqhkiG9w0BAQsFADCBtjELMAkGA1UEBhMC
REUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xEDAOBgNVBAoMB1NpZW1l
bnMxETAPBgNVBAUTCFpaWlpaWkE2MR0wGwYDVQQLDBRTaWVtZW5zIFRydXN0IENlbnRlcjE/
MD0GA1UEAww2U2llbWVucyBJc3N1aW5nIENBIE1lZGl1bSBTdHJlbmd0aCBBdXRoZW50aWNh
dGlvbiAyMDE2MB4XDTE5MTEyMTE0NDQ0N1oXDTIwMTEyMTE0NDQ0N1owdzERMA8GA1UEBRMI
WjAwM0gwOFQxDjAMBgNVBCoMBURpZWdvMRgwFgYDVQQEDA9Mb3V6YW4gTWFydGluZXoxGDAW
BgNVBAoMD1NpZW1lbnMtUGFydG5lcjEeMBwGA1UEAwwVTG91emFuIE1hcnRpbmV6IERpZWdv
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuInpNaC7NRYD+0pOpHDz2pk9xmPt
JGj860SF6Nmn6Eu9EMYKEDfneC6z5QcH+mPS2d0VWgqVVGbRXSPsxJtbi9TCWjQUZdHglEZK
z9zxoFDh2dvW5/+TOT5Jf78FXyqak0YtY6+oMjQ/i9RUqPL7sIlyXLrBYrILzQ9Afo+7bXZg
v3ypp6xtqAV2ctHzQWFi0onJzxLVYguiVb7fFF9rBEMvSZonuw5tvOwJIhbe5FDFOrDcfbyU
ofZ/wikIZ+A+CE5GryXuuQmGxJaC2QqOkRAWQDzLDx9nG+rKiEs5OvlfEZC7EV1PyjZ93coM
faCVdlAgcFZ5fvd37CjyjKl+1QIDAQABo4IC9DCCAvAwggEEBggrBgEFBQcBAQSB9zCB9DAy
BggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5jcnQwQQYI
KwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jQUNl
cnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVucy5jb20vQ049WlpaWlpa
QTYsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRwOi8vb2Nz
cC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAU+BVdRwxsd3tyxAIXkWii
tvdqCUQwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGDSsGAQQBoWkHAgIEAQMwKTAnBggr
BgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kvMIHKBgNVHR8EgcIwgb8wgbyg
gbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTYuY3JshkFsZGFwOi8v
Y2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTYsTD1QS0k/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
TGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkE2LG89VHJ1c3RjZW50ZXI/
Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
AwQwDgYDVR0PAQH/BAQDAgeAMFUGA1UdEQROMEygLAYKKwYBBAGCNxQCA6AeDBxkaWVnby5s
b3V6YW4uZXh0QHNpZW1lbnMuY29tgRxkaWVnby5sb3V6YW4uZXh0QHNpZW1lbnMuY29tMB0G
A1UdDgQWBBQj8k8aqZey68w8ALYKGJSGMt5hZDANBgkqhkiG9w0BAQsFAAOCAgEAFDHqxpb1
R9cB4noC9vx09bkNbmXCpVfl3XCQUmAWTznC0nwEssTTjo0PWuIV4C3jnsp0MRUeHZ6lsyhZ
OzS1ETwYgvj6wzjb8RF3wgn7N/JOvFGaErMz5HZpKOfzGiNpW6/Rmd4hsRDjAwOVQOXUTqc/
0Bj3FMoLRCSWSnTp5HdyvrY2xOKHfTrTjzmcLdFaKE2F5n7+dBkwCKVfzut8CqfVq/I7ks4m
D1IHk93/P6l9U34R2FHPt6zRTNZcWmDirRSlMH4L18CnfiNPuDN/PtRYlt3Vng5EdYN0VCg2
NM/uees0U4ingCb0NFjg66uQ/tjfPQk55MN4Wpls4N6TkMoTCWLiqZzYTGdmVQexzroL6940
tmMr8LoN3TpPf0OdvdKEpyH7fzsx5QlmQyywIWec6X+Fx6+l0g91VJnPEtqACpfZIBZtviHl
gfX298w+SsvBK8C48Pqs8Ijh7tLrCxx7VMLVHZqwWWPK53ga+CDWmjoSQPxi+CPZF7kao6N5
4GrJWwSHlHh6WzTbLyLvTJZZ775Utp4W8s8xMUsQJ413iYzEaC8FcSeNjSk5UiDDiHrKmzpM
tbApD3pUXStblUMKYGTG1Mj9BcEBFkCdoGlw/ulszIrKFfOyRNDG3Ay+Dj/oMjoKsJphu3px
wyft82rTer7UW/I7o0h0DAG4lkMwggdrMIIFU6ADAgECAgR5nlqfMA0GCSqGSIb3DQEBCwUA
MIGeMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQ
MA4GA1UECgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaQTMxHTAbBgNVBAsMFFNpZW1lbnMg
VHJ1c3QgQ2VudGVyMScwJQYDVQQDDB5TaWVtZW5zIElzc3VpbmcgQ0EgRUUgRW5jIDIwMTYw
HhcNMTkwOTI3MDgwMTM5WhcNMjAwOTI3MDgwMTM3WjB3MREwDwYDVQQFEwhaMDAzSDA4VDEO
MAwGA1UEKgwFRGllZ28xGDAWBgNVBAQMD0xvdXphbiBNYXJ0aW5lejEYMBYGA1UECgwPU2ll
bWVucy1QYXJ0bmVyMR4wHAYDVQQDDBVMb3V6YW4gTWFydGluZXogRGllZ28wggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyby5qKzZIrGYWRqxnaAyMt/a/uc0uMk0F3MjwxvPM
vh5DllUpqx0l8ZDakDjPhlEXTeoL4DHNgmh+CDCs76CppM3cNG/1W1Ajo/L2iwMoXaxYuQ/F
q7ED+02KEkWX2DDVVG3fhrUGP20QAq77xPDptmVWZnUnuobZBNYkC49Xfl9HJvkJL8P0+Jqb
Eae7p4roiEr7wNkGriwrVXgA3oPNF/W+OuI76JTNTajS/6PAK/GeqIvLjfuBXpdBZTY031nE
Cztca8vI1jUjQzVhS+0dWpvpfhkVumbvOnid8DI9lapYsX8dpZFsa3ya+T3tjUdGSOOKi0kg
lWf/XYyyfhmDAgMBAAGjggLVMIIC0TAdBgNVHQ4EFgQUprhTCDwNLfPImpSfWdq+QvPTo9Mw
JwYDVR0RBCAwHoEcZGllZ28ubG91emFuLmV4dEBzaWVtZW5zLmNvbTAOBgNVHQ8BAf8EBAMC
BDAwLAYDVR0lBCUwIwYIKwYBBQUHAwQGCisGAQQBgjcKAwQGCysGAQQBgjcKAwQBMIHKBgNV
HR8EgcIwgb8wgbyggbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTMu
Y3JshkFsZGFwOi8vY2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTMsTD1QS0k/Y2VydGlmaWNh
dGVSZXZvY2F0aW9uTGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkEzLG89
VHJ1c3RjZW50ZXI/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDBFBgNVHSAEPjA8MDoGDSsG
AQQBoWkHAgIEAQMwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kv
MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUoassbqB68NPCTeof8R4hivwMre8wggEEBggr
BgEFBQcBAQSB9zCB9DAyBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9a
WlpaWlpBMy5jcnQwQQYIKwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpa
WlpBMyxMPVBLST9jQUNlcnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVu
cy5jb20vQ049WlpaWlpaQTMsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUF
BzABhiRodHRwOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wDQYJKoZIhvcNAQEL
BQADggIBAF98ZMNg28LgkwdjOdvOGbC1QitsWjZTyotmQESF0nClDLUhb0O5675vVixntbrf
eB8xy1+KRiadk40GnAIJ0YzmNl4Tav6hPYv9VBWe5olsWG7C4qB3Q/SwhvW/e+owxv1cBra8
R3oRudiN81eTZQHyNghRephVqQG/dpPYqydoANfIhEpHa79QlpaCAeYl4896AZOS8HYbkDFs
hLdv7sEHtl79YuSWI1wBjbJl70c0Sb4wLRgCPuHyQj2Uw/vQ5xJlEvBDZAIXXe1TP/nqiuY6
7nweJbbeqfFE6ZP3kCe+mEIWGSaO0iThZyLGer8fHs1XiEmhhPgvC7P7KodzpXU6+hX+ZzbD
DxEjFfetV5sh0aNSXG9xx4hZmS9bpImBGR8MvZ7cgxqItvLtY2xvfUbYW244d4RcWesaCDq3
ZEIo6uCIzOzJAwjUdLIac+lLV0rxiHmb7O3cQ19kjpWDB31hmfrus/TKJ55pBKVWBX5m/mFv
K8Ep5USpGrNS0EzOP7I1kQZv2VsvAhSxk/m5FMLpDy8T0O8YgbLypTXoeJFWCF6RduSjVsaZ
lkAtTQYud683pjyOMxJXaQUYGU1PmEYSOonMkVsT9aBcxYkXLp+Ln/+8G0OCYu7dRdwnj+Ut
7yR/ltxtgDcaFApCb0qBTKbgbqZk1fASmkOp+kbdYmoUMYICVjCCAlICAQEwgb8wgbYxCzAJ
BgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQK
DAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVzdCBD
ZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3RyZW5ndGggQXV0
aGVudGljYXRpb24gMjAxNgIEZ5a6PTANBglghkgBZQMEAgEFAKBpMC8GCSqGSIb3DQEJBDEi
BCAOR58AbNfSrI+vtMs+dgAQtn3IVZ3RjYC5hz3j9k+6TTAYBgkqhkiG9w0BCQMxCwYJKoZI
hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAyMTcyMTU2NDdaMA0GCSqGSIb3DQEBAQUABIIB
AHLSBcFHhNHPevbwqvA2ecuVb/aKnj45CFF6l8esP1H5DRm1ee5qMKuIS84NFuFC9RUENNhW
DBzsB+BVGz64o1f8QgIklYVrIJ4JZ0q1abNG7NbkVKWIpS3CQo//YWShUTYg+JpKx4YbahGR
sP5zbufbU4eagrrqBChjPTLy+njdjwCNu0XPykBTKOOf6BMjnS33AYjHJyh83JOY7rw3IDLx
8POQH4g5EMRpl9354s0rEkIezMt7pfUAsqY3QnQ8hvlE4KTikPQ+tvLMK1l/ffcLAP8BdBNI
YA3ikb3qCoGNSLKieYzNnBPhNOIJELUtEEaljAFZYMQzMKCbI4JdiDs=
--B_3664825007_1904734766--
...@@ -16,5 +16,20 @@ describe Gitlab::Email::AttachmentUploader do ...@@ -16,5 +16,20 @@ describe Gitlab::Email::AttachmentUploader do
expect(link[:alt]).to eq("bricks") expect(link[:alt]).to eq("bricks")
expect(link[:url]).to include("bricks.png") expect(link[:url]).to include("bricks.png")
end end
context 'with a signed message' do
let(:message_raw) { fixture_file("emails/valid_reply_signed_smime.eml") }
it 'uploads all attachments except the signature' do
links = described_class.new(message).execute(upload_parent: project, uploader_class: FileUploader)
expect(links).not_to include(a_hash_including(alt: 'smime.p7s'))
image_link = links.first
expect(image_link).not_to be_nil
expect(image_link[:alt]).to eq('gitlab_logo')
expect(image_link[:url]).to include('gitlab_logo.png')
end
end
end end
end end
...@@ -3,17 +3,23 @@ ...@@ -3,17 +3,23 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Email::Handler do describe Gitlab::Email::Handler do
let(:email) { Mail.new { body 'email' } }
describe '.for' do describe '.for' do
it 'picks issue handler if there is no merge request prefix' do it 'picks issue handler if there is no merge request prefix' do
expect(described_class.for('email', 'project+key')).to be_an_instance_of(Gitlab::Email::Handler::CreateIssueHandler) expect(described_class.for(email, 'project+key')).to be_an_instance_of(Gitlab::Email::Handler::CreateIssueHandler)
end end
it 'picks merge request handler if there is merge request key' do it 'picks merge request handler if there is merge request key' do
expect(described_class.for('email', 'project+merge-request+key')).to be_an_instance_of(Gitlab::Email::Handler::CreateMergeRequestHandler) expect(described_class.for(email, 'project+merge-request+key')).to be_an_instance_of(Gitlab::Email::Handler::CreateMergeRequestHandler)
end end
it 'returns nil if no handler is found' do it 'returns nil if no handler is found' do
expect(described_class.for('email', '')).to be_nil expect(described_class.for(email, '')).to be_nil
end
it 'returns nil if provided email is nil' do
expect(described_class.for(nil, '')).to be_nil
end end
end end
...@@ -25,7 +31,7 @@ describe Gitlab::Email::Handler do ...@@ -25,7 +31,7 @@ describe Gitlab::Email::Handler do
it 'picks each handler at least once' do it 'picks each handler at least once' do
matched_handlers = addresses.map do |address| matched_handlers = addresses.map do |address|
described_class.for('email', address).class described_class.for(email, address).class
end end
expect(matched_handlers.uniq).to match_array(ce_handlers) expect(matched_handlers.uniq).to match_array(ce_handlers)
...@@ -34,7 +40,7 @@ describe Gitlab::Email::Handler do ...@@ -34,7 +40,7 @@ describe Gitlab::Email::Handler do
it 'can pick exactly one handler for each address' do it 'can pick exactly one handler for each address' do
addresses.each do |address| addresses.each do |address|
matched_handlers = ce_handlers.select do |handler| matched_handlers = ce_handlers.select do |handler|
handler.new('email', address).can_handle? handler.new(email, address).can_handle?
end end
expect(matched_handlers.count).to eq(1), "#{address} matches #{matched_handlers.count} handlers: #{matched_handlers}" expect(matched_handlers.count).to eq(1), "#{address} matches #{matched_handlers.count} handlers: #{matched_handlers}"
......
...@@ -52,10 +52,10 @@ describe Gitlab::ImportExport::MergeRequestParser do ...@@ -52,10 +52,10 @@ describe Gitlab::ImportExport::MergeRequestParser do
context 'when the diff is invalid' do context 'when the diff is invalid' do
let(:merge_request_diff) { build(:merge_request_diff, merge_request: merge_request, base_commit_sha: 'foobar') } let(:merge_request_diff) { build(:merge_request_diff, merge_request: merge_request, base_commit_sha: 'foobar') }
it 'sets the diff to nil' do it 'sets the diff to empty diff' do
expect(merge_request_diff).to be_invalid expect(merge_request_diff).to be_invalid
expect(merge_request_diff.merge_request).to eq merge_request expect(merge_request_diff.merge_request).to eq merge_request
expect(parsed_merge_request.merge_request_diff).to be_nil expect(parsed_merge_request.merge_request_diff).to be_empty
end end
end end
end end
......
...@@ -662,13 +662,12 @@ describe MergeRequest do ...@@ -662,13 +662,12 @@ describe MergeRequest do
end end
describe '#raw_diffs' do describe '#raw_diffs' do
let(:merge_request) { build(:merge_request) }
let(:options) { { paths: ['a/b', 'b/a', 'c/*'] } } let(:options) { { paths: ['a/b', 'b/a', 'c/*'] } }
context 'when there are MR diffs' do context 'when there are MR diffs' do
it 'delegates to the MR diffs' do let(:merge_request) { create(:merge_request, :with_diffs) }
merge_request.merge_request_diff = MergeRequestDiff.new
it 'delegates to the MR diffs' do
expect(merge_request.merge_request_diff).to receive(:raw_diffs).with(options) expect(merge_request.merge_request_diff).to receive(:raw_diffs).with(options)
merge_request.raw_diffs(options) merge_request.raw_diffs(options)
...@@ -676,6 +675,8 @@ describe MergeRequest do ...@@ -676,6 +675,8 @@ describe MergeRequest do
end end
context 'when there are no MR diffs' do context 'when there are no MR diffs' do
let(:merge_request) { build(:merge_request) }
it 'delegates to the compare object' do it 'delegates to the compare object' do
merge_request.compare = double(:compare) merge_request.compare = double(:compare)
......
...@@ -38,8 +38,8 @@ describe YoutrackService do ...@@ -38,8 +38,8 @@ describe YoutrackService do
expect(described_class.reference_pattern.match('YT-123')[:issue]).to eq('YT-123') expect(described_class.reference_pattern.match('YT-123')[:issue]).to eq('YT-123')
end end
it 'does not allow issue number to be followed by a letter' do it 'allows lowercase project key on the reference' do
expect(described_class.reference_pattern.match('YT-123A')).to eq(nil) expect(described_class.reference_pattern.match('yt-123')[:issue]).to eq('yt-123')
end end
end end
......
...@@ -21,4 +21,8 @@ RSpec.shared_examples 'allows project key on reference pattern' do |url_attr| ...@@ -21,4 +21,8 @@ RSpec.shared_examples 'allows project key on reference pattern' do |url_attr|
expect(described_class.reference_pattern.match('3EXT_EXT-1234')).to eq nil expect(described_class.reference_pattern.match('3EXT_EXT-1234')).to eq nil
expect(described_class.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234' expect(described_class.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
end end
it 'does not allow issue number to finish with a letter' do
expect(described_class.reference_pattern.match('EXT-123A')).to eq(nil)
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment