summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--app/models/ci/job_artifact.rb4
-rw-r--r--app/models/issue.rb4
-rw-r--r--app/models/merge_request.rb31
-rw-r--r--app/models/project_services/issue_tracker_service.rb4
-rw-r--r--app/models/project_services/youtrack_service.rb2
-rw-r--r--changelogs/unreleased/feat-drop-signatures-email-replies.yml5
-rw-r--r--changelogs/unreleased/pages-1-16-release.yml5
-rw-r--r--doc/development/testing_guide/flaky_tests.md1
-rw-r--r--lib/gitlab/email/attachment_uploader.rb18
-rw-r--r--lib/gitlab/regex.rb6
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb12
-rw-r--r--spec/factories/ci/job_artifacts.rb4
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb6
-rw-r--r--spec/fixtures/emails/.gitattributes2
-rw-r--r--spec/fixtures/emails/valid_reply_signed_smime.eml294
-rw-r--r--spec/lib/gitlab/email/attachment_uploader_spec.rb15
-rw-r--r--spec/lib/gitlab/email/handler_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/merge_request_parser_spec.rb4
-rw-r--r--spec/models/merge_request_spec.rb7
-rw-r--r--spec/models/project_services/youtrack_service_spec.rb4
-rw-r--r--spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb4
22 files changed, 408 insertions, 42 deletions
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 141f2e805be..15b989e398f 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.15.0
+1.16.0
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 564853fc8a1..b66bc78094f 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -42,6 +42,7 @@ module Ci
metrics: :gzip,
metrics_referee: :gzip,
network_referee: :gzip,
+ lsif: :gzip,
# All these file formats use `raw` as we need to store them uncompressed
# for Frontend to fetch the files and do analysis
@@ -53,8 +54,7 @@ module Ci
dast: :raw,
license_management: :raw,
license_scanning: :raw,
- performance: :raw,
- lsif: :raw
+ performance: :raw
}.freeze
TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze
diff --git a/app/models/issue.rb b/app/models/issue.rb
index be702134ced..83f9e803d42 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -128,12 +128,12 @@ class Issue < ApplicationRecord
def self.reference_pattern
@reference_pattern ||= %r{
(#{Project.reference_pattern})?
- #{Regexp.escape(reference_prefix)}(?<issue>\d+)
+ #{Regexp.escape(reference_prefix)}#{Gitlab::Regex.issue}
}x
end
def self.link_reference_pattern
- @link_reference_pattern ||= super("issues", /(?<issue>\d+)/)
+ @link_reference_pattern ||= super("issues", Gitlab::Regex.issue)
end
def self.reference_valid?(reference)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 6c32bdadfa8..b469174fc63 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -48,6 +48,7 @@ class MergeRequest < ApplicationRecord
# 1. There are arguments - in which case we might be trying to force-reload.
# 2. This association is already loaded.
# 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
# is the inverse of MergeRequest#merge_request_diff, which means it may not be
@@ -56,7 +57,7 @@ class MergeRequest < ApplicationRecord
def merge_request_diff
fallback = latest_merge_request_diff unless association(:merge_request_diff).loaded?
- fallback || super
+ fallback || super || MergeRequestDiff.new(merge_request_id: id)
end
belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline"
@@ -404,7 +405,7 @@ class MergeRequest < ApplicationRecord
end
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
reversed_commits = compare_commits.reverse
@@ -421,7 +422,7 @@ class MergeRequest < ApplicationRecord
end
def commits_count
- if persisted?
+ if merge_request_diff.persisted?
merge_request_diff.commits_count
elsif compare_commits
compare_commits.size
@@ -431,7 +432,7 @@ class MergeRequest < ApplicationRecord
end
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 =
if compare_commits
@@ -492,11 +493,11 @@ class MergeRequest < ApplicationRecord
end
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
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
def diffs(diff_options = {})
@@ -557,7 +558,7 @@ class MergeRequest < ApplicationRecord
end
def diff_base_commit
- if persisted?
+ if merge_request_diff.persisted?
merge_request_diff.base_commit
else
branch_merge_base_commit
@@ -565,7 +566,7 @@ class MergeRequest < ApplicationRecord
end
def diff_start_commit
- if persisted?
+ if merge_request_diff.persisted?
merge_request_diff.start_commit
else
target_branch_head
@@ -573,7 +574,7 @@ class MergeRequest < ApplicationRecord
end
def diff_head_commit
- if persisted?
+ if merge_request_diff.persisted?
merge_request_diff.head_commit
else
source_branch_head
@@ -581,7 +582,7 @@ class MergeRequest < ApplicationRecord
end
def diff_start_sha
- if persisted?
+ if merge_request_diff.persisted?
merge_request_diff.start_commit_sha
else
target_branch_head.try(:sha)
@@ -589,7 +590,7 @@ class MergeRequest < ApplicationRecord
end
def diff_base_sha
- if persisted?
+ if merge_request_diff.persisted?
merge_request_diff.base_commit_sha
else
branch_merge_base_commit.try(:sha)
@@ -597,7 +598,7 @@ class MergeRequest < ApplicationRecord
end
def diff_head_sha
- if persisted?
+ if merge_request_diff.persisted?
merge_request_diff.head_commit_sha
else
source_branch_head.try(:sha)
@@ -758,7 +759,7 @@ class MergeRequest < ApplicationRecord
end
def ensure_merge_request_diff
- merge_request_diff || create_merge_request_diff
+ merge_request_diff.persisted? || create_merge_request_diff
end
def create_merge_request_diff
@@ -1005,7 +1006,7 @@ class MergeRequest < ApplicationRecord
def closes_issues(current_user = self.author)
if target_branch == project.default_branch
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)
.closed_by_message(messages.join("\n"))
@@ -1421,7 +1422,7 @@ class MergeRequest < ApplicationRecord
end
def has_commits?
- merge_request_diff && commits_count.to_i > 0
+ merge_request_diff.persisted? && commits_count.to_i > 0
end
def has_no_commits?
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 9e1393196ff..7cdbb124dee 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -19,9 +19,9 @@ class IssueTrackerService < Service
# overridden patterns. See ReferenceRegexes.external_pattern
def self.reference_pattern(only_long: false)
if only_long
- /(\b[A-Z][A-Z0-9_]*-)(?<issue>\d+)/
+ /(\b[A-Z][A-Z0-9_]*-)#{Gitlab::Regex.issue}/
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
diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb
index 02d06eeb405..0815e27850d 100644
--- a/app/models/project_services/youtrack_service.rb
+++ b/app/models/project_services/youtrack_service.rb
@@ -8,7 +8,7 @@ class YoutrackService < IssueTrackerService
if only_long
/(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)/
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
diff --git a/changelogs/unreleased/feat-drop-signatures-email-replies.yml b/changelogs/unreleased/feat-drop-signatures-email-replies.yml
new file mode 100644
index 00000000000..1a77059c8fa
--- /dev/null
+++ b/changelogs/unreleased/feat-drop-signatures-email-replies.yml
@@ -0,0 +1,5 @@
+---
+title: Drop signatures in email replies
+merge_request: 25389
+author: Diego Louzán
+type: changed
diff --git a/changelogs/unreleased/pages-1-16-release.yml b/changelogs/unreleased/pages-1-16-release.yml
new file mode 100644
index 00000000000..03dd2cd964d
--- /dev/null
+++ b/changelogs/unreleased/pages-1-16-release.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade pages to 1.16.0
+merge_request: 25238
+author:
+type: added
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index 9ec0a8e803f..f39151cfa59 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -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)
- [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)
+- [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
diff --git a/lib/gitlab/email/attachment_uploader.rb b/lib/gitlab/email/attachment_uploader.rb
index 0a14a909e31..d8962ec0d20 100644
--- a/lib/gitlab/email/attachment_uploader.rb
+++ b/lib/gitlab/email/attachment_uploader.rb
@@ -12,7 +12,7 @@ module Gitlab
def execute(upload_parent:, uploader_class:)
attachments = []
- message.attachments.each do |attachment|
+ filter_signature_attachments(message).each do |attachment|
tmp = Tempfile.new("gitlab-email-attachment")
begin
File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded }
@@ -32,6 +32,22 @@ module Gitlab
attachments
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
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index fd6e24a96d8..38281fb1c91 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -112,7 +112,7 @@ module Gitlab
# Based on Jira's project key format
# https://confluence.atlassian.com/adminjiraserver073/changing-the-project-key-format-861253229.html
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
def jira_transition_id_regex
@@ -144,6 +144,10 @@ module Gitlab
def utc_date_regex
@utc_date_regex ||= /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z/.freeze
end
+
+ def issue
+ @issue ||= /(?<issue>\d+\b)/
+ end
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index da26eb94fb0..3684571ff9c 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -77,6 +77,18 @@ describe Projects::MergeRequestsController do
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
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 590578aec9a..296215abfa0 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -141,11 +141,11 @@ FactoryBot.define do
trait :lsif do
file_type { :lsif }
- file_format { :raw }
+ file_format { :gzip }
after(:build) do |artifact, evaluator|
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
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index eb55613b954..7584789b99d 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -53,7 +53,7 @@ describe 'User creates snippet', :js do
page.within('#new_personal_snippet .md-preview-holder') do
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})
# 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
click_button('Create snippet')
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})
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
@@ -98,7 +98,7 @@ describe 'User creates snippet', :js do
expect(page).to have_selector('strong')
end
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})
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
diff --git a/spec/fixtures/emails/.gitattributes b/spec/fixtures/emails/.gitattributes
new file mode 100644
index 00000000000..6c6ba98a0dd
--- /dev/null
+++ b/spec/fixtures/emails/.gitattributes
@@ -0,0 +1,2 @@
+# Do not mangle line endings or signature will be invalid
+valid_reply_signed_smime.eml eol=crlf \ No newline at end of file
diff --git a/spec/fixtures/emails/valid_reply_signed_smime.eml b/spec/fixtures/emails/valid_reply_signed_smime.eml
new file mode 100644
index 00000000000..0c5e2c439ae
--- /dev/null
+++ b/spec/fixtures/emails/valid_reply_signed_smime.eml
@@ -0,0 +1,294 @@
+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--
diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb
index c69b2f1eabc..462be76a58d 100644
--- a/spec/lib/gitlab/email/attachment_uploader_spec.rb
+++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb
@@ -16,5 +16,20 @@ describe Gitlab::Email::AttachmentUploader do
expect(link[:alt]).to eq("bricks")
expect(link[:url]).to include("bricks.png")
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
diff --git a/spec/lib/gitlab/email/handler_spec.rb b/spec/lib/gitlab/email/handler_spec.rb
index 5229b778ccf..5014e4c22ce 100644
--- a/spec/lib/gitlab/email/handler_spec.rb
+++ b/spec/lib/gitlab/email/handler_spec.rb
@@ -3,17 +3,23 @@
require 'spec_helper'
describe Gitlab::Email::Handler do
+ let(:email) { Mail.new { body 'email' } }
+
describe '.for' 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
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
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
@@ -25,7 +31,7 @@ describe Gitlab::Email::Handler do
it 'picks each handler at least once' do
matched_handlers = addresses.map do |address|
- described_class.for('email', address).class
+ described_class.for(email, address).class
end
expect(matched_handlers.uniq).to match_array(ce_handlers)
@@ -34,7 +40,7 @@ describe Gitlab::Email::Handler do
it 'can pick exactly one handler for each address' do
addresses.each do |address|
matched_handlers = ce_handlers.select do |handler|
- handler.new('email', address).can_handle?
+ handler.new(email, address).can_handle?
end
expect(matched_handlers.count).to eq(1), "#{address} matches #{matched_handlers.count} handlers: #{matched_handlers}"
diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
index ab834ac3fa8..0cdb3c43992 100644
--- a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
+++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
@@ -52,10 +52,10 @@ describe Gitlab::ImportExport::MergeRequestParser do
context 'when the diff is invalid' do
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.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
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 36fd5d21e73..14f50ffd689 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -662,13 +662,12 @@ describe MergeRequest do
end
describe '#raw_diffs' do
- let(:merge_request) { build(:merge_request) }
let(:options) { { paths: ['a/b', 'b/a', 'c/*'] } }
context 'when there are MR diffs' do
- it 'delegates to the MR diffs' do
- merge_request.merge_request_diff = MergeRequestDiff.new
+ let(:merge_request) { create(:merge_request, :with_diffs) }
+ it 'delegates to the MR diffs' do
expect(merge_request.merge_request_diff).to receive(:raw_diffs).with(options)
merge_request.raw_diffs(options)
@@ -676,6 +675,8 @@ describe MergeRequest do
end
context 'when there are no MR diffs' do
+ let(:merge_request) { build(:merge_request) }
+
it 'delegates to the compare object' do
merge_request.compare = double(:compare)
diff --git a/spec/models/project_services/youtrack_service_spec.rb b/spec/models/project_services/youtrack_service_spec.rb
index 0067793f8d8..b8fff635e99 100644
--- a/spec/models/project_services/youtrack_service_spec.rb
+++ b/spec/models/project_services/youtrack_service_spec.rb
@@ -38,8 +38,8 @@ describe YoutrackService do
expect(described_class.reference_pattern.match('YT-123')[:issue]).to eq('YT-123')
end
- it 'does not allow issue number to be followed by a letter' do
- expect(described_class.reference_pattern.match('YT-123A')).to eq(nil)
+ it 'allows lowercase project key on the reference' do
+ expect(described_class.reference_pattern.match('yt-123')[:issue]).to eq('yt-123')
end
end
diff --git a/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb b/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
index 0a483fd30ba..b275d594792 100644
--- a/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
+++ b/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
@@ -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('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
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