Commit bff7c4b1 authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-08-29' into 'master'

CE upstream - 2018-08-29 20:05 UTC

Closes #2695 and gitlab-com/migration#766

See merge request gitlab-org/gitlab-ee!7053
parents b0c431c0 72a5e401
...@@ -276,6 +276,7 @@ entry. ...@@ -276,6 +276,7 @@ entry.
## 11.1.5 (2018-08-27) ## 11.1.5 (2018-08-27)
- No changes. - No changes.
### Security (3 changes) ### Security (3 changes)
- Fixed persistent XSS rendering/escaping of diff location lines. - Fixed persistent XSS rendering/escaping of diff location lines.
......
import Vue from 'vue'; import Vue from 'vue';
import Flash from '../flash';
import PerformanceBarService from './services/performance_bar_service'; import PerformanceBarService from './services/performance_bar_service';
import PerformanceBarStore from './stores/performance_bar_store'; import PerformanceBarStore from './stores/performance_bar_store';
...@@ -46,7 +45,8 @@ export default ({ container }) => ...@@ -46,7 +45,8 @@ export default ({ container }) =>
this.store.addRequestDetails(requestId, res.data.data); this.store.addRequestDetails(requestId, res.data.data);
}) })
.catch(() => .catch(() =>
Flash(`Error getting performance bar results for ${requestId}`), // eslint-disable-next-line no-console
console.warn(`Error getting performance bar results for ${requestId}`),
); );
}, },
}, },
......
...@@ -11,13 +11,10 @@ export default class PerformanceBarService { ...@@ -11,13 +11,10 @@ export default class PerformanceBarService {
static registerInterceptor(peekUrl, callback) { static registerInterceptor(peekUrl, callback) {
const interceptor = response => { const interceptor = response => {
const requestId = response.headers['x-request-id']; const [fireCallback, requestId, requestUrl] =
// Get the request URL from response.config for Axios, and response for PerformanceBarService.callbackParams(response, peekUrl);
// Vue Resource.
const requestUrl = (response.config || response).url;
const cachedResponse = response.headers['x-gitlab-from-cache'] === 'true';
if (requestUrl !== peekUrl && requestId && !cachedResponse) { if (fireCallback) {
callback(requestId, requestUrl); callback(requestId, requestUrl);
} }
...@@ -38,4 +35,16 @@ export default class PerformanceBarService { ...@@ -38,4 +35,16 @@ export default class PerformanceBarService {
vueResourceInterceptor, vueResourceInterceptor,
); );
} }
static callbackParams(response, peekUrl) {
const requestId = response.headers && response.headers['x-request-id'];
// Get the request URL from response.config for Axios, and response for
// Vue Resource.
const requestUrl = (response.config || response).url;
const apiRequest = requestUrl && requestUrl.match(/^\/api\//);
const cachedResponse = response.headers && response.headers['x-gitlab-from-cache'] === 'true';
const fireCallback = requestUrl !== peekUrl && requestId && !apiRequest && !cachedResponse;
return [fireCallback, requestId, requestUrl];
}
} }
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore';
import JobNameComponent from './job_name_component.vue'; import JobNameComponent from './job_name_component.vue';
import JobComponent from './job_component.vue'; import JobComponent from './job_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
...@@ -47,7 +46,7 @@ export default { ...@@ -47,7 +46,7 @@ export default {
computed: { computed: {
tooltipText() { tooltipText() {
return _.escape(`${this.job.name} - ${this.job.status.label}`); return `${this.job.name} - ${this.job.status.label}`;
}, },
}, },
......
<script> <script>
import _ from 'underscore';
import ActionComponent from './action_component.vue'; import ActionComponent from './action_component.vue';
import JobNameComponent from './job_name_component.vue'; import JobNameComponent from './job_name_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
...@@ -62,7 +61,7 @@ export default { ...@@ -62,7 +61,7 @@ export default {
const textBuilder = []; const textBuilder = [];
if (this.job.name) { if (this.job.name) {
textBuilder.push(_.escape(this.job.name)); textBuilder.push(this.job.name);
} }
if (this.job.name && this.status.tooltip) { if (this.job.name && this.status.tooltip) {
...@@ -106,7 +105,6 @@ export default { ...@@ -106,7 +105,6 @@ export default {
:class="cssClassJobName" :class="cssClassJobName"
:data-boundary="tooltipBoundary" :data-boundary="tooltipBoundary"
data-container="body" data-container="body"
data-html="true"
class="js-pipeline-graph-job-link" class="js-pipeline-graph-job-link"
> >
...@@ -122,7 +120,6 @@ export default { ...@@ -122,7 +120,6 @@ export default {
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
class="js-job-component-tooltip non-details-job-component" class="js-job-component-tooltip non-details-job-component"
data-html="true"
data-container="body" data-container="body"
> >
......
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
- tooltip = "#{subject.name} - #{status.status_tooltip}" - tooltip = "#{subject.name} - #{status.status_tooltip}"
- if status.has_details? - if status.has_details?
= link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, html: 'true', container: 'body' } do = link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
%span{ class: klass }= sprite_icon(status.icon) %span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name %span.ci-build-text= subject.name
- else - else
.menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' } } .menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } }
%span{ class: klass }= sprite_icon(status.icon) %span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name %span.ci-build-text= subject.name
......
...@@ -32,9 +32,9 @@ ...@@ -32,9 +32,9 @@
%a{ href: "##{line_code}", data: { linenumber: link_text } } %a{ href: "##{line_code}", data: { linenumber: link_text } }
%td.line_content.noteable_line{ class: type }< %td.line_content.noteable_line{ class: type }<
- if email - if email
%pre= line.text %pre= line.rich_text
- else - else
= diff_line_content(line.text) = diff_line_content(line.rich_text)
- if line_discussions&.any? - if line_discussions&.any?
- discussion_expanded = local_assigns.fetch(:discussion_expanded, line_discussions.any?(&:expanded?)) - discussion_expanded = local_assigns.fetch(:discussion_expanded, line_discussions.any?(&:expanded?))
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
- discussion_left = discussions_left.try(:first) - discussion_left = discussions_left.try(:first)
- if discussion_left && discussion_left.resolvable? - if discussion_left && discussion_left.resolvable?
%diff-note-avatars{ "discussion-id" => discussion_left.id } %diff-note-avatars{ "discussion-id" => discussion_left.id }
%td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.text) %td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.rich_text)
- else - else
%td.old_line.diff-line-num.empty-cell %td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.left-side %td.line_content.parallel.left-side
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
- discussion_right = discussions_right.try(:first) - discussion_right = discussions_right.try(:first)
- if discussion_right && discussion_right.resolvable? - if discussion_right && discussion_right.resolvable?
%diff-note-avatars{ "discussion-id" => discussion_right.id } %diff-note-avatars{ "discussion-id" => discussion_right.id }
%td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.text) %td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.rich_text)
- else - else
%td.old_line.diff-line-num.empty-cell %td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.right-side %td.line_content.parallel.right-side
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
- builds.select{|build| build.status == build_status}.each do |build| - builds.select{|build| build.status == build_status}.each do |build|
.build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } } .build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
- tooltip = sanitize(build.tooltip_message.dup) - tooltip = sanitize(build.tooltip_message.dup)
= link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' }) do = link_to(project_job_path(@project, build), data: { toggle: 'tooltip', title: tooltip, container: 'body' }) do
= sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right') = sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
%span{ class: "ci-status-icon-#{build.status}" } %span{ class: "ci-status-icon-#{build.status}" }
= ci_icon_for_status(build.status) = ci_icon_for_status(build.status)
......
---
title: Don't show flash messages for performance bar errors
merge_request: 21411
author:
type: other
---
title: 'Rails 5: replace removed silence_stream'
merge_request: 21387
author: Jasper Maes
type: other
---
title: Fixed persistent XSS rendering/escaping of diff location lines
merge_request:
author:
type: security
---
title: Block link-local addresses in URLBlocker
merge_request:
author:
type: security
...@@ -30,7 +30,7 @@ The `gitlab` chart includes all required dependencies, and takes a few minutes ...@@ -30,7 +30,7 @@ The `gitlab` chart includes all required dependencies, and takes a few minutes
to deploy. to deploy.
TIP: **Tip:** TIP: **Tip:**
For large scale deployments, we strongly recommend using the For production deployments, we strongly recommend using the
[detailed installation instructions](https://gitlab.com/charts/gitlab/blob/master/doc/installation/README.md) [detailed installation instructions](https://gitlab.com/charts/gitlab/blob/master/doc/installation/README.md)
utilizing [external Postgres, Redis, and object storage](https://gitlab.com/charts/gitlab/tree/master/doc/advanced) services. utilizing [external Postgres, Redis, and object storage](https://gitlab.com/charts/gitlab/tree/master/doc/advanced) services.
......
...@@ -34,7 +34,7 @@ module Gitlab ...@@ -34,7 +34,7 @@ module Gitlab
end end
def description def description
"<br> (#{failure_reason_message})" "- (#{failure_reason_message})"
end end
def failure_reason_message def failure_reason_message
......
...@@ -37,7 +37,7 @@ module Gitlab ...@@ -37,7 +37,7 @@ module Gitlab
end end
end end
diff_line.text = rich_line diff_line.rich_text = rich_line
diff_line diff_line
end end
......
module Gitlab module Gitlab
module Diff module Diff
class Line class Line
SERIALIZE_KEYS = %i(line_code text type index old_pos new_pos).freeze SERIALIZE_KEYS = %i(line_code rich_text text type index old_pos new_pos).freeze
attr_reader :line_code, :type, :index, :old_pos, :new_pos attr_reader :line_code, :type, :index, :old_pos, :new_pos
attr_writer :rich_text attr_writer :rich_text
attr_accessor :text attr_accessor :text
def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil) def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil, rich_text: nil)
@text, @type, @index = text, type, index @text, @type, @index = text, type, index
@old_pos, @new_pos = old_pos, new_pos @old_pos, @new_pos = old_pos, new_pos
@parent_file = parent_file @parent_file = parent_file
@rich_text = rich_text
# When line code is not provided from cache store we build it # When line code is not provided from cache store we build it
# using the parent_file(Diff::File or Conflict::File). # using the parent_file(Diff::File or Conflict::File).
...@@ -18,7 +19,7 @@ module Gitlab ...@@ -18,7 +19,7 @@ module Gitlab
end end
def self.init_from_hash(hash) def self.init_from_hash(hash)
new(hash[:text], hash[:type], hash[:index], hash[:old_pos], hash[:new_pos], line_code: hash[:line_code]) new(hash[:text], hash[:type], hash[:index], hash[:old_pos], hash[:new_pos], line_code: hash[:line_code], rich_text: hash[:rich_text])
end end
def to_hash def to_hash
...@@ -85,7 +86,7 @@ module Gitlab ...@@ -85,7 +86,7 @@ module Gitlab
old_line: old_line, old_line: old_line,
new_line: new_line, new_line: new_line,
text: text, text: text,
rich_text: rich_text || text, rich_text: rich_text || CGI.escapeHTML(text),
meta_data: meta_positions meta_data: meta_positions
} }
end end
......
...@@ -13,13 +13,11 @@ module Gitlab ...@@ -13,13 +13,11 @@ module Gitlab
end end
def save def save
copy_files(@from, uploads_export_path) if File.directory?(@from)
if File.file?(@from) && @relative_export_path == 'avatar' if File.file?(@from) && @relative_export_path == 'avatar'
copy_files(@from, File.join(uploads_export_path, @project.avatar.filename)) copy_files(@from, File.join(uploads_export_path, @project.avatar.filename))
end end
copy_from_object_storage copy_project_uploads
true true
rescue => e rescue => e
...@@ -48,14 +46,19 @@ module Gitlab ...@@ -48,14 +46,19 @@ module Gitlab
UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute
end end
def copy_from_object_storage def copy_project_uploads
return unless Gitlab::ImportExport.object_storage?
each_uploader do |uploader| each_uploader do |uploader|
next unless uploader.file next unless uploader.file
next if uploader.upload.local? # Already copied, using the old method
download_and_copy(uploader) if uploader.upload.local?
next unless uploader.upload.exist?
copy_files(uploader.absolute_path, File.join(uploads_export_path, uploader.upload.path))
else
next unless Gitlab::ImportExport.object_storage?
download_and_copy(uploader)
end
end end
end end
......
...@@ -31,6 +31,7 @@ module Gitlab ...@@ -31,6 +31,7 @@ module Gitlab
validate_localhost!(addrs_info) unless allow_localhost validate_localhost!(addrs_info) unless allow_localhost
validate_local_network!(addrs_info) unless allow_local_network validate_local_network!(addrs_info) unless allow_local_network
validate_link_local!(addrs_info) unless allow_local_network
true true
end end
...@@ -89,6 +90,13 @@ module Gitlab ...@@ -89,6 +90,13 @@ module Gitlab
raise BlockedUrlError, "Requests to the local network are not allowed" raise BlockedUrlError, "Requests to the local network are not allowed"
end end
def validate_link_local!(addrs_info)
netmask = IPAddr.new('169.254.0.0/16')
return unless addrs_info.any? { |addr| addr.ipv6_linklocal? || netmask.include?(addr.ip_address) }
raise BlockedUrlError, "Requests to the link local network are not allowed"
end
def internal?(uri) def internal?(uri)
internal_web?(uri) || internal_shell?(uri) internal_web?(uri) || internal_shell?(uri)
end end
......
...@@ -82,7 +82,7 @@ namespace :gettext do ...@@ -82,7 +82,7 @@ namespace :gettext do
# `gettext:find` writes touches to temp files to `stderr` which would cause # `gettext:find` writes touches to temp files to `stderr` which would cause
# `static-analysis` to report failures. We can ignore these. # `static-analysis` to report failures. We can ignore these.
silence_stream($stderr) do silence_sdterr do
Rake::Task['gettext:find'].invoke Rake::Task['gettext:find'].invoke
end end
...@@ -118,4 +118,15 @@ namespace :gettext do ...@@ -118,4 +118,15 @@ namespace :gettext do
end end
end end
end end
def silence_sdterr(&block)
old_stderr = $stderr.dup
$stderr.reopen(File::NULL)
$stderr.sync = true
yield
ensure
$stderr.reopen(old_stderr)
old_stderr.close
end
end end
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
context :create do context :create, :core do
describe 'Files management' do describe 'Files management' do
it 'user creates, edits and deletes a file via the Web' do it 'user creates, edits and deletes a file via the Web' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
......
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
context :verify, :docker do context :verify, :orchestrated, :docker do
describe 'Pipeline creation and processing' do describe 'Pipeline creation and processing' do
let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:executor) { "qa-runner-#{Time.now.to_i}" }
......
...@@ -62,20 +62,20 @@ module QA ...@@ -62,20 +62,20 @@ module QA
end end
gitlab_ci = <<~YAML gitlab_ci = <<~YAML
cat-config: cat-config:
script: script:
- mkdir -p ~/.ssh - mkdir -p ~/.ssh
- ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts - ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts
- eval $(ssh-agent -s) - eval $(ssh-agent -s)
- ssh-add -D - ssh-add -D
- echo "$#{deploy_key_name}" | ssh-add - - echo "$#{deploy_key_name}" | ssh-add -
- git clone #{@repository_location.git_uri} - git clone #{@repository_location.git_uri}
- cd #{@project.name} - cd #{@project.name}
- git checkout #{deploy_key_name} - git checkout #{deploy_key_name}
- sha1sum .gitlab-ci.yml - sha1sum .gitlab-ci.yml
tags: tags:
- qa - qa
- docker - docker
YAML YAML
Factory::Repository::ProjectPush.fabricate! do |resource| Factory::Repository::ProjectPush.fabricate! do |resource|
......
...@@ -2,6 +2,7 @@ require 'rails_helper' ...@@ -2,6 +2,7 @@ require 'rails_helper'
describe 'Merge request > User sees diff', :js do describe 'Merge request > User sees diff', :js do
include ProjectForksHelper include ProjectForksHelper
include RepoHelpers
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
...@@ -81,5 +82,58 @@ describe 'Merge request > User sees diff', :js do ...@@ -81,5 +82,58 @@ describe 'Merge request > User sees diff', :js do
expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1) expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1)
end end
end end
context 'when file contains html' do
let(:current_user) { project.owner }
let(:branch_name) {"test_branch"}
def create_file(branch_name, file_name, content)
Files::CreateService.new(
project,
current_user,
start_branch: branch_name,
branch_name: branch_name,
commit_message: "Create file",
file_path: file_name,
file_content: content
).execute
project.commit(branch_name)
end
it 'escapes any HTML special characters in the diff chunk header' do
file_content =
<<~CONTENT
function foo<input> {
let a = 1;
let b = 2;
let c = 3;
let d = 3;
}
CONTENT
new_file_content =
<<~CONTENT
function foo<input> {
let a = 1;
let b = 2;
let c = 3;
let x = 3;
}
CONTENT
file_name = 'xss_file.txt'
create_file('master', file_name, file_content)
merge_request = create(:merge_request, source_project: project)
create_file(merge_request.source_branch, file_name, new_file_content)
project.commit(merge_request.source_branch)
visit diffs_project_merge_request_path(project, merge_request)
expect(page).to have_text("function foo<input> {")
end
end
end end
end end
...@@ -40,7 +40,7 @@ describe 'User browses a job', :js do ...@@ -40,7 +40,7 @@ describe 'User browses a job', :js do
it 'displays the failure reason' do it 'displays the failure reason' do
within('.builds-container') do within('.builds-container') do
build_link = first('.build-job > a') build_link = first('.build-job > a')
expect(build_link['data-title']).to eq('test - failed <br> (unknown failure)') expect(build_link['data-title']).to eq('test - failed - (unknown failure)')
end end
end end
end end
...@@ -51,7 +51,7 @@ describe 'User browses a job', :js do ...@@ -51,7 +51,7 @@ describe 'User browses a job', :js do
it 'displays the failure reason and retried label' do it 'displays the failure reason and retried label' do
within('.builds-container') do within('.builds-container') do
build_link = first('.build-job > a') build_link = first('.build-job > a')
expect(build_link['data-title']).to eq('test - failed <br> (unknown failure) (retried)') expect(build_link['data-title']).to eq('test - failed - (unknown failure) (retried)')
end end
end end
end end
......
...@@ -36,7 +36,7 @@ describe 'User browses jobs' do ...@@ -36,7 +36,7 @@ describe 'User browses jobs' do
it 'displays a tooltip with the failure reason' do it 'displays a tooltip with the failure reason' do
page.within('.ci-table') do page.within('.ci-table') do
failed_job_link = page.find('.ci-failed') failed_job_link = page.find('.ci-failed')
expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)') expect(failed_job_link[:title]).to eq('Failed - (unknown failure)')
end end
end end
end end
......
...@@ -126,7 +126,7 @@ describe 'Pipeline', :js do ...@@ -126,7 +126,7 @@ describe 'Pipeline', :js do
it 'should include the failure reason' do it 'should include the failure reason' do
page.within('#ci-badge-test') do page.within('#ci-badge-test') do
build_link = page.find('.js-pipeline-graph-job-link') build_link = page.find('.js-pipeline-graph-job-link')
expect(build_link['data-original-title']).to eq('test - failed <br> (unknown failure)') expect(build_link['data-original-title']).to eq('test - failed - (unknown failure)')
end end
end end
end end
...@@ -319,7 +319,7 @@ describe 'Pipeline', :js do ...@@ -319,7 +319,7 @@ describe 'Pipeline', :js do
it 'displays a tooltip with the failure reason' do it 'displays a tooltip with the failure reason' do
page.within('.ci-table') do page.within('.ci-table') do
failed_job_link = page.find('.ci-failed') failed_job_link = page.find('.ci-failed')
expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)') expect(failed_job_link[:title]).to eq('Failed - (unknown failure)')
end end
end end
end end
......
...@@ -406,7 +406,7 @@ describe 'Pipelines', :js do ...@@ -406,7 +406,7 @@ describe 'Pipelines', :js do
within('.js-builds-dropdown-list') do within('.js-builds-dropdown-list') do
build_element = page.find('.mini-pipeline-graph-dropdown-item') build_element = page.find('.mini-pipeline-graph-dropdown-item')
expect(build_element['data-original-title']).to eq('build - failed <br> (unknown failure)') expect(build_element['data-original-title']).to eq('build - failed - (unknown failure)')
end end
end end
end end
......
import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
describe('PerformanceBarService', () => {
describe('callbackParams', () => {
describe('fireCallback', () => {
function fireCallback(response, peekUrl) {
return PerformanceBarService.callbackParams(response, peekUrl)[0];
}
it('returns false when the request URL is the peek URL', () => {
expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/peek' }, '/peek'))
.toBeFalsy();
});
it('returns false when there is no request ID', () => {
expect(fireCallback({ headers: {}, url: '/request' }, '/peek'))
.toBeFalsy();
});
it('returns false when the request is an API request', () => {
expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/api/' }, '/peek'))
.toBeFalsy();
});
it('returns false when the response is from the cache', () => {
expect(fireCallback({ headers: { 'x-request-id': '123', 'x-gitlab-from-cache': 'true' }, url: '/request' }, '/peek'))
.toBeFalsy();
});
it('returns true when all conditions are met', () => {
expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/request' }, '/peek'))
.toBeTruthy();
});
});
describe('requestId', () => {
function requestId(response, peekUrl) {
return PerformanceBarService.callbackParams(response, peekUrl)[1];
}
it('gets the request ID from the headers', () => {
expect(requestId({ headers: { 'x-request-id': '123' } }, '/peek'))
.toEqual('123');
});
});
describe('requestUrl', () => {
function requestUrl(response, peekUrl) {
return PerformanceBarService.callbackParams(response, peekUrl)[2];
}
it('gets the request URL from the response object', () => {
expect(requestUrl({ headers: {}, url: '/request' }, '/peek'))
.toEqual('/request');
});
it('gets the request URL from response.config if present', () => {
expect(requestUrl({ headers: {}, config: { url: '/config-url' }, url: '/request' }, '/peek'))
.toEqual('/config-url');
});
});
});
});
...@@ -82,12 +82,4 @@ describe('dropdown job component', () => { ...@@ -82,12 +82,4 @@ describe('dropdown job component', () => {
it('renders dropdown with jobs', () => { it('renders dropdown with jobs', () => {
expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length); expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length);
}); });
it('escapes tooltip title', () => {
expect(
vm.$el.querySelector('.js-pipeline-graph-job-link').getAttribute('data-original-title'),
).toEqual(
'&lt;img src=x onerror=alert(document.domain)&gt; - passed',
);
});
}); });
...@@ -161,24 +161,4 @@ describe('pipeline graph job component', () => { ...@@ -161,24 +161,4 @@ describe('pipeline graph job component', () => {
expect(component.$el.querySelector(tooltipBoundary)).toBeNull(); expect(component.$el.querySelector(tooltipBoundary)).toBeNull();
}); });
}); });
describe('tooltipText', () => {
it('escapes job name', () => {
component = mountComponent(JobComponent, {
job: {
id: 4259,
name: '<img src=x onerror=alert(document.domain)>',
status: {
icon: 'status_success',
label: 'success',
tooltip: 'failed',
},
},
});
expect(
component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title'),
).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; - failed');
});
});
}); });
...@@ -88,7 +88,7 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -88,7 +88,7 @@ describe Gitlab::Ci::Status::Build::Factory do
expect(status.icon).to eq 'status_failed' expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed' expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq 'failed' expect(status.label).to eq 'failed'
expect(status.status_tooltip).to eq 'failed <br> (unknown failure)' expect(status.status_tooltip).to eq 'failed - (unknown failure)'
expect(status).to have_details expect(status).to have_details
expect(status).to have_action expect(status).to have_action
end end
......
...@@ -76,7 +76,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do ...@@ -76,7 +76,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
let(:status) { described_class.new(build_status) } let(:status) { described_class.new(build_status) }
it 'does override badge_tooltip' do it 'does override badge_tooltip' do
expect(status.badge_tooltip).to eq('failed <br> (unknown failure)') expect(status.badge_tooltip).to eq('failed - (unknown failure)')
end end
end end
...@@ -87,7 +87,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do ...@@ -87,7 +87,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
let(:status) { described_class.new(build_status) } let(:status) { described_class.new(build_status) }
it 'does override status_tooltip' do it 'does override status_tooltip' do
expect(status.status_tooltip).to eq 'failed <br> (unknown failure) (allowed to fail)' expect(status.status_tooltip).to eq 'failed - (unknown failure) (allowed to fail)'
end end
end end
......
...@@ -52,7 +52,7 @@ describe Gitlab::Ci::Status::Build::Failed do ...@@ -52,7 +52,7 @@ describe Gitlab::Ci::Status::Build::Failed do
let(:status) { Gitlab::Ci::Status::Failed.new(build, user) } let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
it 'does override badge_tooltip' do it 'does override badge_tooltip' do
expect(subject.badge_tooltip).to eq 'failed <br> (script failure)' expect(subject.badge_tooltip).to eq 'failed - (script failure)'
end end
end end
...@@ -61,7 +61,7 @@ describe Gitlab::Ci::Status::Build::Failed do ...@@ -61,7 +61,7 @@ describe Gitlab::Ci::Status::Build::Failed do
let(:status) { Gitlab::Ci::Status::Failed.new(build, user) } let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
it 'does override status_tooltip' do it 'does override status_tooltip' do
expect(subject.status_tooltip).to eq 'failed <br> (script failure)' expect(subject.status_tooltip).to eq 'failed - (script failure)'
end end
end end
......
...@@ -66,7 +66,7 @@ describe Gitlab::Ci::Status::Build::Retried do ...@@ -66,7 +66,7 @@ describe Gitlab::Ci::Status::Build::Retried do
let(:status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) } let(:status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
it 'does override status_tooltip' do it 'does override status_tooltip' do
expect(subject.status_tooltip).to eq 'failed <br> (unknown failure) (retried)' expect(subject.status_tooltip).to eq 'failed - (unknown failure) (retried)'
end end
end end
......
...@@ -69,10 +69,6 @@ describe Gitlab::Conflict::File do ...@@ -69,10 +69,6 @@ describe Gitlab::Conflict::File do
CGI.unescapeHTML(ActionView::Base.full_sanitizer.sanitize(html)).delete("\n") CGI.unescapeHTML(ActionView::Base.full_sanitizer.sanitize(html)).delete("\n")
end end
it 'modifies the existing lines' do
expect { conflict_file.highlight_lines! }.to change { conflict_file.lines.map(&:instance_variables) }
end
it 'is called implicitly when rich_text is accessed on a line' do it 'is called implicitly when rich_text is accessed on a line' do
expect(conflict_file).to receive(:highlight_lines!).once.and_call_original expect(conflict_file).to receive(:highlight_lines!).once.and_call_original
......
...@@ -24,19 +24,19 @@ describe Gitlab::Diff::Highlight do ...@@ -24,19 +24,19 @@ describe Gitlab::Diff::Highlight do
it 'highlights and marks unchanged lines' do it 'highlights and marks unchanged lines' do
code = %Q{ <span id="LC7" class="line" lang="ruby"> <span class="k">def</span> <span class="nf">popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></span>\n} code = %Q{ <span id="LC7" class="line" lang="ruby"> <span class="k">def</span> <span class="nf">popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></span>\n}
expect(subject[2].text).to eq(code) expect(subject[2].rich_text).to eq(code)
end end
it 'highlights and marks removed lines' do it 'highlights and marks removed lines' do
code = %Q{-<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n} code = %Q{-<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[4].text).to eq(code) expect(subject[4].rich_text).to eq(code)
end end
it 'highlights and marks added lines' do it 'highlights and marks added lines' do
code = %Q{+<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="no"><span class="idiff left">RuntimeError</span></span><span class="p"><span class="idiff">,</span></span><span class="idiff right"> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n} code = %Q{+<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="no"><span class="idiff left">RuntimeError</span></span><span class="p"><span class="idiff">,</span></span><span class="idiff right"> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[5].text).to eq(code) expect(subject[5].rich_text).to eq(code)
end end
end end
...@@ -69,8 +69,8 @@ describe Gitlab::Diff::Highlight do ...@@ -69,8 +69,8 @@ describe Gitlab::Diff::Highlight do
it 'marks added lines' do it 'marks added lines' do
code = %q{+ raise <span class="idiff left right">RuntimeError, </span>&quot;System commands must be given as an array of strings&quot;} code = %q{+ raise <span class="idiff left right">RuntimeError, </span>&quot;System commands must be given as an array of strings&quot;}
expect(subject[5].text).to eq(code) expect(subject[5].rich_text).to eq(code)
expect(subject[5].text).to be_html_safe expect(subject[5].rich_text).to be_html_safe
end end
context 'when the inline diff marker has an invalid range' do context 'when the inline diff marker has an invalid range' do
......
describe Gitlab::Diff::Line do
describe '.init_from_hash' do
it 'round-trips correctly with to_hash' do
line = described_class.new('<input>', 'match', 0, 0, 1,
parent_file: double(:file),
line_code: double(:line_code),
rich_text: '&lt;input&gt;')
expect(described_class.init_from_hash(line.to_hash).to_hash)
.to eq(line.to_hash)
end
end
context "when setting rich text" do
it 'escapes any HTML special characters in the diff chunk header' do
subject = described_class.new("<input>", "", 0, 0, 0)
line = subject.as_json
expect(line[:rich_text]).to eq("&lt;input&gt;")
end
end
end
...@@ -36,6 +36,38 @@ describe Gitlab::ImportExport::UploadsManager do ...@@ -36,6 +36,38 @@ describe Gitlab::ImportExport::UploadsManager do
expect(File).to exist(exported_file_path) expect(File).to exist(exported_file_path)
end end
context 'with orphaned project upload files' do
let(:orphan_path) { File.join(FileUploader.absolute_base_dir(project), 'f93f088ddf492ffd950cf059002cbbb6', 'orphan.jpg') }
let(:exported_orphan_path) { "#{shared.export_path}/uploads/f93f088ddf492ffd950cf059002cbbb6/orphan.jpg" }
before do
FileUtils.mkdir_p(File.dirname(orphan_path))
FileUtils.touch(orphan_path)
end
after do
File.delete(orphan_path) if File.exist?(orphan_path)
end
it 'excludes orphaned upload files' do
manager.save
expect(File).not_to exist(exported_orphan_path)
end
end
context 'with an upload missing its file' do
before do
File.delete(upload.absolute_path)
end
it 'does not cause errors' do
manager.save
expect(shared.errors).to be_empty
end
end
end end
context 'using object storage' do context 'using object storage' do
......
# coding: utf-8
require 'spec_helper' require 'spec_helper'
describe Gitlab::UrlBlocker do describe Gitlab::UrlBlocker do
...@@ -82,6 +83,17 @@ describe Gitlab::UrlBlocker do ...@@ -82,6 +83,17 @@ describe Gitlab::UrlBlocker do
expect(described_class).not_to be_blocked_url("http://#{ip}") expect(described_class).not_to be_blocked_url("http://#{ip}")
end end
end end
it 'allows IPv4 link-local endpoints' do
expect(described_class).not_to be_blocked_url('http://169.254.169.254')
expect(described_class).not_to be_blocked_url('http://169.254.168.100')
end
# This is blocked due to the hostname check: https://gitlab.com/gitlab-org/gitlab-ce/issues/50227
it 'blocks IPv6 link-local endpoints' do
expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]')
expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]')
end
end end
context 'false' do context 'false' do
...@@ -96,10 +108,21 @@ describe Gitlab::UrlBlocker do ...@@ -96,10 +108,21 @@ describe Gitlab::UrlBlocker do
expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false) expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false)
end end
end end
it 'blocks IPv4 link-local endpoints' do
expect(described_class).to be_blocked_url('http://169.254.169.254', allow_local_network: false)
expect(described_class).to be_blocked_url('http://169.254.168.100', allow_local_network: false)
end
it 'blocks IPv6 link-local endpoints' do
expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]', allow_local_network: false)
expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]', allow_local_network: false)
expect(described_class).to be_blocked_url('http://[FE80::C800:EFF:FE74:8]', allow_local_network: false)
end
end end
def stub_domain_resolv(domain, ip) def stub_domain_resolv(domain, ip)
allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true)]) allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false)])
end end
def unstub_domain_resolv def unstub_domain_resolv
......
...@@ -78,7 +78,7 @@ describe Ci::BuildPresenter do ...@@ -78,7 +78,7 @@ describe Ci::BuildPresenter do
it 'returns the reason of failure' do it 'returns the reason of failure' do
status_title = presenter.status_title status_title = presenter.status_title
expect(status_title).to eq('Failed <br> (unknown failure)') expect(status_title).to eq('Failed - (unknown failure)')
end end
end end
...@@ -89,7 +89,7 @@ describe Ci::BuildPresenter do ...@@ -89,7 +89,7 @@ describe Ci::BuildPresenter do
status_title = presenter.status_title status_title = presenter.status_title
expect(status_title).not_to include('(retried)') expect(status_title).not_to include('(retried)')
expect(status_title).to eq('Failed <br> (unknown failure)') expect(status_title).to eq('Failed - (unknown failure)')
end end
end end
...@@ -99,7 +99,7 @@ describe Ci::BuildPresenter do ...@@ -99,7 +99,7 @@ describe Ci::BuildPresenter do
it 'returns the reason of failure' do it 'returns the reason of failure' do
status_title = presenter.status_title status_title = presenter.status_title
expect(status_title).to eq('Failed <br> (unknown failure)') expect(status_title).to eq('Failed - (unknown failure)')
end end
end end
...@@ -173,7 +173,7 @@ describe Ci::BuildPresenter do ...@@ -173,7 +173,7 @@ describe Ci::BuildPresenter do
it 'returns the reason of failure' do it 'returns the reason of failure' do
tooltip = subject.tooltip_message tooltip = subject.tooltip_message
expect(tooltip).to eq("#{build.name} - failed <br> (script failure)") expect(tooltip).to eq("#{build.name} - failed - (script failure)")
end end
end end
...@@ -183,7 +183,7 @@ describe Ci::BuildPresenter do ...@@ -183,7 +183,7 @@ describe Ci::BuildPresenter do
it 'should include the reason of failure and the retried title' do it 'should include the reason of failure and the retried title' do
tooltip = subject.tooltip_message tooltip = subject.tooltip_message
expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (retried)") expect(tooltip).to eq("#{build.name} - failed - (script failure) (retried)")
end end
end end
...@@ -193,7 +193,7 @@ describe Ci::BuildPresenter do ...@@ -193,7 +193,7 @@ describe Ci::BuildPresenter do
it 'should include the reason of failure' do it 'should include the reason of failure' do
tooltip = subject.tooltip_message tooltip = subject.tooltip_message
expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (allowed to fail)") expect(tooltip).to eq("#{build.name} - failed - (script failure) (allowed to fail)")
end end
end end
......
...@@ -37,7 +37,7 @@ describe BuildSerializer do ...@@ -37,7 +37,7 @@ describe BuildSerializer do
it 'serializes only status' do it 'serializes only status' do
expect(subject[:text]).to eq(status.text) expect(subject[:text]).to eq(status.text)
expect(subject[:label]).to eq('failed') expect(subject[:label]).to eq('failed')
expect(subject[:tooltip]).to eq('failed <br> (unknown failure)') expect(subject[:tooltip]).to eq('failed - (unknown failure)')
expect(subject[:icon]).to eq(status.icon) expect(subject[:icon]).to eq(status.icon)
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png")
end end
......
...@@ -142,7 +142,7 @@ describe JobEntity do ...@@ -142,7 +142,7 @@ describe JobEntity do
end end
it 'should indicate the failure reason on tooltip' do it 'should indicate the failure reason on tooltip' do
expect(subject[:status][:tooltip]).to eq('failed <br> (API failure)') expect(subject[:status][:tooltip]).to eq('failed - (API failure)')
end end
it 'should include a callout message with a verbose output' do it 'should include a callout message with a verbose output' do
...@@ -166,7 +166,7 @@ describe JobEntity do ...@@ -166,7 +166,7 @@ describe JobEntity do
end end
it 'should indicate the failure reason on tooltip' do it 'should indicate the failure reason on tooltip' do
expect(subject[:status][:tooltip]).to eq('failed <br> (API failure) (allowed to fail)') expect(subject[:status][:tooltip]).to eq('failed - (API failure) (allowed to fail)')
end end
it 'should include a callout message with a verbose output' do it 'should include a callout message with a verbose output' do
......
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