Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-03-06 09:08:13 +00:00
parent ab128cc125
commit 8e94dad32b
22 changed files with 257 additions and 137 deletions

View file

@ -1,26 +1,45 @@
import Vue from 'vue';
import ConfirmModal from '~/vue_shared/components/confirm_modal.vue';
const mountConfirmModal = button => {
const props = {
path: button.dataset.path,
method: button.dataset.method,
modalAttributes: JSON.parse(button.dataset.modalAttributes),
};
const mountConfirmModal = () => {
return new Vue({
data() {
return {
path: '',
method: '',
modalAttributes: null,
showModal: false,
};
},
mounted() {
document.querySelectorAll('.js-confirm-modal-button').forEach(button => {
button.addEventListener('click', e => {
e.preventDefault();
this.path = button.dataset.path;
this.method = button.dataset.method;
this.modalAttributes = JSON.parse(button.dataset.modalAttributes);
this.showModal = true;
});
});
},
methods: {
dismiss() {
this.showModal = false;
},
},
render(h) {
return h(ConfirmModal, { props });
return h(ConfirmModal, {
props: {
path: this.path,
method: this.method,
modalAttributes: this.modalAttributes,
showModal: this.showModal,
},
on: { dismiss: this.dismiss },
});
},
}).$mount();
};
export default () => {
document.getElementsByClassName('js-confirm-modal-button').forEach(button => {
button.addEventListener('click', e => {
e.preventDefault();
mountConfirmModal(button);
});
});
};
export default () => mountConfirmModal();

View file

@ -1,4 +1,4 @@
import _ from 'underscore';
import { last } from 'lodash';
import FilteredSearchContainer from './container';
import FilteredSearchTokenizer from './filtered_search_tokenizer';
import FilteredSearchDropdownManager from './filtered_search_dropdown_manager';
@ -70,11 +70,11 @@ export default class DropdownUtils {
if (!allowMultiple && itemInExistingTokens) {
updatedItem.droplab_hidden = true;
} else if (!isSearchItem && (!lastKey || _.last(searchInput.split('')) === ' ')) {
} else if (!isSearchItem && (!lastKey || last(searchInput.split('')) === ' ')) {
updatedItem.droplab_hidden = false;
} else if (lastKey) {
const split = lastKey.split(':');
const tokenName = _.last(split[0].split(' '));
const tokenName = last(split[0].split(' '));
const match = isSearchItem
? allowedKeys.some(key => key.startsWith(tokenName.toLowerCase()))
@ -129,7 +129,7 @@ export default class DropdownUtils {
const values = [];
if (untilInput) {
const inputIndex = _.findIndex(tokens, t => t.classList.contains('input-token'));
const inputIndex = tokens.findIndex(t => t.classList.contains('input-token'));
// Add one to include input-token to the tokens array
tokens.splice(inputIndex + 1);
}

View file

@ -1,5 +1,5 @@
import { last } from 'lodash';
import AvailableDropdownMappings from 'ee_else_ce/filtered_search/available_dropdown_mappings';
import _ from 'underscore';
import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
@ -184,8 +184,8 @@ export default class FilteredSearchDropdownManager {
// Eg. token = 'label:'
const split = lastToken.split(':');
const dropdownName = _.last(split[0].split(' '));
const possibleOperatorToken = _.last(split[1]);
const dropdownName = last(split[0].split(' '));
const possibleOperatorToken = last(split[1]);
const hasOperator = FilteredSearchVisualTokens.permissibleOperatorValues.includes(
possibleOperatorToken && possibleOperatorToken.trim(),

View file

@ -1,4 +1,4 @@
import _ from 'underscore';
import { last } from 'lodash';
import recentSearchesStorageKeys from 'ee_else_ce/filtered_search/recent_searches_storage_keys';
import { getParameterByName, getUrlParamsArray } from '~/lib/utils/common_utils';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
@ -456,7 +456,7 @@ export default class FilteredSearchManager {
if (fragments.length > 1) {
const inputValues = fragments[0].split(' ');
const tokenKey = _.last(inputValues);
const tokenKey = last(inputValues);
if (inputValues.length > 1) {
inputValues.pop();

View file

@ -1,4 +1,4 @@
import { flatten } from 'underscore';
import { flattenDeep } from 'lodash';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
import { __ } from '~/locale';
@ -73,7 +73,7 @@ export const alternativeTokenKeys = [
},
];
export const conditions = flatten(
export const conditions = flattenDeep(
[
{
url: 'assignee_id=None',

View file

@ -1,4 +1,4 @@
import _ from 'underscore';
import { uniq } from 'lodash';
class RecentSearchesStore {
constructor(initialState = {}, allowedKeys) {
@ -20,7 +20,7 @@ class RecentSearchesStore {
setRecentSearches(searches = []) {
const trimmedSearches = searches.map(search => search.trim());
this.state.recentSearches = _.uniq(trimmedSearches).slice(0, 5);
this.state.recentSearches = uniq(trimmedSearches).slice(0, 5);
return this.state.recentSearches;
}
}

View file

@ -1,4 +1,4 @@
import _ from 'underscore';
import { escape as esc } from 'lodash';
import { USER_TOKEN_TYPES } from 'ee_else_ce/filtered_search/constants';
import FilteredSearchContainer from '~/filtered_search/container';
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
@ -48,7 +48,7 @@ export default class VisualTokenValue {
tokenValueContainer.dataset.originalValue = tokenValue;
tokenValueElement.innerHTML = `
<img class="avatar s20" src="${user.avatar_url}" alt="">
${_.escape(user.name)}
${esc(user.name)}
`;
/* eslint-enable no-param-reassign */
})

View file

@ -9,34 +9,43 @@ export default {
props: {
modalAttributes: {
type: Object,
required: true,
required: false,
default: () => {
return {};
},
},
path: {
type: String,
required: true,
required: false,
default: '',
},
method: {
type: String,
required: true,
required: false,
default: '',
},
showModal: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
isDismissed: false,
};
},
mounted() {
this.openModal();
watch: {
showModal(val) {
if (val) {
// Wait for v-if to render
this.$nextTick(() => {
this.openModal();
});
}
},
},
methods: {
openModal() {
this.$refs.modal.show();
},
submitModal() {
this.$refs.form.requestSubmit();
},
dismiss() {
this.isDismissed = true;
this.$refs.form.submit();
},
},
csrf,
@ -45,11 +54,11 @@ export default {
<template>
<gl-modal
v-if="!isDismissed"
v-if="showModal"
ref="modal"
v-bind="modalAttributes"
@primary="submitModal"
@canceled="dismiss"
@canceled="$emit('dismiss')"
>
<form ref="form" :action="path" method="post">
<!-- Rails workaround for <form method="delete" />

View file

@ -3,8 +3,8 @@
module Clusters
module Applications
class CertManager < ApplicationRecord
VERSION = 'v0.9.1'
CRD_VERSION = '0.9'
VERSION = 'v0.10.1'
CRD_VERSION = '0.10'
self.table_name = 'clusters_applications_cert_managers'

View file

@ -0,0 +1,5 @@
---
title: Ensure RepositoryLinkFilter handles Gitaly failures gracefully
merge_request: 26531
author:
type: performance

View file

@ -0,0 +1,5 @@
---
title: Fixed bug where processing NuGet packages are returned from the Packages API
merge_request: 26270
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Support Rails 6 `insert_all!`
merge_request: 26595
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Use cert-manager 0.10 instead of 0.9 for new chart installations
merge_request: 26345
author:
type: changed

View file

@ -107,24 +107,20 @@ artifact available. Behind the scenes, the
[GitLab Klar analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/klar/)
is used and runs the scans.
### Example
The following is a sample `.gitlab-ci.yml` that will build your Docker Image, push it to the container registry and run Container Scanning.
The following is a sample `.gitlab-ci.yml` that will build your Docker image,
push it to the Container Registry, and run Container Scanning:
```yaml
variables:
DOCKER_DRIVER: overlay2
services:
- docker:stable-dind
- docker:19.03.5-dind
stages:
- build
- test
include:
- template: Container-Scanning.gitlab-ci.yml
build:
image: docker:stable
stage: build
@ -135,40 +131,37 @@ build:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build -t $IMAGE .
- docker push $IMAGE
include:
- template: Container-Scanning.gitlab-ci.yml
```
### Vulnerability Whitelisting
### Customizing the Container Scanning settings
If you want to whitelist specific vulnerabilities, you'll need to:
You can change container scanning settings by using the [`variables`](../../../ci/yaml/README.md#variables)
parameter in your `.gitlab-ci.yml` to change [environment variables](#available-variables).
1. Set [`GIT_STRATEGY: fetch`](../../../ci/yaml/README.md#git-strategy) in your `.gitlab-ci.yml` file by following the instructions described in the
[overriding the Container Scanning template](#overriding-the-container-scanning-template) section of this document.
1. Define the whitelisted vulnerabilities in a YAML file named `clair-whitelist.yml` which must use the format described
in the [following whitelist example file](https://github.com/arminc/clair-scanner/blob/v12/example-whitelist.yaml).
1. Add the `clair-whitelist.yml` file to the Git repository of your project
### Overriding the Container Scanning template
If you want to override the job definition (for example, change properties like
`variables`), you need to declare a `container_scanning` job after the
template inclusion and specify any additional keys under it. For example:
In the following example, we [include](../../../ci/yaml/README.md#include) the template and also
set the `CLAIR_OUTPUT` variable to `High`:
```yaml
include:
- template: Container-Scanning.gitlab-ci.yml
template: Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
GIT_STRATEGY: fetch
variables:
CLAIR_OUTPUT: High
```
The `CLAIR_OUTPUT` variable defined in the main `gitlab-ci.yml` will overwrite what's
defined in `Container-Scanning.gitlab-ci.yml`, changing the Container Scanning behavior.
[//]: # "NOTE: The container scanning tool references the following heading in the code, so if you"
[//]: # " make a change to this heading, make sure to update the documentation URLs used in the"
[//]: # " container scanning tool (https://gitlab.com/gitlab-org/security-products/analyzers/klar)"
### Available variables
#### Available variables
Container Scanning can be [configured](#overriding-the-container-scanning-template)
Container Scanning can be [configured](#customizing-the-container-scanning-settings)
using environment variables.
| Environment Variable | Description | Default |
@ -187,36 +180,32 @@ using environment variables.
| `CLAIR_DB_IMAGE_TAG` | (**DEPRECATED - use `CLAIR_DB_IMAGE` instead**) The Docker image tag for the [Postgres server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db). It can be useful to override this value with a specific version, for example, to provide a consistent set of vulnerabilities for integration testing purposes. | `latest` |
| `DOCKERFILE_PATH` | The path to the `Dockerfile` to be used for generating remediations. By default, the scanner will look for a file named `Dockerfile` in the root directory of the project, so this variable should only be configured if your `Dockerfile` is in a non-standard location, such as a subdirectory. See [Solutions for vulnerabilities](#solutions-for-vulnerabilities-auto-remediation) for more details. | `Dockerfile` |
## Security Dashboard
### Overriding the Container Scanning template
The Security Dashboard is a good place to get an overview of all the security
vulnerabilities in your groups, projects and pipelines. Read more about the
[Security Dashboard](../security_dashboard/index.md).
If you want to override the job definition (for example, change properties like
`variables`), you need to declare a `container_scanning` job after the
template inclusion and specify any additional keys under it. For example:
## Interacting with the vulnerabilities
```yaml
include:
template: Container-Scanning.gitlab-ci.yml
Once a vulnerability is found, you can interact with it. Read more on how to
[interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
container_scanning:
variables:
GIT_STRATEGY: fetch
```
## Solutions for vulnerabilities (auto-remediation)
### Vulnerability whitelisting
Some vulnerabilities can be fixed by applying the solution that GitLab
automatically generates.
If you want to whitelist specific vulnerabilities, you'll need to:
To enable remediation support, the scanning tool _must_ have access to the `Dockerfile` specified by
the `DOCKERFILE_PATH` environment variable. To ensure that the scanning tool has access to this
file, it's necessary to set [`GIT_STRATEGY: fetch`](../../../ci/yaml/README.md#git-strategy) in
your `.gitlab-ci.yml` file by following the instructions described in this document's
[overriding the Container Scanning template](#overriding-the-container-scanning-template) section.
1. Set `GIT_STRATEGY: fetch` in your `.gitlab-ci.yml` file by following the instructions described in the
[overriding the Container Scanning template](#overriding-the-container-scanning-template) section of this document.
1. Define the whitelisted vulnerabilities in a YAML file named `clair-whitelist.yml` which must use the format described
in the [whitelist example file](https://github.com/arminc/clair-scanner/blob/v12/example-whitelist.yaml).
1. Add the `clair-whitelist.yml` file to the Git repository of your project.
Read more about the [solutions for vulnerabilities](../index.md#solutions-for-vulnerabilities-auto-remediation).
## Vulnerabilities database update
For more information about the vulnerabilities database update, check the
[maintenance table](../index.md#maintenance-and-update-of-the-vulnerabilities-database).
## Running Container Scanning in an offline air-gapped installation
### Running Container Scanning in an offline, air-gapped installation
Container Scanning can be executed on an offline air-gapped GitLab Ultimate installation using the following process:
@ -245,7 +234,7 @@ It may be worthwhile to set up a [scheduled pipeline](../../../ci/pipelines/sche
image: docker:stable
services:
- docker:stable-dind
- docker:19.03.5-dind
stages:
- build
@ -396,6 +385,33 @@ the report JSON unless stated otherwise. Presence of optional fields depends on
| `remediations[].summary` | Overview of how the vulnerabilities have been fixed. |
| `remediations[].diff` | base64-encoded remediation code diff, compatible with [`git apply`](https://git-scm.com/docs/git-format-patch#_discussion). |
## Security Dashboard
The [Security Dashboard](../security_dashboard/index.md) shows you an overview of all
the security vulnerabilities in your groups, projects and pipelines.
## Vulnerabilities database update
For more information about the vulnerabilities database update, check the
[maintenance table](../index.md#maintenance-and-update-of-the-vulnerabilities-database).
## Interacting with the vulnerabilities
Once a vulnerability is found, you can [interact with it](../index.md#interacting-with-the-vulnerabilities).
## Solutions for vulnerabilities (auto-remediation)
Some vulnerabilities can be fixed by applying the solution that GitLab
automatically generates.
To enable remediation support, the scanning tool _must_ have access to the `Dockerfile` specified by
the `DOCKERFILE_PATH` environment variable. To ensure that the scanning tool has access to this
file, it's necessary to set [`GIT_STRATEGY: fetch`](../../../ci/yaml/README.md#git-strategy) in
your `.gitlab-ci.yml` file by following the instructions described in this document's
[overriding the Container Scanning template](#overriding-the-container-scanning-template) section.
Read more about the [solutions for vulnerabilities](../index.md#solutions-for-vulnerabilities-auto-remediation).
## Troubleshooting
### docker: Error response from daemon: failed to copy xattrs

View file

@ -70,14 +70,9 @@ module API
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
optional :name_regex_delete, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
# require either name_regex (deprecated) or name_regex_delete, it is ok to have both
given name_regex_delete: ->(val) { val.nil? } do
requires :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
end
optional :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
given name_regex: ->(val) { val.nil? } do
requires :name_regex_delete, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
end
# require either name_regex (deprecated) or name_regex_delete, it is ok to have both
at_least_one_of :name_regex, :name_regex_delete
optional :name_regex_keep, type: String, desc: 'The tag name regexp to retain'
optional :keep_n, type: Integer, desc: 'Keep n of latest tags with matching name'
optional :older_than, type: String, desc: 'Delete older than: 1h, 1d, 1month'

View file

@ -80,6 +80,13 @@ module Banzai
end
Gitlab::GitalyClient::BlobService.new(repository).get_blob_types(revision_paths, 1)
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => e
# Handle Gitaly connection issues gracefully
Gitlab::ErrorTracking.track_exception(e, project_id: project.id)
# Return all links as blob types
paths.collect do |path|
[path, :blob]
end
end
def get_uri(html_attr)

View file

@ -9250,6 +9250,12 @@ msgstr ""
msgid "Geo|Remove"
msgstr ""
msgid "Geo|Remove entry"
msgstr ""
msgid "Geo|Remove tracking database entry"
msgstr ""
msgid "Geo|Repository sync capacity"
msgstr ""
@ -9301,15 +9307,15 @@ msgstr ""
msgid "Geo|This is a primary node"
msgstr ""
msgid "Geo|Tracking database entry will be removed. Are you sure?"
msgstr ""
msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
msgstr ""
msgid "Geo|Tracking entry for upload (%{type}/%{id}) was successfully removed."
msgstr ""
msgid "Geo|Tracking entry will be removed. Are you sure?"
msgstr ""
msgid "Geo|URL"
msgstr ""

View file

@ -0,0 +1 @@
HTMLFormElement.prototype.submit = jest.fn();

View file

@ -1,4 +1,5 @@
import './element_scroll_into_view';
import './form_element';
import './get_client_rects';
import './inner_text';
import './window_scroll_to';

View file

@ -3,6 +3,8 @@ import { GlModal } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants';
import ConfirmModal from '~/vue_shared/components/confirm_modal.vue';
jest.mock('~/lib/utils/csrf', () => ({ token: 'test-csrf-token' }));
describe('vue_shared/components/confirm_modal', () => {
const testModalProps = {
path: `${TEST_HOST}/1`,
@ -39,45 +41,61 @@ describe('vue_shared/components/confirm_modal', () => {
});
const findModal = () => wrapper.find(GlModal);
const findForm = () => wrapper.find('form');
const findFormData = () =>
findForm()
.findAll('input')
.wrappers.map(x => ({ name: x.attributes('name'), value: x.attributes('value') }));
describe('template', () => {
beforeEach(() => {
createComponent();
describe('when showModal is false', () => {
beforeEach(() => {
createComponent();
});
it('does not render GlModal', () => {
expect(findModal().exists()).toBeFalsy();
});
});
it('calls openModal on mount', () => {
expect(actionSpies.openModal).toHaveBeenCalled();
});
describe('when showModal is true', () => {
beforeEach(() => {
createComponent({ showModal: true });
});
it('renders GlModal', () => {
expect(findModal().exists()).toBeTruthy();
it('renders GlModal', () => {
expect(findModal().exists()).toBeTruthy();
expect(findModal().attributes()).toEqual(
expect.objectContaining({
modalid: testModalProps.modalAttributes.modalId,
oktitle: testModalProps.modalAttributes.okTitle,
okvariant: testModalProps.modalAttributes.okVariant,
}),
);
});
});
});
describe('methods', () => {
beforeEach(() => {
createComponent();
createComponent({ showModal: true });
});
describe('submitModal', () => {
it('does not submit form', () => {
expect(findForm().element.submit).not.toHaveBeenCalled();
});
describe('when modal submitted', () => {
beforeEach(() => {
wrapper.vm.$refs.form.requestSubmit = jest.fn();
findModal().vm.$emit('primary');
});
it('calls requestSubmit', () => {
wrapper.vm.submitModal();
expect(wrapper.vm.$refs.form.requestSubmit).toHaveBeenCalled();
});
});
describe('dismiss', () => {
it('removes gl-modal', () => {
expect(findModal().exists()).toBeTruthy();
wrapper.vm.dismiss();
return wrapper.vm.$nextTick(() => {
expect(findModal().exists()).toBeFalsy();
});
it('submits form', () => {
expect(findFormData()).toEqual([
{ name: '_method', value: testModalProps.method },
{ name: 'authenticity_token', value: 'test-csrf-token' },
]);
expect(findForm().element.submit).toHaveBeenCalled();
});
});
});

View file

@ -149,6 +149,34 @@ describe Banzai::Filter::RepositoryLinkFilter do
end
shared_examples :valid_repository do
it 'handles Gitaly unavailable exceptions gracefully' do
allow_next_instance_of(Gitlab::GitalyClient::BlobService) do |blob_service|
allow(blob_service).to receive(:get_blob_types).and_raise(GRPC::Unavailable)
end
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
an_instance_of(GRPC::Unavailable), project_id: project.id
)
doc = ""
expect { doc = filter(link('doc/api/README.md')) }.not_to raise_error
expect(doc.at_css('a')['href'])
.to eq "/#{project_path}/-/blob/#{ref}/doc/api/README.md"
end
it 'handles Gitaly timeout exceptions gracefully' do
allow_next_instance_of(Gitlab::GitalyClient::BlobService) do |blob_service|
allow(blob_service).to receive(:get_blob_types).and_raise(GRPC::DeadlineExceeded)
end
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
an_instance_of(GRPC::DeadlineExceeded), project_id: project.id
)
doc = ""
expect { doc = filter(link('doc/api/README.md')) }.not_to raise_error
expect(doc.at_css('a')['href'])
.to eq "/#{project_path}/-/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds absolute URL for a file in the repo' do
doc = filter(link('/doc/api/README.md'))
expect(doc.at_css('a')['href'])

View file

@ -46,11 +46,11 @@ describe Clusters::Applications::CertManager do
expect(subject.name).to eq('certmanager')
expect(subject.chart).to eq('certmanager/cert-manager')
expect(subject.repository).to eq('https://charts.jetstack.io')
expect(subject.version).to eq('v0.9.1')
expect(subject.version).to eq('v0.10.1')
expect(subject).to be_rbac
expect(subject.files).to eq(cert_manager.files.merge(cluster_issuer_file))
expect(subject.preinstall).to eq([
'kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.9/deploy/manifests/00-crds.yaml',
'kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.10/deploy/manifests/00-crds.yaml',
'kubectl label --overwrite namespace gitlab-managed-apps certmanager.k8s.io/disable-validation=true'
])
expect(subject.postinstall).to eq([
@ -82,7 +82,7 @@ describe Clusters::Applications::CertManager do
let(:cert_manager) { create(:clusters_applications_cert_manager, :errored, version: '0.0.1') }
it 'is initialized with the locked version' do
expect(subject.version).to eq('v0.9.1')
expect(subject.version).to eq('v0.10.1')
end
end
end