Commit 846745cb authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/security/gitlab@12-10-stable-ee

parent dfc92d08
...@@ -105,7 +105,6 @@ export default class Clusters { ...@@ -105,7 +105,6 @@ export default class Clusters {
}); });
this.installApplication = this.installApplication.bind(this); this.installApplication = this.installApplication.bind(this);
this.showToken = this.showToken.bind(this);
this.errorContainer = document.querySelector('.js-cluster-error'); this.errorContainer = document.querySelector('.js-cluster-error');
this.successContainer = document.querySelector('.js-cluster-success'); this.successContainer = document.querySelector('.js-cluster-success');
...@@ -116,7 +115,6 @@ export default class Clusters { ...@@ -116,7 +115,6 @@ export default class Clusters {
); );
this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason'); this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason');
this.successApplicationContainer = document.querySelector('.js-cluster-application-notice'); this.successApplicationContainer = document.querySelector('.js-cluster-application-notice');
this.showTokenButton = document.querySelector('.js-show-cluster-token');
this.tokenField = document.querySelector('.js-cluster-token'); this.tokenField = document.querySelector('.js-cluster-token');
this.ingressDomainHelpText = document.querySelector('.js-ingress-domain-help-text'); this.ingressDomainHelpText = document.querySelector('.js-ingress-domain-help-text');
this.ingressDomainSnippet = this.ingressDomainSnippet =
...@@ -255,7 +253,6 @@ export default class Clusters { ...@@ -255,7 +253,6 @@ export default class Clusters {
} }
addListeners() { addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication); eventHub.$on('installApplication', this.installApplication);
eventHub.$on('updateApplication', data => this.updateApplication(data)); eventHub.$on('updateApplication', data => this.updateApplication(data));
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data)); eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
...@@ -271,7 +268,6 @@ export default class Clusters { ...@@ -271,7 +268,6 @@ export default class Clusters {
} }
removeListeners() { removeListeners() {
if (this.showTokenButton) this.showTokenButton.removeEventListener('click', this.showToken);
eventHub.$off('installApplication', this.installApplication); eventHub.$off('installApplication', this.installApplication);
eventHub.$off('updateApplication', this.updateApplication); eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain'); eventHub.$off('saveKnativeDomain');
...@@ -339,18 +335,6 @@ export default class Clusters { ...@@ -339,18 +335,6 @@ export default class Clusters {
} }
} }
showToken() {
const type = this.tokenField.getAttribute('type');
if (type === 'password') {
this.tokenField.setAttribute('type', 'text');
this.showTokenButton.textContent = s__('ClusterIntegration|Hide');
} else {
this.tokenField.setAttribute('type', 'password');
this.showTokenButton.textContent = s__('ClusterIntegration|Show');
}
}
hideAll() { hideAll() {
this.errorContainer.classList.add('hidden'); this.errorContainer.classList.add('hidden');
this.successContainer.classList.add('hidden'); this.successContainer.classList.add('hidden');
......
...@@ -101,7 +101,11 @@ export default class Issue { ...@@ -101,7 +101,11 @@ export default class Issue {
this.disableCloseReopenButton($button); this.disableCloseReopenButton($button);
const url = $button.attr('href'); const url = $button.data('close-reopen-url');
if (!url) {
return;
}
return axios return axios
.put(url) .put(url)
.then(({ data }) => { .then(({ data }) => {
......
<script> <script>
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { GlFormGroup, GlFormInput, GlFormRadioGroup, GlFormTextarea } from '@gitlab/ui'; import { GlFormGroup, GlFormInput, GlFormRadioGroup, GlFormTextarea } from '@gitlab/ui';
import { escape as esc } from 'lodash';
const defaultFileName = dashboard => dashboard.path.split('/').reverse()[0]; const defaultFileName = dashboard => dashboard.path.split('/').reverse()[0];
...@@ -42,7 +43,7 @@ export default { ...@@ -42,7 +43,7 @@ export default {
html: sprintf( html: sprintf(
__('Commit to %{branchName} branch'), __('Commit to %{branchName} branch'),
{ {
branchName: `<strong>${this.defaultBranch}</strong>`, branchName: `<strong>${esc(this.defaultBranch)}</strong>`,
}, },
false, false,
), ),
......
...@@ -10,6 +10,12 @@ module Clusters ...@@ -10,6 +10,12 @@ module Clusters
def execute(cluster) def execute(cluster)
if validate_params(cluster) if validate_params(cluster)
token = params.dig(:platform_kubernetes_attributes, :token)
if token.blank?
params[:platform_kubernetes_attributes]&.delete(:token)
end
cluster.update(params) cluster.update(params)
else else
false false
......
...@@ -25,16 +25,10 @@ ...@@ -25,16 +25,10 @@
label: s_('ClusterIntegration|CA Certificate'), label_class: 'label-bold', label: s_('ClusterIntegration|CA Certificate'), label_class: 'label-bold',
input_group_class: 'gl-field-error-anchor', append: copy_ca_cert_btn input_group_class: 'gl-field-error-anchor', append: copy_ca_cert_btn
- show_token_btn = (platform_field.button s_('ClusterIntegration|Show'), = platform_field.password_field :token, type: 'password', class: 'js-select-on-focus js-cluster-token',
type: 'button', class: 'js-show-cluster-token btn btn-default') readonly: cluster.read_only_kubernetes_platform_fields?, autocomplete: 'new-password',
- copy_token_btn = clipboard_button(text: platform.token, title: s_('ClusterIntegration|Copy Service Token'), label: s_('ClusterIntegration|Enter new Service Token'), label_class: 'label-bold',
class: 'input-group-text btn-default') if cluster.read_only_kubernetes_platform_fields? input_group_class: 'gl-field-error-anchor'
= platform_field.text_field :token, type: 'password', class: 'js-select-on-focus js-cluster-token',
required: true, title: s_('ClusterIntegration|Service token is required.'),
readonly: cluster.read_only_kubernetes_platform_fields?,
label: s_('ClusterIntegration|Service Token'), label_class: 'label-bold',
input_group_class: 'gl-field-error-anchor', append: show_token_btn + copy_token_btn
= platform_field.form_group :authorization_type do = platform_field.form_group :authorization_type do
= platform_field.check_box :authorization_type, { disabled: true, label: s_('ClusterIntegration|RBAC-enabled cluster'), = platform_field.check_box :authorization_type, { disabled: true, label: s_('ClusterIntegration|RBAC-enabled cluster'),
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.float-left.btn-group.prepend-left-10.issuable-close-dropdown.droplab-dropdown.js-issuable-close-dropdown .float-left.btn-group.prepend-left-10.issuable-close-dropdown.droplab-dropdown.js-issuable-close-dropdown
= link_to "#{display_button_action} #{display_issuable_type}", close_reopen_issuable_path(issuable), = link_to "#{display_button_action} #{display_issuable_type}", close_reopen_issuable_path(issuable),
method: button_method, class: "#{button_class} btn-#{button_action}", title: "#{display_button_action} #{display_issuable_type}" method: button_method, class: "#{button_class} btn-#{button_action}", title: "#{display_button_action} #{display_issuable_type}", data: { 'close-reopen-url': close_reopen_issuable_path(issuable) }
= button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color", = button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color",
data: { 'dropdown-trigger' => '#issuable-close-menu' }, 'aria-label' => 'Toggle dropdown' do data: { 'dropdown-trigger' => '#issuable-close-menu' }, 'aria-label' => 'Toggle dropdown' do
......
---
title: Disable caching on repo/blobs/[sha]/raw endpoint
merge_request:
author:
type: security
---
title: Kubernetes cluster details page no longer exposes Service Token
merge_request:
author:
type: security
---
title: Disallow user to control PUT request using mermaid markdown in issue description
merge_request:
author:
type: security
---
title: Fix file enuming using Group Import
merge_request:
author:
type: security
---
title: Prevent XSS in the monitoring dashboard
merge_request:
author:
type: security
...@@ -1197,7 +1197,7 @@ PUT /projects/:id ...@@ -1197,7 +1197,7 @@ PUT /projects/:id
| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge request by default | | `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge request by default |
| `external_authorization_classification_label` | string | no | **(PREMIUM)** The classification label for the project | | `external_authorization_classification_label` | string | no | **(PREMIUM)** The classification label for the project |
| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project | | `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project |
| `mirror_user_id` | integer | no | **(STARTER)** User responsible for all the activity surrounding a pull mirror event | | `mirror_user_id` | integer | no | **(STARTER)** User responsible for all the activity surrounding a pull mirror event. Can only be set by admins. |
| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds | | `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds |
| `only_mirror_protected_branches` | boolean | no | **(STARTER)** Only mirror protected branches | | `only_mirror_protected_branches` | boolean | no | **(STARTER)** Only mirror protected branches |
| `mirror_overwrites_diverged_branches` | boolean | no | **(STARTER)** Pull mirror overwrites diverged branches | | `mirror_overwrites_diverged_branches` | boolean | no | **(STARTER)** Pull mirror overwrites diverged branches |
......
...@@ -4,6 +4,8 @@ module API ...@@ -4,6 +4,8 @@ module API
class GroupImport < Grape::API class GroupImport < Grape::API
MAXIMUM_FILE_SIZE = 50.megabytes.freeze MAXIMUM_FILE_SIZE = 50.megabytes.freeze
helpers Helpers::FileUploadHelpers
helpers do helpers do
def parent_group def parent_group
find_group!(params[:parent_id]) if params[:parent_id].present? find_group!(params[:parent_id]) if params[:parent_id].present?
...@@ -48,29 +50,20 @@ module API ...@@ -48,29 +50,20 @@ module API
params do params do
requires :path, type: String, desc: 'Group path' requires :path, type: String, desc: 'Group path'
requires :name, type: String, desc: 'Group name' requires :name, type: String, desc: 'Group name'
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The group export file to be imported'
optional :parent_id, type: Integer, desc: "The ID of the parent group that the group will be imported into. Defaults to the current user's namespace." optional :parent_id, type: Integer, desc: "The ID of the parent group that the group will be imported into. Defaults to the current user's namespace."
optional 'file.path', type: String, desc: 'Path to locally stored body (generated by Workhorse)'
optional 'file.name', type: String, desc: 'Real filename as send in Content-Disposition (generated by Workhorse)'
optional 'file.type', type: String, desc: 'Real content type as send in Content-Type (generated by Workhorse)'
optional 'file.size', type: Integer, desc: 'Real size of file (generated by Workhorse)'
optional 'file.md5', type: String, desc: 'MD5 checksum of the file (generated by Workhorse)'
optional 'file.sha1', type: String, desc: 'SHA1 checksum of the file (generated by Workhorse)'
optional 'file.sha256', type: String, desc: 'SHA256 checksum of the file (generated by Workhorse)'
end end
post 'import' do post 'import' do
authorize_create_group! authorize_create_group!
require_gitlab_workhorse! require_gitlab_workhorse!
validate_file!
uploaded_file = UploadedFile.from_params(params, :file, ImportExportUploader.workhorse_local_upload_path)
bad_request!('Unable to process group import file') unless uploaded_file
group_params = { group_params = {
path: params[:path], path: params[:path],
name: params[:name], name: params[:name],
parent_id: params[:parent_id], parent_id: params[:parent_id],
visibility_level: closest_allowed_visibility_level, visibility_level: closest_allowed_visibility_level,
import_export_upload: ImportExportUpload.new(import_file: uploaded_file) import_export_upload: ImportExportUpload.new(import_file: params[:file])
} }
group = ::Groups::CreateService.new(current_user, group_params).execute group = ::Groups::CreateService.new(current_user, group_params).execute
......
...@@ -6,6 +6,8 @@ module API ...@@ -6,6 +6,8 @@ module API
class Repositories < Grape::API class Repositories < Grape::API
include PaginationParams include PaginationParams
helpers ::API::Helpers::HeadersHelpers
before { authorize! :download_code, user_project } before { authorize! :download_code, user_project }
params do params do
...@@ -67,6 +69,8 @@ module API ...@@ -67,6 +69,8 @@ module API
get ':id/repository/blobs/:sha/raw' do get ':id/repository/blobs/:sha/raw' do
assign_blob_vars! assign_blob_vars!
no_cache_headers
send_git_blob @repo, @blob send_git_blob @repo, @blob
end end
......
...@@ -4362,9 +4362,6 @@ msgstr "" ...@@ -4362,9 +4362,6 @@ msgstr ""
msgid "ClusterIntegration|Copy Kubernetes cluster name" msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr "" msgstr ""
msgid "ClusterIntegration|Copy Service Token"
msgstr ""
msgid "ClusterIntegration|Could not load IAM roles" msgid "ClusterIntegration|Could not load IAM roles"
msgstr "" msgstr ""
...@@ -4443,6 +4440,9 @@ msgstr "" ...@@ -4443,6 +4440,9 @@ msgstr ""
msgid "ClusterIntegration|Enabled stack" msgid "ClusterIntegration|Enabled stack"
msgstr "" msgstr ""
msgid "ClusterIntegration|Enter new Service Token"
msgstr ""
msgid "ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster" msgid "ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster"
msgstr "" msgstr ""
...@@ -4518,9 +4518,6 @@ msgstr "" ...@@ -4518,9 +4518,6 @@ msgstr ""
msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts." msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
msgstr "" msgstr ""
msgid "ClusterIntegration|Hide"
msgstr ""
msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}." msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
msgstr "" msgstr ""
...@@ -4896,9 +4893,6 @@ msgstr "" ...@@ -4896,9 +4893,6 @@ msgstr ""
msgid "ClusterIntegration|Set the global mode for the WAF in this cluster. This can be overridden at the environmental level." msgid "ClusterIntegration|Set the global mode for the WAF in this cluster. This can be overridden at the environmental level."
msgstr "" msgstr ""
msgid "ClusterIntegration|Show"
msgstr ""
msgid "ClusterIntegration|Something went wrong on our end." msgid "ClusterIntegration|Something went wrong on our end."
msgstr "" msgstr ""
...@@ -21248,9 +21242,6 @@ msgstr "" ...@@ -21248,9 +21242,6 @@ msgstr ""
msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches." msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr "" msgstr ""
msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
msgstr ""
msgid "This variable can not be masked." msgid "This variable can not be masked."
msgstr "" msgstr ""
...@@ -23973,6 +23964,9 @@ msgstr "" ...@@ -23973,6 +23964,9 @@ msgstr ""
msgid "You will be removed from existing projects/groups" msgid "You will be removed from existing projects/groups"
msgstr "" msgstr ""
msgid "You will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
msgid "You will first need to set up Jira Integration to use this feature." msgid "You will first need to set up Jira Integration to use this feature."
msgstr "" msgstr ""
......
...@@ -39,7 +39,7 @@ describe 'User Cluster', :js do ...@@ -39,7 +39,7 @@ describe 'User Cluster', :js do
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value) expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
.to have_content('http://example.com') .to have_content('http://example.com')
expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value) expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
.to have_content('my-token') .to be_empty
end end
end end
......
...@@ -46,7 +46,7 @@ describe 'User Cluster', :js do ...@@ -46,7 +46,7 @@ describe 'User Cluster', :js do
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value) expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
.to have_content('http://example.com') .to have_content('http://example.com')
expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value) expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
.to have_content('my-token') .to be_empty
end end
it 'user sees RBAC is enabled by default' do it 'user sees RBAC is enabled by default' do
......
...@@ -82,28 +82,6 @@ describe('Clusters', () => { ...@@ -82,28 +82,6 @@ describe('Clusters', () => {
}); });
}); });
describe('showToken', () => {
it('should update token field type', () => {
cluster.showTokenButton.click();
expect(cluster.tokenField.getAttribute('type')).toEqual('text');
cluster.showTokenButton.click();
expect(cluster.tokenField.getAttribute('type')).toEqual('password');
});
it('should update show token button text', () => {
cluster.showTokenButton.click();
expect(cluster.showTokenButton.textContent).toEqual('Hide');
cluster.showTokenButton.click();
expect(cluster.showTokenButton.textContent).toEqual('Show');
});
});
describe('checkForNewInstalls', () => { describe('checkForNewInstalls', () => {
const INITIAL_APP_MAP = { const INITIAL_APP_MAP = {
helm: { status: null, title: 'Helm Tiller' }, helm: { status: null, title: 'Helm Tiller' },
......
<div class="description" updated-at="">
<div class="md issue-realtime-trigger-pulse">
<svg
id="mermaid-1587752414912"
width="100%"
xmlns="http://www.w3.org/2000/svg"
style="max-width: 185.35000610351562px;"
viewBox="0 0 185.35000610351562 50.5"
class="mermaid"
>
<g transform="translate(0, 0)">
<g class="output">
<g class="clusters"></g>
<g class="edgePaths"></g>
<g class="edgeLabels"></g>
<g class="nodes">
<g
class="node js-issuable-actions btn-close clickable"
style="opacity: 1;"
id="A"
transform="translate(92.67500305175781,25.25)"
title="click to PUT"
>
<a
class="js-issuable-actions btn-close clickable"
href="https://invalid"
rel="noopener"
>
<rect
rx="0"
ry="0"
x="-84.67500305175781"
y="-17.25"
width="169.35000610351562"
height="34.5"
class="label-container"
></rect>
<g class="label" transform="translate(0,0)">
<g transform="translate(-74.67500305175781,-7.25)">
<text style="">
<tspan xml:space="preserve" dy="1em" x="1">Click to send a PUT request</tspan>
</text>
</g>
</g>
</a>
</g>
</g>
</g>
</g>
<text class="source" display="none">
Click to send a PUT request
</text>
</svg>
</div>
<textarea
data-update-url="/h5bp/html5-boilerplate/-/issues/35.json"
dir="auto"
class="hidden js-task-list-field"
></textarea>
<div class="modal-open recaptcha-modal js-recaptcha-modal" style="display: none;">
<div role="dialog" tabindex="-1" class="modal d-block">
<div role="document" class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title float-left">Please solve the reCAPTCHA</h4>
<button type="button" data-dismiss="modal" aria-label="Close" class="close float-right">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div>
<p>We want to be sure it is you, please confirm you are not a robot.</p>
<div></div>
</div>
</div>
<!---->
</div>
</div>
</div>
<div class="modal-backdrop fade show"></div>
</div>
</div>
...@@ -18,6 +18,7 @@ describe('Issue', () => { ...@@ -18,6 +18,7 @@ describe('Issue', () => {
preloadFixtures('issues/closed-issue.html'); preloadFixtures('issues/closed-issue.html');
preloadFixtures('issues/issue-with-task-list.html'); preloadFixtures('issues/issue-with-task-list.html');
preloadFixtures('issues/open-issue.html'); preloadFixtures('issues/open-issue.html');
preloadFixtures('static/issue_with_mermaid_graph.html');
function expectErrorMessage() { function expectErrorMessage() {
const $flashMessage = $('div.flash-alert'); const $flashMessage = $('div.flash-alert');
...@@ -228,4 +229,30 @@ describe('Issue', () => { ...@@ -228,4 +229,30 @@ describe('Issue', () => {
}); });
}); });
}); });
describe('when not displaying blocked warning', () => {
describe('when clicking a mermaid graph inside an issue description', () => {
let mock;
let spy;
beforeEach(() => {
loadFixtures('static/issue_with_mermaid_graph.html');
mock = new MockAdapter(axios);
spy = jest.spyOn(axios, 'put');
});
afterEach(() => {
mock.restore();
jest.clearAllMocks();
});
it('does not make a PUT request', () => {
Issue.prototype.initIssueBtnEventListeners();
$('svg a.js-issuable-actions').trigger('click');
expect(spy).not.toHaveBeenCalled();
});
});
});
}); });
...@@ -3,9 +3,17 @@ import DuplicateDashboardForm from '~/monitoring/components/duplicate_dashboard_ ...@@ -3,9 +3,17 @@ import DuplicateDashboardForm from '~/monitoring/components/duplicate_dashboard_
import { dashboardGitResponse } from '../mock_data'; import { dashboardGitResponse } from '../mock_data';
describe('DuplicateDashboardForm', () => { let wrapper;
let wrapper;
const createMountedWrapper = (props = {}) => {
// Use `mount` to render native input elements
wrapper = mount(DuplicateDashboardForm, {
propsData: { ...props },
sync: false,
});
};
describe('DuplicateDashboardForm', () => {
const defaultBranch = 'master'; const defaultBranch = 'master';
const findByRef = ref => wrapper.find({ ref }); const findByRef = ref => wrapper.find({ ref });
...@@ -20,14 +28,7 @@ describe('DuplicateDashboardForm', () => { ...@@ -20,14 +28,7 @@ describe('DuplicateDashboardForm', () => {
}; };
beforeEach(() => { beforeEach(() => {
// Use `mount` to render native input elements createMountedWrapper({ dashboard: dashboardGitResponse[0], defaultBranch });
wrapper = mount(DuplicateDashboardForm, {
propsData: {
dashboard: dashboardGitResponse[0],
defaultBranch,
},
sync: false,
});
}); });
it('renders correctly', () => { it('renders correctly', () => {
...@@ -144,3 +145,18 @@ describe('DuplicateDashboardForm', () => { ...@@ -144,3 +145,18 @@ describe('DuplicateDashboardForm', () => {
}); });
}); });
}); });
describe('DuplicateDashboardForm escapes elements', () => {
const branchToEscape = "<img/src='x'onerror=alert(document.domain)>";
beforeEach(() => {
createMountedWrapper({ dashboard: dashboardGitResponse[0], defaultBranch: branchToEscape });
});
it('should escape branch name data', () => {
const branchOptionHtml = wrapper.vm.branchOptions[0].html;
const escapedBranch = '&lt;img/src=&#39;x&#39;onerror=alert(document.domain)&gt';
expect(branchOptionHtml).toEqual(expect.stringContaining(escapedBranch));
});
});
...@@ -11,7 +11,7 @@ describe API::GroupImport do ...@@ -11,7 +11,7 @@ describe API::GroupImport do
let(:file) { File.join('spec', 'fixtures', 'group_export.tar.gz') } let(:file) { File.join('spec', 'fixtures', 'group_export.tar.gz') }
let(:export_path) { "#{Dir.tmpdir}/group_export_spec" } let(:export_path) { "#{Dir.tmpdir}/group_export_spec" }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } } let(:workhorse_headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
before do before do
allow_next_instance_of(Gitlab::ImportExport) do |import_export| allow_next_instance_of(Gitlab::ImportExport) do |import_export|
...@@ -35,7 +35,7 @@ describe API::GroupImport do ...@@ -35,7 +35,7 @@ describe API::GroupImport do
} }
end end
subject { post api('/groups/import', user), params: params, headers: workhorse_header } subject { upload_archive(file_upload, workhorse_headers, params) }
shared_examples 'when all params are correct' do shared_examples 'when all params are correct' do
context 'when user is authorized to create new group' do context 'when user is authorized to create new group' do
...@@ -151,7 +151,7 @@ describe API::GroupImport do ...@@ -151,7 +151,7 @@ describe API::GroupImport do
params[:file] = file_upload params[:file] = file_upload
expect do expect do
post api('/groups/import', user), params: params, headers: workhorse_header upload_archive(file_upload, workhorse_headers, params)
end.not_to change { Group.count }.from(1) end.not_to change { Group.count }.from(1)
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
...@@ -171,7 +171,7 @@ describe API::GroupImport do ...@@ -171,7 +171,7 @@ describe API::GroupImport do
context 'without a file from workhorse' do context 'without a file from workhorse' do
it 'rejects the request' do it 'rejects the request' do
subject upload_archive(nil, workhorse_headers, params)
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
end end
...@@ -179,7 +179,7 @@ describe API::GroupImport do ...@@ -179,7 +179,7 @@ describe API::GroupImport do
context 'without a workhorse header' do context 'without a workhorse header' do
it 'rejects request without a workhorse header' do it 'rejects request without a workhorse header' do
post api('/groups/import', user), params: params upload_archive(file_upload, {}, params)
expect(response).to have_gitlab_http_status(:forbidden) expect(response).to have_gitlab_http_status(:forbidden)
end end
...@@ -189,9 +189,7 @@ describe API::GroupImport do ...@@ -189,9 +189,7 @@ describe API::GroupImport do
let(:params) do let(:params) do
{ {
path: 'test-import-group', path: 'test-import-group',
name: 'test-import-group', name: 'test-import-group'
'file.path' => file_upload.path,
'file.name' => file_upload.original_filename
} }
end end
...@@ -229,9 +227,7 @@ describe API::GroupImport do ...@@ -229,9 +227,7 @@ describe API::GroupImport do
{ {
path: 'test-import-group', path: 'test-import-group',
name: 'test-import-group', name: 'test-import-group',
file: fog_file, file: fog_file
'file.remote_id' => file_name,
'file.size' => fog_file.size
} }
end end
...@@ -245,10 +241,21 @@ describe API::GroupImport do ...@@ -245,10 +241,21 @@ describe API::GroupImport do
include_examples 'when some params are missing' include_examples 'when some params are missing'
end end
end end
def upload_archive(file, headers = {}, params = {})
workhorse_finalize(
api('/groups/import', user),
method: :post,
file_key: :file,
params: params.merge(file: file),
headers: headers,
send_rewritten_field: true
)
end
end end
describe 'POST /groups/import/authorize' do describe 'POST /groups/import/authorize' do
subject { post api('/groups/import/authorize', user), headers: workhorse_header } subject { post api('/groups/import/authorize', user), headers: workhorse_headers }
it 'authorizes importing group with workhorse header' do it 'authorizes importing group with workhorse header' do
subject subject
...@@ -258,7 +265,7 @@ describe API::GroupImport do ...@@ -258,7 +265,7 @@ describe API::GroupImport do
end end
it 'rejects requests that bypassed gitlab-workhorse' do it 'rejects requests that bypassed gitlab-workhorse' do
workhorse_header.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER) workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
subject subject
......
...@@ -177,6 +177,12 @@ describe API::Repositories do ...@@ -177,6 +177,12 @@ describe API::Repositories do
expect(headers['Content-Disposition']).to eq 'inline' expect(headers['Content-Disposition']).to eq 'inline'
end end
it_behaves_like 'uncached response' do
before do
get api(route, current_user)
end
end
context 'when sha does not exist' do context 'when sha does not exist' do
it_behaves_like '404 response' do it_behaves_like '404 response' do
let(:request) { get api(route.sub(sample_blob.oid, 'abcd9876'), current_user) } let(:request) { get api(route.sub(sample_blob.oid, 'abcd9876'), current_user) }
......
...@@ -47,6 +47,39 @@ describe Clusters::UpdateService do ...@@ -47,6 +47,39 @@ describe Clusters::UpdateService do
expect(cluster.platform.namespace).to eq('custom-namespace') expect(cluster.platform.namespace).to eq('custom-namespace')
end end
end end
context 'when service token is empty' do
let(:params) do
{
platform_kubernetes_attributes: {
token: ''
}
}
end
it 'does not update the token' do
current_token = cluster.platform.token
is_expected.to eq(true)
cluster.platform.reload
expect(cluster.platform.token).to eq(current_token)
end
end
context 'when service token is not empty' do
let(:params) do
{
platform_kubernetes_attributes: {
token: 'new secret token'
}
}
end
it 'updates the token' do
is_expected.to eq(true)
expect(cluster.platform.token).to eq('new secret token')
end
end
end end
context 'when invalid params' do context 'when invalid params' do
......
# frozen_string_literal: true
#
# Pairs with lib/gitlab/no_cache_headers.rb
#
RSpec.shared_examples 'uncached response' do
it 'defines an uncached header response' do
expect(response.headers["Cache-Control"]).to include("no-store", "no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
end
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
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