summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/jira_import/components/jira_import_app.vue20
-rw-r--r--app/assets/javascripts/jira_import/components/jira_import_setup.vue25
-rw-r--r--app/assets/javascripts/jira_import/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/prometheus/metrics/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/prometheus/metrics/new/index.js3
-rw-r--r--app/assets/stylesheets/pages/reports.scss9
-rw-r--r--app/controllers/projects/import/jira_controller.rb4
-rw-r--r--app/graphql/resolvers/projects/jira_imports_resolver.rb2
-rw-r--r--app/models/clusters/cluster.rb1
-rw-r--r--app/models/group_group_link.rb2
-rw-r--r--app/models/project.rb6
-rw-r--r--app/models/project_group_link.rb9
-rw-r--r--app/services/jira_import/start_import_service.rb2
-rw-r--r--app/views/projects/import/jira/show.html.haml4
-rw-r--r--app/views/projects/issues/import_csv/_button.html.haml2
-rw-r--r--app/workers/concerns/gitlab/jira_import/import_worker.rb2
-rw-r--r--app/workers/gitlab/jira_import/stage/start_import_worker.rb2
-rw-r--r--changelogs/unreleased/119235-extra-cluster-usage-data.yml5
-rw-r--r--doc/administration/high_availability/database.md2
-rw-r--r--doc/api/api_resources.md6
-rw-r--r--doc/api/project_vulnerabilities.md215
-rw-r--r--doc/api/vulnerabilities.md223
-rw-r--r--doc/api/vulnerability_exports.md138
-rw-r--r--doc/api/vulnerability_issue_links.md217
-rw-r--r--doc/development/packages.md2
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md6
-rw-r--r--doc/user/application_security/offline_deployments/index.md1
-rw-r--r--doc/user/application_security/vulnerabilities/img/standalone_vulnerability_page_v12_10.pngbin0 -> 26548 bytes
-rw-r--r--doc/user/application_security/vulnerabilities/index.md69
-rw-r--r--doc/user/compliance/license_compliance/index.md19
-rw-r--r--doc/user/permissions.md22
-rw-r--r--lib/gitlab/git_access_result/custom_action.rb2
-rw-r--r--lib/gitlab/jira_import/base_importer.rb2
-rw-r--r--lib/gitlab/tracking.rb7
-rw-r--r--lib/gitlab/usage_data.rb3
-rw-r--r--locale/gitlab.pot53
-rw-r--r--package.json4
-rw-r--r--spec/controllers/groups/shared_projects_controller_spec.rb2
-rw-r--r--spec/factories/group_group_links.rb8
-rw-r--r--spec/factories/project_group_links.rb5
-rw-r--r--spec/factories/usage_data.rb4
-rw-r--r--spec/finders/group_descendants_finder_spec.rb2
-rw-r--r--spec/frontend/jira_import/components/jira_import_app_spec.js38
-rw-r--r--spec/frontend/jira_import/components/jira_import_setup_spec.js28
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb10
-rw-r--r--spec/models/clusters/cluster_spec.rb16
-rw-r--r--spec/models/group_group_link_spec.rb16
-rw-r--r--spec/models/project_group_link_spec.rb17
-rw-r--r--spec/requests/api/internal/base_spec.rb2
-rw-r--r--spec/support/helpers/usage_data_helpers.rb3
-rw-r--r--spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb1
-rw-r--r--yarn.lock18
52 files changed, 1203 insertions, 62 deletions
diff --git a/app/assets/javascripts/jira_import/components/jira_import_app.vue b/app/assets/javascripts/jira_import/components/jira_import_app.vue
index 4b19c4d1b17..6efac949979 100644
--- a/app/assets/javascripts/jira_import/components/jira_import_app.vue
+++ b/app/assets/javascripts/jira_import/components/jira_import_app.vue
@@ -1,13 +1,25 @@
<script>
import getJiraProjects from '../queries/getJiraProjects.query.graphql';
+import JiraImportSetup from './jira_import_setup.vue';
export default {
name: 'JiraImportApp',
+ components: {
+ JiraImportSetup,
+ },
props: {
+ isJiraConfigured: {
+ type: Boolean,
+ required: true,
+ },
projectPath: {
type: String,
required: true,
},
+ setupIllustration: {
+ type: String,
+ required: true,
+ },
},
apollo: {
getJiraImports: {
@@ -18,11 +30,17 @@ export default {
};
},
update: data => data.project.jiraImports,
+ skip() {
+ return !this.isJiraConfigured;
+ },
},
},
};
</script>
<template>
- <div></div>
+ <div>
+ <jira-import-setup v-if="!isJiraConfigured" :illustration="setupIllustration" />
+ <div v-else></div>
+ </div>
</template>
diff --git a/app/assets/javascripts/jira_import/components/jira_import_setup.vue b/app/assets/javascripts/jira_import/components/jira_import_setup.vue
new file mode 100644
index 00000000000..917930397f4
--- /dev/null
+++ b/app/assets/javascripts/jira_import/components/jira_import_setup.vue
@@ -0,0 +1,25 @@
+<script>
+export default {
+ name: 'JiraImportSetup',
+ props: {
+ illustration: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="empty-state">
+ <div class="svg-content">
+ <img :src="illustration" :alt="__('Set up Jira Integration illustration')" />
+ </div>
+ <div class="text-content d-flex flex-column align-items-center">
+ <p>{{ __('You will first need to set up Jira Integration to use this feature.') }}</p>
+ <a class="btn btn-success" href="../services/jira/edit">
+ {{ __('Set up Jira Integration') }}
+ </a>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jira_import/index.js b/app/assets/javascripts/jira_import/index.js
index a17313fd774..13b16b81c49 100644
--- a/app/assets/javascripts/jira_import/index.js
+++ b/app/assets/javascripts/jira_import/index.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
+import { parseBoolean } from '~/lib/utils/common_utils';
import App from './components/jira_import_app.vue';
Vue.use(VueApollo);
@@ -23,7 +24,9 @@ export default function mountJiraImportApp() {
render(createComponent) {
return createComponent(App, {
props: {
+ isJiraConfigured: parseBoolean(el.dataset.isJiraConfigured),
projectPath: el.dataset.projectPath,
+ setupIllustration: el.dataset.setupIllustration,
},
});
},
diff --git a/app/assets/javascripts/pages/projects/prometheus/metrics/edit/index.js b/app/assets/javascripts/pages/projects/prometheus/metrics/edit/index.js
new file mode 100644
index 00000000000..2fd047675b9
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/prometheus/metrics/edit/index.js
@@ -0,0 +1,3 @@
+import customMetrics from '~/custom_metrics';
+
+document.addEventListener('DOMContentLoaded', customMetrics);
diff --git a/app/assets/javascripts/pages/projects/prometheus/metrics/new/index.js b/app/assets/javascripts/pages/projects/prometheus/metrics/new/index.js
new file mode 100644
index 00000000000..2fd047675b9
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/prometheus/metrics/new/index.js
@@ -0,0 +1,3 @@
+import customMetrics from '~/custom_metrics';
+
+document.addEventListener('DOMContentLoaded', customMetrics);
diff --git a/app/assets/stylesheets/pages/reports.scss b/app/assets/stylesheets/pages/reports.scss
index 390ebd48685..56194f0af67 100644
--- a/app/assets/stylesheets/pages/reports.scss
+++ b/app/assets/stylesheets/pages/reports.scss
@@ -63,15 +63,6 @@
list-style: none;
padding: 0 1px;
margin: 0;
-
- .license-item {
- line-height: $gl-padding-32;
-
- .license-packages {
- font-size: $label-font-size;
- }
-
- }
}
.report-block-list-icon {
diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb
index b5adef399c7..ca427928d85 100644
--- a/app/controllers/projects/import/jira_controller.rb
+++ b/app/controllers/projects/import/jira_controller.rb
@@ -7,6 +7,7 @@ module Projects
before_action :jira_integration_configured?
def show
+ @is_jira_configured = @project.jira_service.present?
return if Feature.enabled?(:jira_issue_import_vue, @project)
unless @project.latest_jira_import&.in_progress?
@@ -39,12 +40,13 @@ module Projects
private
def jira_import_enabled?
- return if Feature.enabled?(:jira_issue_import, @project)
+ return if @project.jira_issues_import_feature_flag_enabled?
redirect_to project_issues_path(@project)
end
def jira_integration_configured?
+ return if Feature.enabled?(:jira_issue_import_vue, @project)
return if @project.jira_service
flash[:notice] = _("Configure the Jira integration first on your project's %{strong_start} Settings > Integrations > Jira%{strong_end} page." %
diff --git a/app/graphql/resolvers/projects/jira_imports_resolver.rb b/app/graphql/resolvers/projects/jira_imports_resolver.rb
index 9f71d4f187e..b0784b3cdf7 100644
--- a/app/graphql/resolvers/projects/jira_imports_resolver.rb
+++ b/app/graphql/resolvers/projects/jira_imports_resolver.rb
@@ -14,7 +14,7 @@ module Resolvers
end
def authorized_resource?(project)
- return false unless Feature.enabled?(:jira_issue_import, project)
+ return false unless project.jira_issues_import_feature_flag_enabled?
Ability.allowed?(context[:current_user], :admin_project, project)
end
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 42771eaa82a..9ef3d64f21a 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -123,6 +123,7 @@ module Clusters
scope :managed, -> { where(managed: true) }
scope :with_persisted_applications, -> { eager_load(*APPLICATIONS_ASSOCIATIONS) }
scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
+ scope :with_management_project, -> { where.not(management_project: nil) }
scope :for_project_namespace, -> (namespace_id) { joins(:projects).where(projects: { namespace_id: namespace_id }) }
diff --git a/app/models/group_group_link.rb b/app/models/group_group_link.rb
index 58c188369da..c233f59b1a6 100644
--- a/app/models/group_group_link.rb
+++ b/app/models/group_group_link.rb
@@ -13,6 +13,8 @@ class GroupGroupLink < ApplicationRecord
validates :group_access, inclusion: { in: Gitlab::Access.all_values },
presence: true
+ scope :non_guests, -> { where('group_access > ?', Gitlab::Access::GUEST) }
+
def self.access_options
Gitlab::Access.options_with_owner
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 1f968cdfad1..4cd92b119b4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -786,6 +786,10 @@ class Project < ApplicationRecord
Feature.enabled?(:context_commits, default_enabled: true)
end
+ def jira_issues_import_feature_flag_enabled?
+ Feature.enabled?(:jira_issue_import, self)
+ end
+
def team
@team ||= ProjectTeam.new(self)
end
@@ -968,7 +972,7 @@ class Project < ApplicationRecord
end
def jira_import?
- import_type == 'jira' && latest_jira_import.present? && Feature.enabled?(:jira_issue_import, self)
+ import_type == 'jira' && latest_jira_import.present? && jira_issues_import_feature_flag_enabled?
end
def gitlab_project_import?
diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb
index b4071c6d4a6..f1c491d1a05 100644
--- a/app/models/project_group_link.rb
+++ b/app/models/project_group_link.rb
@@ -3,11 +3,6 @@
class ProjectGroupLink < ApplicationRecord
include Expirable
- GUEST = 10
- REPORTER = 20
- DEVELOPER = 30
- MAINTAINER = 40
-
belongs_to :project
belongs_to :group
@@ -18,6 +13,8 @@ class ProjectGroupLink < ApplicationRecord
validates :group_access, inclusion: { in: Gitlab::Access.values }, presence: true
validate :different_group
+ scope :non_guests, -> { where('group_access > ?', Gitlab::Access::GUEST) }
+
after_commit :refresh_group_members_authorized_projects
alias_method :shared_with_group, :group
@@ -27,7 +24,7 @@ class ProjectGroupLink < ApplicationRecord
end
def self.default_access
- DEVELOPER
+ Gitlab::Access::DEVELOPER
end
def self.search(query)
diff --git a/app/services/jira_import/start_import_service.rb b/app/services/jira_import/start_import_service.rb
index fbbd2d883f0..134cef089e7 100644
--- a/app/services/jira_import/start_import_service.rb
+++ b/app/services/jira_import/start_import_service.rb
@@ -44,7 +44,7 @@ module JiraImport
end
def validate
- return build_error_response(_('Jira import feature is disabled.')) unless Feature.enabled?(:jira_issue_import, project)
+ return build_error_response(_('Jira import feature is disabled.')) unless project.jira_issues_import_feature_flag_enabled?
return build_error_response(_('You do not have permissions to run the import.')) unless user.can?(:admin_project, project)
return build_error_response(_('Jira integration not configured.')) unless project.jira_service&.active?
return build_error_response(_('Unable to find Jira project to import data from.')) if jira_project_key.blank?
diff --git a/app/views/projects/import/jira/show.html.haml b/app/views/projects/import/jira/show.html.haml
index cfc4baa1c25..6003f33f0ba 100644
--- a/app/views/projects/import/jira/show.html.haml
+++ b/app/views/projects/import/jira/show.html.haml
@@ -1,5 +1,7 @@
- if Feature.enabled?(:jira_issue_import_vue, @project)
- .js-jira-import-root{ data: { project_path: @project.full_path } }
+ .js-jira-import-root{ data: { project_path: @project.full_path,
+ is_jira_configured: @is_jira_configured.to_s,
+ setup_illustration: image_path('illustrations/manual_action.svg') } }
- else
- title = _('Jira Issue Import')
- page_title title
diff --git a/app/views/projects/issues/import_csv/_button.html.haml b/app/views/projects/issues/import_csv/_button.html.haml
index 0a352d26b0b..07c34b51037 100644
--- a/app/views/projects/issues/import_csv/_button.html.haml
+++ b/app/views/projects/issues/import_csv/_button.html.haml
@@ -1,6 +1,6 @@
- type = local_assigns.fetch(:type, :icon)
-- if Feature.enabled?(:jira_issue_import, @project)
+- if @project.jira_issues_import_feature_flag_enabled?
.dropdown.btn-group
%button.btn.rounded-right.text-center{ class: ('has-tooltip' if type == :icon), title: (_('Import issues') if type == :icon),
data: { toggle: 'dropdown' }, 'aria-label' => _('Import issues'), 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
diff --git a/app/workers/concerns/gitlab/jira_import/import_worker.rb b/app/workers/concerns/gitlab/jira_import/import_worker.rb
index 169d3797b88..537300e6eba 100644
--- a/app/workers/concerns/gitlab/jira_import/import_worker.rb
+++ b/app/workers/concerns/gitlab/jira_import/import_worker.rb
@@ -26,7 +26,7 @@ module Gitlab
def can_import?(project)
return false unless project
- return false if Feature.disabled?(:jira_issue_import, project)
+ return false unless project.jira_issues_import_feature_flag_enabled?
project.latest_jira_import&.started?
end
diff --git a/app/workers/gitlab/jira_import/stage/start_import_worker.rb b/app/workers/gitlab/jira_import/stage/start_import_worker.rb
index 1561ad90cc1..5b36feadbd1 100644
--- a/app/workers/gitlab/jira_import/stage/start_import_worker.rb
+++ b/app/workers/gitlab/jira_import/stage/start_import_worker.rb
@@ -25,7 +25,7 @@ module Gitlab
def start_import
return false unless project
- return false if Feature.disabled?(:jira_issue_import, project)
+ return false unless project.jira_issues_import_feature_flag_enabled?
return true if start(project.latest_jira_import)
Gitlab::Import::Logger.info(
diff --git a/changelogs/unreleased/119235-extra-cluster-usage-data.yml b/changelogs/unreleased/119235-extra-cluster-usage-data.yml
new file mode 100644
index 00000000000..acb5f924239
--- /dev/null
+++ b/changelogs/unreleased/119235-extra-cluster-usage-data.yml
@@ -0,0 +1,5 @@
+---
+title: Add usage data metrics for instance level clusters and clusters with management projects
+merge_request: 28510
+author:
+type: added
diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md
index 9cb95559412..01af971d664 100644
--- a/doc/administration/high_availability/database.md
+++ b/doc/administration/high_availability/database.md
@@ -199,7 +199,7 @@ Few notes on the service itself:
- Passwords will be stored in the following locations:
- `/etc/gitlab/gitlab.rb`: hashed
- `/var/opt/gitlab/pgbouncer/pg_auth`: hashed
- - `/var/opt/gitlab/gitlab-consul/.pgpass`: plaintext
+ - `/var/opt/gitlab/consul/.pgpass`: plaintext
##### PostgreSQL information
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
index 34d2c40d1f9..aebbc95750d 100644
--- a/doc/api/api_resources.md
+++ b/doc/api/api_resources.md
@@ -71,8 +71,10 @@ The following API resources are available in the project context:
| [Services](services.md) | `/projects/:id/services` |
| [Tags](tags.md) | `/projects/:id/repository/tags` |
| [Visual Review discussions](visual_review_discussions.md) **(STARTER**) | `/projects/:id/merge_requests/:merge_request_id/visual_review_discussions` |
-| [Vulnerabilities](vulnerabilities.md) **(ULTIMATE)** | `/projects/:id/vulnerabilities` |
-| [Vulnerability Findings](vulnerability_findings.md) **(ULTIMATE)** | `/projects/:id/vulnerability_findings` |
+| [Vulnerabilities](vulnerabilities.md) **(ULTIMATE)** | `/vulnerabilities/:id` |
+| [Vulnerability exports](vulnerability_exports.md) **(ULTIMATE)** | `/projects/:id/vulnerability_exports` |
+| [Project vulnerabilities](project_vulnerabilities.md) **(ULTIMATE)** | `/projects/:id/vulnerabilities` |
+| [Vulnerability findings](vulnerability_findings.md) **(ULTIMATE)** | `/projects/:id/vulnerability_findings` |
| [Wikis](wikis.md) | `/projects/:id/wikis` |
## Group resources
diff --git a/doc/api/project_vulnerabilities.md b/doc/api/project_vulnerabilities.md
new file mode 100644
index 00000000000..84bbc789b0c
--- /dev/null
+++ b/doc/api/project_vulnerabilities.md
@@ -0,0 +1,215 @@
+# Project Vulnerabilities API **(ULTIMATE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
+
+CAUTION: **Caution:**
+This API is currently in development and is protected by a **disabled**
+[feature flag](../development/feature_flags/index.md).
+On a self-managed GitLab instance, an administrator can enable it by starting the Rails console
+(`sudo gitlab-rails console`) and then running the following command: `Feature.enable(:first_class_vulnerabilities)`.
+To test if the Vulnerabilities API was successfully enabled, run the following command:
+`Feature.enabled?(:first_class_vulnerabilities)`.
+
+CAUTION: **Caution:**
+This API is in an alpha stage and considered unstable.
+The response payload may be subject to change or breakage
+across GitLab releases.
+
+Every API call to vulnerabilities must be [authenticated](README.md#authentication).
+
+Vulnerability permissions inherit permissions from their project. If a project is
+private, and a user isn't a member of the project to which the vulnerability
+belongs, requests to that project will return a `404 Not Found` status code.
+
+## Vulnerabilities pagination
+
+API results are paginated, and `GET` requests return 20 results at a time by default.
+
+Read more on [pagination](README.md#pagination).
+
+## List project vulnerabilities
+
+List all of a project's vulnerabilities.
+
+If an authenticated user does not have permission to
+[use the Project Security Dashboard](../user/permissions.md#project-members-permissions),
+`GET` requests for vulnerabilities of this project will result in a `403` status code.
+
+```plaintext
+GET /projects/:id/vulnerabilities
+```
+
+| Attribute | Type | Required | Description |
+| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer or string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+
+```bash
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/vulnerabilities
+```
+
+Example response:
+
+```json
+[
+ {
+ "author_id": 1,
+ "confidence": "medium",
+ "created_at": "2020-04-07T14:01:04.655Z",
+ "description": null,
+ "dismissed_at": null,
+ "dismissed_by_id": null,
+ "due_date": null,
+ "finding": {
+ "confidence": "medium",
+ "created_at": "2020-04-07T14:01:04.630Z",
+ "id": 103,
+ "location_fingerprint": "228998b5db51d86d3b091939e2f5873ada0a14a1",
+ "metadata_version": "2.0",
+ "name": "Regular Expression Denial of Service in debug",
+ "primary_identifier_id": 135,
+ "project_fingerprint": "05e7cc9978ca495cf739a9f707ed34811e41c615",
+ "project_id": 24,
+ "raw_metadata": "{\"category\":\"dependency_scanning\",\"name\":\"Regular Expression Denial of Service\",\"message\":\"Regular Expression Denial of Service in debug\",\"description\":\"The debug module is vulnerable to regular expression denial of service when untrusted user input is passed into the `o` formatter. It takes around 50k characters to block for 2 seconds making this a low severity issue.\",\"cve\":\"yarn.lock:debug:gemnasium:37283ed4-0380-40d7-ada7-2d994afcc62a\",\"severity\":\"Unknown\",\"solution\":\"Upgrade to latest versions.\",\"scanner\":{\"id\":\"gemnasium\",\"name\":\"Gemnasium\"},\"location\":{\"file\":\"yarn.lock\",\"dependency\":{\"package\":{\"name\":\"debug\"},\"version\":\"1.0.5\"}},\"identifiers\":[{\"type\":\"gemnasium\",\"name\":\"Gemnasium-37283ed4-0380-40d7-ada7-2d994afcc62a\",\"value\":\"37283ed4-0380-40d7-ada7-2d994afcc62a\",\"url\":\"https://deps.sec.gitlab.com/packages/npm/debug/versions/1.0.5/advisories\"}],\"links\":[{\"url\":\"https://nodesecurity.io/advisories/534\"},{\"url\":\"https://github.com/visionmedia/debug/issues/501\"},{\"url\":\"https://github.com/visionmedia/debug/pull/504\"}],\"remediations\":[null]}",
+ "report_type": "dependency_scanning",
+ "scanner_id": 63,
+ "severity": "low",
+ "updated_at": "2020-04-07T14:01:04.664Z",
+ "uuid": "f1d528ae-d0cc-47f6-a72f-936cec846ae7",
+ "vulnerability_id": 103
+ },
+ "id": 103,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "project": {
+ "created_at": "2020-04-07T13:54:25.634Z",
+ "description": "",
+ "id": 24,
+ "name": "security-reports",
+ "name_with_namespace": "gitlab-org / security-reports",
+ "path": "security-reports",
+ "path_with_namespace": "gitlab-org/security-reports"
+ },
+ "project_default_branch": "master",
+ "report_type": "dependency_scanning",
+ "resolved_at": null,
+ "resolved_by_id": null,
+ "resolved_on_default_branch": false,
+ "severity": "low",
+ "start_date": null,
+ "state": "detected",
+ "title": "Regular Expression Denial of Service in debug",
+ "updated_at": "2020-04-07T14:01:04.655Z",
+ "updated_by_id": null
+ }
+]
+```
+
+## New vulnerability
+
+Creates a new vulnerability.
+
+If an authenticated user does not have a permission to
+[create a new vulnerability](../user/permissions.md#project-members-permissions),
+this request will result in a `403` status code.
+
+```plaintext
+POST /projects/:id/vulnerabilities?finding_id=<your_finding_id>
+```
+
+| Attribute | Type | Required | Description |
+| ------------------- | ----------------- | ---------- | -----------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer or string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) which the authenticated user is a member of |
+| `finding_id` | integer or string | yes | The ID of a Vulnerability Finding from which the new Vulnerability will be created |
+
+The other attributes of a newly created Vulnerability are populated from
+its source Vulnerability Finding, or with these default values:
+
+| Attribute | Value |
+|--------------|-------------------------------------------------------|
+| `author` | The authenticated user |
+| `title` | The `name` attribute of a Vulnerability Finding |
+| `state` | `opened` |
+| `severity` | The `severity` attribute of a Vulnerability Finding |
+| `confidence` | The `confidence` attribute of a Vulnerability Finding |
+
+```shell
+curl --header POST "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/vulnerabilities?finding_id=1
+```
+
+Example response:
+
+```json
+{
+ "author_id": 1,
+ "confidence": "medium",
+ "created_at": "2020-04-07T14:01:04.655Z",
+ "description": null,
+ "dismissed_at": null,
+ "dismissed_by_id": null,
+ "due_date": null,
+ "finding": {
+ "confidence": "medium",
+ "created_at": "2020-04-07T14:01:04.630Z",
+ "id": 103,
+ "location_fingerprint": "228998b5db51d86d3b091939e2f5873ada0a14a1",
+ "metadata_version": "2.0",
+ "name": "Regular Expression Denial of Service in debug",
+ "primary_identifier_id": 135,
+ "project_fingerprint": "05e7cc9978ca495cf739a9f707ed34811e41c615",
+ "project_id": 24,
+ "raw_metadata": "{\"category\":\"dependency_scanning\",\"name\":\"Regular Expression Denial of Service\",\"message\":\"Regular Expression Denial of Service in debug\",\"description\":\"The debug module is vulnerable to regular expression denial of service when untrusted user input is passed into the `o` formatter. It takes around 50k characters to block for 2 seconds making this a low severity issue.\",\"cve\":\"yarn.lock:debug:gemnasium:37283ed4-0380-40d7-ada7-2d994afcc62a\",\"severity\":\"Unknown\",\"solution\":\"Upgrade to latest versions.\",\"scanner\":{\"id\":\"gemnasium\",\"name\":\"Gemnasium\"},\"location\":{\"file\":\"yarn.lock\",\"dependency\":{\"package\":{\"name\":\"debug\"},\"version\":\"1.0.5\"}},\"identifiers\":[{\"type\":\"gemnasium\",\"name\":\"Gemnasium-37283ed4-0380-40d7-ada7-2d994afcc62a\",\"value\":\"37283ed4-0380-40d7-ada7-2d994afcc62a\",\"url\":\"https://deps.sec.gitlab.com/packages/npm/debug/versions/1.0.5/advisories\"}],\"links\":[{\"url\":\"https://nodesecurity.io/advisories/534\"},{\"url\":\"https://github.com/visionmedia/debug/issues/501\"},{\"url\":\"https://github.com/visionmedia/debug/pull/504\"}],\"remediations\":[null]}",
+ "report_type": "dependency_scanning",
+ "scanner_id": 63,
+ "severity": "low",
+ "updated_at": "2020-04-07T14:01:04.664Z",
+ "uuid": "f1d528ae-d0cc-47f6-a72f-936cec846ae7",
+ "vulnerability_id": 103
+ },
+ "id": 103,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "project": {
+ "created_at": "2020-04-07T13:54:25.634Z",
+ "description": "",
+ "id": 24,
+ "name": "security-reports",
+ "name_with_namespace": "gitlab-org / security-reports",
+ "path": "security-reports",
+ "path_with_namespace": "gitlab-org/security-reports"
+ },
+ "project_default_branch": "master",
+ "report_type": "dependency_scanning",
+ "resolved_at": null,
+ "resolved_by_id": null,
+ "resolved_on_default_branch": false,
+ "severity": "low",
+ "start_date": null,
+ "state": "detected",
+ "title": "Regular Expression Denial of Service in debug",
+ "updated_at": "2020-04-07T14:01:04.655Z",
+ "updated_by_id": null
+}
+```
+
+### Errors
+
+This error occurs when a Finding chosen to create a Vulnerability from is not found, or
+is already associated with a different Vulnerability:
+
+```plaintext
+A Vulnerability Finding is not found or already attached to a different Vulnerability
+```
+
+Status code: `400`
+
+Example response:
+
+```json
+{
+ "message": {
+ "base": [
+ "finding is not found or is already attached to a vulnerability"
+ ]
+ }
+}
+```
diff --git a/doc/api/vulnerabilities.md b/doc/api/vulnerabilities.md
index 21b3a6f4c96..ff1a6a7ebcd 100644
--- a/doc/api/vulnerabilities.md
+++ b/doc/api/vulnerabilities.md
@@ -1,3 +1,224 @@
# Vulnerabilities API **(ULTIMATE)**
-This document was moved to [another location](vulnerability_findings.md).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
+
+NOTE: **Note:**
+The former Vulnerabilities API was renamed to Vulnerability Findings API
+and its documentation was moved to [a different location](vulnerability_findings.md).
+This document now describes the new Vulnerabilities API that provides access to
+[Standalone Vulnerabilities](https://gitlab.com/groups/gitlab-org/-/epics/634).
+
+CAUTION: **Caution:**
+This API is currently in development and is protected by a **disabled**
+[feature flag](../development/feature_flags/index.md).
+On a self-managed GitLab instance, an administrator can enable it by starting the Rails console
+(`sudo gitlab-rails console`) and then running the following command: `Feature.enable(:first_class_vulnerabilities)`.
+To test if the Vulnerabilities API was successfully enabled, run the following command:
+`Feature.enabled?(:first_class_vulnerabilities)`.
+
+CAUTION: **Caution:**
+This API is in an alpha stage and considered unstable.
+The response payload may be subject to change or breakage
+across GitLab releases.
+
+Every API call to vulnerabilities must be [authenticated](README.md#authentication).
+
+Vulnerability permissions inherit permissions from their project. If a project is
+private, and a user isn't a member of the project to which the vulnerability
+belongs, requests to that project will return a `404 Not Found` status code.
+
+## Single vulnerability
+
+Gets a single vulnerability
+
+```plaintext
+GET /vulnerabilities/:id
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer or string | yes | The ID of a Vulnerability to get |
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/vulnerabilities/1
+```
+
+Example response:
+
+```json
+{
+ "id": 1,
+ "title": "Predictable pseudorandom number generator",
+ "description": null,
+ "state": "opened",
+ "severity": "medium",
+ "confidence": "medium",
+ "report_type": "sast",
+ "project": {
+ "id": 32,
+ "name": "security-reports",
+ "full_path": "/gitlab-examples/security/security-reports",
+ "full_name": "gitlab-examples / security / security-reports"
+ },
+ "author_id": 1,
+ "updated_by_id": null,
+ "last_edited_by_id": null,
+ "closed_by_id": null,
+ "start_date": null,
+ "due_date": null,
+ "created_at": "2019-10-13T15:08:40.219Z",
+ "updated_at": "2019-10-13T15:09:40.382Z",
+ "last_edited_at": null,
+ "closed_at": null
+}
+```
+
+## Confirm vulnerability
+
+Confirms a given vulnerability. Returns status code `304` if the vulnerability is already confirmed.
+
+If an authenticated user does not have permission to
+[confirm vulnerabilities](../user/permissions.md#project-members-permissions),
+this request will result in a `403` status code.
+
+```plaintext
+POST /vulnerabilities/:id/confirm
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer or string | yes | The ID of a vulnerability to confirm |
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/vulnerabilities/5/confirm"
+```
+
+Example response:
+
+```json
+{
+ "id": 2,
+ "title": "Predictable pseudorandom number generator",
+ "description": null,
+ "state": "confirmed",
+ "severity": "medium",
+ "confidence": "medium",
+ "report_type": "sast",
+ "project": {
+ "id": 32,
+ "name": "security-reports",
+ "full_path": "/gitlab-examples/security/security-reports",
+ "full_name": "gitlab-examples / security / security-reports"
+ },
+ "author_id": 1,
+ "updated_by_id": null,
+ "last_edited_by_id": null,
+ "closed_by_id": null,
+ "start_date": null,
+ "due_date": null,
+ "created_at": "2019-10-13T15:08:40.219Z",
+ "updated_at": "2019-10-13T15:09:40.382Z",
+ "last_edited_at": null,
+ "closed_at": null
+}
+```
+
+## Resolve vulnerability
+
+Resolves a given vulnerability. Returns status code `304` if the vulnerability is already resolved.
+
+If an authenticated user does not have permission to
+[resolve vulnerabilities](../user/permissions.md#project-members-permissions),
+this request will result in a `403` status code.
+
+```plaintext
+POST /vulnerabilities/:id/resolve
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer or string | yes | The ID of a Vulnerability to resolve |
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/vulnerabilities/5/resolve"
+```
+
+Example response:
+
+```json
+{
+ "id": 2,
+ "title": "Predictable pseudorandom number generator",
+ "description": null,
+ "state": "resolved",
+ "severity": "medium",
+ "confidence": "medium",
+ "report_type": "sast",
+ "project": {
+ "id": 32,
+ "name": "security-reports",
+ "full_path": "/gitlab-examples/security/security-reports",
+ "full_name": "gitlab-examples / security / security-reports"
+ },
+ "author_id": 1,
+ "updated_by_id": null,
+ "last_edited_by_id": null,
+ "closed_by_id": null,
+ "start_date": null,
+ "due_date": null,
+ "created_at": "2019-10-13T15:08:40.219Z",
+ "updated_at": "2019-10-13T15:09:40.382Z",
+ "last_edited_at": null,
+ "closed_at": null
+}
+```
+
+## Dismiss vulnerability
+
+Dismisses a given vulnerability. Returns status code `304` if the vulnerability is already dismissed.
+
+If an authenticated user does not have permission to
+[dismiss vulnerabilities](../user/permissions.md#project-members-permissions),
+this request will result in a `403` status code.
+
+```plaintext
+POST /vulnerabilities/:id/dismiss
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer or string | yes | The ID of a vulnerability to dismiss |
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/vulnerabilities/5/dismiss"
+```
+
+Example response:
+
+```json
+{
+ "id": 2,
+ "title": "Predictable pseudorandom number generator",
+ "description": null,
+ "state": "closed",
+ "severity": "medium",
+ "confidence": "medium",
+ "report_type": "sast",
+ "project": {
+ "id": 32,
+ "name": "security-reports",
+ "full_path": "/gitlab-examples/security/security-reports",
+ "full_name": "gitlab-examples / security / security-reports"
+ },
+ "author_id": 1,
+ "updated_by_id": null,
+ "last_edited_by_id": null,
+ "closed_by_id": null,
+ "start_date": null,
+ "due_date": null,
+ "created_at": "2019-10-13T15:08:40.219Z",
+ "updated_at": "2019-10-13T15:09:40.382Z",
+ "last_edited_at": null,
+ "closed_at": null
+}
+```
diff --git a/doc/api/vulnerability_exports.md b/doc/api/vulnerability_exports.md
new file mode 100644
index 00000000000..f2666783087
--- /dev/null
+++ b/doc/api/vulnerability_exports.md
@@ -0,0 +1,138 @@
+# Project Vulnerabilities API **(ULTIMATE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/197494) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
+
+CAUTION: **Caution:**
+This API is currently in development and is protected by a **disabled**
+[feature flag](../development/feature_flags/index.md).
+On a self-managed GitLab instance, an administrator can enable it by starting the Rails console
+(`sudo gitlab-rails console`) and then running the following command: `Feature.enable(:first_class_vulnerabilities)`.
+To test if the Vulnerability Exports API was successfully enabled, run the following command:
+`Feature.enabled?(:first_class_vulnerabilities)`.
+
+CAUTION: **Caution:**
+This API is in an alpha stage and considered unstable.
+The response payload may be subject to change or breakage
+across GitLab releases.
+
+Every API call to vulnerability exports must be [authenticated](README.md#authentication).
+
+Vulnerability export permissions inherit permissions from their project. If a project is
+private and a user isn't a member of the project to which the vulnerability
+belongs, requests to that project return a `404 Not Found` status code.
+Vulnerability exports can be only accessed by the export's author.
+
+## Create vulnerability export
+
+Creates a new vulnerability export.
+
+If an authenticated user doesn't have permission to
+[create a new vulnerability](../user/permissions.md#project-members-permissions),
+this request results in a `403` status code.
+
+```plaintext
+POST /projects/:id/vulnerability_exports
+```
+
+| Attribute | Type | Required | Description |
+| ------------------- | ----------------- | ---------- | -----------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer or string | yes | The ID or [URL-encoded path](README.md#namespaced-path-encoding) of the project which the authenticated user is a member of |
+
+```shell
+curl --header POST "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/vulnerability_exports
+```
+
+The created vulnerability export will be automatically deleted after 1 hour.
+
+Example response:
+
+```json
+{
+ "id": 2,
+ "created_at": "2020-03-30T09:35:38.746Z",
+ "project_id": 1,
+ "format": "csv",
+ "status": "created",
+ "started_at": null,
+ "finished_at": null,
+ "_links": {
+ "self": "https://gitlab.example.com/api/v4/projects/1/vulnerability_exports/2",
+ "download": "https://gitlab.example.com/api/v4/projects/1/vulnerability_exports/2/download"
+ }
+}
+```
+
+## Get single vulnerability export
+
+Gets a single vulnerability export.
+
+```plaintext
+POST /projects/:id/vulnerability_exports/:vulnerability_export_id
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer or string | yes | The vulnerability's ID |
+| `vulnerability_export_id` | integer or string | yes | The vulnerability export's ID |
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/vulnerability_exports/2
+```
+
+If the vulnerability export isn't finished, the response is `202 Accepted`.
+
+Example response:
+
+```json
+{
+ "id": 2,
+ "created_at": "2020-03-30T09:35:38.746Z",
+ "project_id": 1,
+ "format": "csv",
+ "status": "finished",
+ "started_at": "2020-03-30T09:36:54.469Z",
+ "finished_at": "2020-03-30T09:36:55.008Z",
+ "_links": {
+ "self": "https://gitlab.example.com/api/v4/projects/1/vulnerability_exports/2",
+ "download": "https://gitlab.example.com/api/v4/projects/1/vulnerability_exports/2/download"
+ }
+}
+```
+
+## Download vulnerability export
+
+Downloads a single vulnerability export.
+
+```plaintext
+POST /projects/:id/vulnerability_exports/:vulnerability_export_id/download
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer or string | yes | The vulnerability's ID |
+| `vulnerability_export_id` | integer or string | yes | The vulnerability export's ID |
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/vulnerability_exports/2/download
+```
+
+The response will be `404 Not Found` if the vulnerability export is not finished yet or was not found.
+
+Example response:
+
+```csv
+Scanner Type,Scanner Name,Status,Vulnerability,Details,Additional Info,Severity,CVE
+container_scanning,Clair,confirmed,CVE-2017-16997 in glibc,,CVE-2017-16997 in glibc,critical,CVE-2017-16997
+container_scanning,Clair,detected,CVE-2017-18269 in glibc,,CVE-2017-18269 in glibc,critical,CVE-2017-18269
+container_scanning,Clair,detected,CVE-2018-1000001 in glibc,,CVE-2018-1000001 in glibc,high,CVE-2018-1000001
+container_scanning,Clair,detected,CVE-2016-10228 in glibc,,CVE-2016-10228 in glibc,medium,CVE-2016-10228
+container_scanning,Clair,confirmed,CVE-2010-4052 in glibc,,CVE-2010-4052 in glibc,low,CVE-2010-4052
+container_scanning,Clair,detected,CVE-2018-18520 in elfutils,,CVE-2018-18520 in elfutils,low,CVE-2018-18520
+container_scanning,Clair,detected,CVE-2018-16869 in nettle,,CVE-2018-16869 in nettle,unknown,CVE-2018-16869
+dependency_scanning,Gemnasium,detected,Regular Expression Denial of Service in debug,,Regular Expression Denial of Service in debug,unknown,yarn.lock:debug:gemnasium:37283ed4-0380-40d7-ada7-2d994afcc62a
+dependency_scanning,Gemnasium,detected,Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js,,Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js,unknown,yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98
+sast,Find Security Bugs,detected,Predictable pseudorandom number generator,,Predictable pseudorandom number generator,medium,818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM:src/main/java/com/gitlab/security_products/tests/App.java:47
+sast,Find Security Bugs,detected,Cipher with no integrity,,Cipher with no integrity,medium,e6449b89335daf53c0db4c0219bc1634:CIPHER_INTEGRITY:src/main/java/com/gitlab/security_products/tests/App.java:29
+sast,Find Security Bugs,detected,Predictable pseudorandom number generator,,Predictable pseudorandom number generator,medium,e8ff1d01f74cd372f78da8f5247d3e73:PREDICTABLE_RANDOM:src/main/java/com/gitlab/security_products/tests/App.java:41
+sast,Find Security Bugs,confirmed,ECB mode is insecure 2,,ECB mode is insecure,medium,ea0f905fc76f2739d5f10a1fd1e37a10:ECB_MODE:src/main/java/com/gitlab/security_products/tests/App.java:29
+```
diff --git a/doc/api/vulnerability_issue_links.md b/doc/api/vulnerability_issue_links.md
new file mode 100644
index 00000000000..05213e788c4
--- /dev/null
+++ b/doc/api/vulnerability_issue_links.md
@@ -0,0 +1,217 @@
+# Vulnerability Issue links API **(ULTIMATE)**
+
+CAUTION: **Caution:**
+This API is in an alpha stage and considered unstable.
+The response payload may be subject to change or breakage
+across GitLab releases.
+
+## List related issues
+
+Get a list of related issues of a given issue, sorted by the relationship creation datetime (ascending).
+Issues will be filtered according to the user authorizations.
+
+```plaintext
+GET /projects/:id/issues/:issue_iid/links
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+|-------------|---------|----------|--------------------------------------|
+| `id` | integer or string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `issue_iid` | integer | yes | The internal ID of a project's issue |
+
+```json
+[
+ {
+ "id" : 84,
+ "iid" : 14,
+ "issue_link_id": 1
+ "project_id" : 4,
+ "created_at" : "2016-01-07T12:44:33.959Z",
+ "title" : "Issues with auth",
+ "state" : "opened",
+ "assignees" : [],
+ "assignee" : null,
+ "labels" : [
+ "bug"
+ ],
+ "author" : {
+ "name" : "Alexandra Bashirian",
+ "avatar_url" : null,
+ "state" : "active",
+ "web_url" : "https://gitlab.example.com/eileen.lowe",
+ "id" : 18,
+ "username" : "eileen.lowe"
+ },
+ "description" : null,
+ "updated_at" : "2016-01-07T12:44:33.959Z",
+ "milestone" : null,
+ "subscribed" : true,
+ "user_notes_count": 0,
+ "due_date": null,
+ "web_url": "http://example.com/example/example/issues/14",
+ "confidential": false,
+ "weight": null,
+ }
+]
+```
+
+## Create an issue link
+
+Creates a two-way relation between two issues. User must be allowed to update both issues in order to succeed.
+
+```plaintext
+POST /projects/:id/issues/:issue_iid/links
+```
+
+| Attribute | Type | Required | Description |
+|-------------|---------|----------|--------------------------------------|
+| `id` | integer or string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `issue_iid` | integer | yes | The internal ID of a project's issue |
+| `target_project_id` | integer or string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project |
+| `target_issue_iid` | integer or string | yes | The internal ID of a target project's issue |
+
+```json
+{
+ "source_issue" : {
+ "id" : 83,
+ "iid" : 11,
+ "project_id" : 4,
+ "created_at" : "2016-01-07T12:44:33.959Z",
+ "title" : "Issues with auth",
+ "state" : "opened",
+ "assignees" : [],
+ "assignee" : null,
+ "labels" : [
+ "bug"
+ ],
+ "author" : {
+ "name" : "Alexandra Bashirian",
+ "avatar_url" : null,
+ "state" : "active",
+ "web_url" : "https://gitlab.example.com/eileen.lowe",
+ "id" : 18,
+ "username" : "eileen.lowe"
+ },
+ "description" : null,
+ "updated_at" : "2016-01-07T12:44:33.959Z",
+ "milestone" : null,
+ "subscribed" : true,
+ "user_notes_count": 0,
+ "due_date": null,
+ "web_url": "http://example.com/example/example/issues/11",
+ "confidential": false,
+ "weight": null,
+ },
+ "target_issue" : {
+ "id" : 84,
+ "iid" : 14,
+ "project_id" : 4,
+ "created_at" : "2016-01-07T12:44:33.959Z",
+ "title" : "Issues with auth",
+ "state" : "opened",
+ "assignees" : [],
+ "assignee" : null,
+ "labels" : [
+ "bug"
+ ],
+ "author" : {
+ "name" : "Alexandra Bashirian",
+ "avatar_url" : null,
+ "state" : "active",
+ "web_url" : "https://gitlab.example.com/eileen.lowe",
+ "id" : 18,
+ "username" : "eileen.lowe"
+ },
+ "description" : null,
+ "updated_at" : "2016-01-07T12:44:33.959Z",
+ "milestone" : null,
+ "subscribed" : true,
+ "user_notes_count": 0,
+ "due_date": null,
+ "web_url": "http://example.com/example/example/issues/14",
+ "confidential": false,
+ "weight": null,
+ }
+}
+```
+
+## Delete an issue link
+
+Deletes an issue link, removing the two-way relationship.
+
+```plaintext
+DELETE /projects/:id/issues/:issue_iid/links/:issue_link_id
+```
+
+| Attribute | Type | Required | Description |
+|-------------|---------|----------|--------------------------------------|
+| `id` | integer or string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `issue_iid` | integer | yes | The internal ID of a project's issue |
+| `issue_link_id` | integer or string | yes | The ID of an issue relationship |
+
+```json
+{
+ "source_issue" : {
+ "id" : 83,
+ "iid" : 11,
+ "project_id" : 4,
+ "created_at" : "2016-01-07T12:44:33.959Z",
+ "title" : "Issues with auth",
+ "state" : "opened",
+ "assignees" : [],
+ "assignee" : null,
+ "labels" : [
+ "bug"
+ ],
+ "author" : {
+ "name" : "Alexandra Bashirian",
+ "avatar_url" : null,
+ "state" : "active",
+ "web_url" : "https://gitlab.example.com/eileen.lowe",
+ "id" : 18,
+ "username" : "eileen.lowe"
+ },
+ "description" : null,
+ "updated_at" : "2016-01-07T12:44:33.959Z",
+ "milestone" : null,
+ "subscribed" : true,
+ "user_notes_count": 0,
+ "due_date": null,
+ "web_url": "http://example.com/example/example/issues/11",
+ "confidential": false,
+ "weight": null,
+ },
+ "target_issue" : {
+ "id" : 84,
+ "iid" : 14,
+ "project_id" : 4,
+ "created_at" : "2016-01-07T12:44:33.959Z",
+ "title" : "Issues with auth",
+ "state" : "opened",
+ "assignees" : [],
+ "assignee" : null,
+ "labels" : [
+ "bug"
+ ],
+ "author" : {
+ "name" : "Alexandra Bashirian",
+ "avatar_url" : null,
+ "state" : "active",
+ "web_url" : "https://gitlab.example.com/eileen.lowe",
+ "id" : 18,
+ "username" : "eileen.lowe"
+ },
+ "description" : null,
+ "updated_at" : "2016-01-07T12:44:33.959Z",
+ "milestone" : null,
+ "subscribed" : true,
+ "user_notes_count": 0,
+ "due_date": null,
+ "web_url": "http://example.com/example/example/issues/14",
+ "confidential": false,
+ "weight": null,
+ }
+}
+```
diff --git a/doc/development/packages.md b/doc/development/packages.md
index 0880e053901..66b50ce12c8 100644
--- a/doc/development/packages.md
+++ b/doc/development/packages.md
@@ -63,6 +63,8 @@ The current state of existing package registries availability is:
| Maven | Yes | Yes | Yes |
| Conan | No - [open issue](https://gitlab.com/gitlab-org/gitlab/issues/11679) | No - [open issue](https://gitlab.com/gitlab-org/gitlab/issues/11679) | Yes |
| NPM | No - [open issue](https://gitlab.com/gitlab-org/gitlab/issues/36853) | Yes | No - [open issue](https://gitlab.com/gitlab-org/gitlab/issues/36853) |
+| NuGet | Yes | No - [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/36423) | No |
+| PyPI | Yes | No | No |
NOTE: **Note:** NPM is currently a hybrid of the instance level and group level.
It is using the top-level group or namespace as the defining portion of the name
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index cbfdf2d188c..f28bab6ad86 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -197,9 +197,11 @@ but commented out to help encourage others to add to it in the future. -->
|clusters_enabled|counts||
|project_clusters_enabled|counts||
|group_clusters_enabled|counts||
+|instance_clusters_enabled|counts||
|clusters_disabled|counts||
|project_clusters_disabled|counts||
|group_clusters_disabled|counts||
+|instance_clusters_disabled|counts||
|clusters_platforms_eks|counts||
|clusters_platforms_gke|counts||
|clusters_platforms_user|counts||
@@ -211,6 +213,7 @@ but commented out to help encourage others to add to it in the future. -->
|clusters_applications_runner|counts||
|clusters_applications_knative|counts||
|clusters_applications_elastic_stack|counts||
+|clusters_management_project|counts||
|in_review_folder|counts||
|grafana_integrated_projects|counts||
|groups|counts||
@@ -382,11 +385,14 @@ but commented out to help encourage others to add to it in the future. -->
|clusters_applications_helm|usage_activity_by_stage|configure|
|clusters_applications_ingress|usage_activity_by_stage|configure|
|clusters_applications_knative|usage_activity_by_stage|configure|
+|clusters_management_project|usage_activity_by_stage|configure|
|clusters_disabled|usage_activity_by_stage|configure|
|clusters_enabled|usage_activity_by_stage|configure|
|clusters_platforms_gke|usage_activity_by_stage|configure|
|clusters_platforms_eks|usage_activity_by_stage|configure|
|clusters_platforms_user|usage_activity_by_stage|configure|
+|instance_clusters_disabled|usage_activity_by_stage|configure|
+|instance_clusters_enabled|usage_activity_by_stage|configure|
|group_clusters_disabled|usage_activity_by_stage|configure|
|group_clusters_enabled|usage_activity_by_stage|configure|
|project_clusters_disabled|usage_activity_by_stage|configure|
diff --git a/doc/user/application_security/offline_deployments/index.md b/doc/user/application_security/offline_deployments/index.md
index db309357530..5a5f149a3bf 100644
--- a/doc/user/application_security/offline_deployments/index.md
+++ b/doc/user/application_security/offline_deployments/index.md
@@ -78,3 +78,4 @@ above. You can find more information at each of the pages below:
- [Container scanning offline directions](../container_scanning/index.md#running-container-scanning-in-an-offline-environment)
- [SAST offline directions](../sast/index.md#gitlab-sast-in-an-offline-environment)
- [DAST offline directions](../dast/index.md#running-dast-in-an-offline-environment)
+- [License Compliance offline directions](../../compliance/license_compliance/index.md#running-license-compliance-in-an-offline-environment)
diff --git a/doc/user/application_security/vulnerabilities/img/standalone_vulnerability_page_v12_10.png b/doc/user/application_security/vulnerabilities/img/standalone_vulnerability_page_v12_10.png
new file mode 100644
index 00000000000..0fdb8d1e201
--- /dev/null
+++ b/doc/user/application_security/vulnerabilities/img/standalone_vulnerability_page_v12_10.png
Binary files differ
diff --git a/doc/user/application_security/vulnerabilities/index.md b/doc/user/application_security/vulnerabilities/index.md
new file mode 100644
index 00000000000..5cb4f16e0d8
--- /dev/null
+++ b/doc/user/application_security/vulnerabilities/index.md
@@ -0,0 +1,69 @@
+---
+type: reference, howto
+---
+
+# Standalone Vulnerability pages
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/13561) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
+
+CAUTION: **Warning:**
+This feature is currently [Alpha](https://about.gitlab.com/handbook/product/#alpha-beta-ga).
+You can begin using it, but it may receive important changes in the future.
+
+Each security vulnerability in the [Vulnerability List](../dependency_list/index.md) has its own standalone
+page.
+
+![Standalone vulnerability page](img/standalone_vulnerability_page_v12_10.png)
+
+On the standalone vulnerability page, you can interact with the vulnerability in
+several different ways:
+
+- [Change the Vulnerability Status](#changing-vulnerability-status) - You can change the
+ status of a vulnerability to **Detected**, **Confirmed**, **Dismissed**, or **Resolved**.
+- [Create issue](#creating-an-issue-for-a-vulnerability) - Create a new issue with the
+ title and description prepopulated with information from the vulnerability report.
+ By default, such issues are [confidential](../../project/issues/confidential_issues.md).
+- [Solution](#automatic-remediation-solutions-for-vulnerabilities) - For some vulnerabilities,
+ a solution is provided for how to fix the vulnerability.
+
+## Changing vulnerability status
+
+You can switch the status of a vulnerability using the **Status** dropdown to one of
+the following values:
+
+| State | Description |
+|-----------|-------------------------------------------------------------------|
+| Detected | The default state for a newly discovered vulnerability |
+| Confirmed | A user has seen this vulnerability and confirmed it to be real |
+| Dismissed | A user has seen this vulnerability and dismissed it |
+| Resolved | The vulnerability has been fixed and is no longer in the codebase |
+
+## Creating an issue for a vulnerability
+
+You can create an issue for a vulnerability by selecting the **Create issue** button.
+
+This creates a [confidential issue](../../project/issues/confidential_issues.md) in the
+project the vulnerability came from, and prepopulates it with useful information from
+the vulnerability report. After the issue is created, GitLab redirects you to the
+issue page so you can edit, assign, or comment on the issue.
+
+## Automatic remediation solutions for vulnerabilities
+
+You can fix some vulnerabilities by applying the solution that GitLab automatically
+generates for you. GitLab supports the following scanners:
+
+- [Dependency Scanning](../dependency_scanning/index.md): Automatic Patch creation
+ is only available for Node.js projects managed with `yarn`.
+- [Container Scanning](../container_scanning/index.md).
+
+### Manually applying a suggested patch
+
+To apply a patch automatically generated by GitLab to fix a vulnerability:
+
+1. Open the issue created in [Create issue](#creating-an-issue-for-a-vulnerability).
+1. In the **Issue description**, scroll to **Solution** and download the linked patch file.
+1. Ensure your local project has the same commit checked out that was used to generate the patch.
+1. Run `git apply remediation.patch` to apply the patch.
+1. Verify and commit the changes to your branch.
+
+![Apply patch for dependency scanning](../img/vulnerability_solution.png)
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index 485e9d8213d..9fcc9acf5ea 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -198,6 +198,22 @@ you can use the `MAVEN_CLI_OPTS` environment variable.
Read more on [how to use private Maven repos](../../application_security/index.md#using-private-maven-repos).
+You can also use `MAVEN_CLI_OPTS` to connect to a trusted Maven repository that uses a self-signed
+or internally trusted certificate. For example:
+
+```yaml
+include:
+ - template: License-Scanning.gitlab-ci.yml
+
+license_scanning:
+ variables:
+ MAVEN_CLI_OPTS: -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true -Dmaven.wagon.http.ssl.insecure=true
+```
+
+Alternatively, you can use a Java key store to verify the TLS connection. For instructions on how to
+generate a key store file, see the
+[Maven Guide to Remote repository access through authenticated HTTPS](http://maven.apache.org/guides/mini/guide-repository-ssl.html).
+
### Selecting the version of Python
> - [Introduced](https://gitlab.com/gitlab-org/security-products/license-management/-/merge_requests/36) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
@@ -305,6 +321,9 @@ process:
1. Ensure the package registry is reachable from within the GitLab environment and that the package
manager is configured to use your preferred package registry.
+Additional [configuration](#using-private-maven-repos) may be needed for connecting to private Maven
+repositories.
+
## Project policies for License Compliance
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/5940) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.4.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 76a33559666..fabe6fd40c9 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -57,7 +57,7 @@ The following table depicts the various user permission levels in a project.
| View Dependency list **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View License list **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View licenses in Dependency list **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
-| View [Design Management](project/issues/design_management.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View [Design Management](project/issues/design_management.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ |
| View project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Pull project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control-core) | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -84,15 +84,15 @@ The following table depicts the various user permission levels in a project.
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
| View project statistics | | ✓ | ✓ | ✓ | ✓ |
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
-| Create/edit/delete [Releases](project/releases/index.md)| | | ✓ | ✓ | ✓ |
+| Create new merge request | | ✓ | ✓ | ✓ | ✓ |
| Pull from [Conan repository](packages/conan_repository/index.md), [Maven repository](packages/maven_repository/index.md), or [NPM registry](packages/npm_registry/index.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
| Publish to [Conan repository](packages/conan_repository/index.md), [Maven repository](packages/maven_repository/index.md), or [NPM registry](packages/npm_registry/index.md) **(PREMIUM)** | | | ✓ | ✓ | ✓ |
-| Upload [Design Management](project/issues/design_management.md) files | | | ✓ | ✓ | ✓ |
+| Upload [Design Management](project/issues/design_management.md) files | | | ✓ | ✓ | ✓ |
+| Create/edit/delete [Releases](project/releases/index.md)| | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
-| Create new merge request | | ✓ | ✓ | ✓ | ✓ |
| Assign merge requests | | | ✓ | ✓ | ✓ |
| Label merge requests | | | ✓ | ✓ | ✓ |
| Lock merge request threads | | | ✓ | ✓ | ✓ |
@@ -107,8 +107,12 @@ The following table depicts the various user permission levels in a project.
| Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create/edit/delete project milestones | | | ✓ | ✓ | ✓ |
| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
-| View vulnerabilities in Dependency list **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
-| Create issue from vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| View vulnerability findings in Dependency list **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| Create issue from vulnerability finding **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| Dismiss vulnerability finding **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| View vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| Create vulnerability from vulnerability finding **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| Resolve vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Dismiss vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Apply code change suggestions | | | ✓ | ✓ | ✓ |
| Create and edit wiki pages | | | ✓ | ✓ | ✓ |
@@ -217,21 +221,21 @@ group.
| View group epic **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create/edit group epic **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
+| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| Create project in group | | | ✓ (3) | ✓ (3) | ✓ (3) |
| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
-| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| Enable/disable a dependency proxy **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| View/manage group-level Kubernetes cluster | | | | ✓ | ✓ |
| Create subgroup | | | | ✓ (1) | ✓ |
+| Edit epic comments (posted by any user) **(ULTIMATE)** | | | | ✓ (2) | ✓ (2) |
| Edit group | | | | | ✓ |
| Manage group level CI/CD variables | | | | | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
| Delete group epic **(ULTIMATE)** | | | | | ✓ |
-| Edit epic comments (posted by any user) **(ULTIMATE)** | | | | ✓ (2) | ✓ (2) |
| View group Audit Events | | | | | ✓ |
| Disable notification emails | | | | | ✓ |
-| View/manage group-level Kubernetes cluster | | | | ✓ | ✓ |
1. Groups can be set to [allow either Owners or Owners and
Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup)
diff --git a/lib/gitlab/git_access_result/custom_action.rb b/lib/gitlab/git_access_result/custom_action.rb
index 336f3405f72..e03459ea7a1 100644
--- a/lib/gitlab/git_access_result/custom_action.rb
+++ b/lib/gitlab/git_access_result/custom_action.rb
@@ -10,7 +10,7 @@ module Gitlab
# {
# 'action' => 'geo_proxy_to_primary',
# 'data' => {
- # 'api_endpoints' => %w{geo/proxy_git_push_ssh/info_refs geo/proxy_git_push_ssh/push},
+ # 'api_endpoints' => %w{geo/proxy_git_ssh/info_refs_receive_pack geo/proxy_git_ssh/receive_pack},
# 'gl_username' => user.username,
# 'primary_repo' => geo_primary_http_url_to_repo(project_or_wiki)
# }
diff --git a/lib/gitlab/jira_import/base_importer.rb b/lib/gitlab/jira_import/base_importer.rb
index afb443020b7..5fbdbbc08c1 100644
--- a/lib/gitlab/jira_import/base_importer.rb
+++ b/lib/gitlab/jira_import/base_importer.rb
@@ -6,7 +6,7 @@ module Gitlab
attr_reader :project, :client, :formatter, :jira_project_key
def initialize(project)
- raise Projects::ImportService::Error, _('Jira import feature is disabled.') unless Feature.enabled?(:jira_issue_import, project)
+ raise Projects::ImportService::Error, _('Jira import feature is disabled.') unless project.jira_issues_import_feature_flag_enabled?
raise Projects::ImportService::Error, _('Jira integration not configured.') unless project.jira_service&.active?
@jira_project_key = project.latest_jira_import&.jira_project_key
diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
index 37688d6e0e7..9a7b4cc65f6 100644
--- a/lib/gitlab/tracking.rb
+++ b/lib/gitlab/tracking.rb
@@ -9,6 +9,13 @@ module Gitlab
module ControllerConcern
extend ActiveSupport::Concern
+ included do
+ # Tracking events from the template is not ideal and we are moving this to the client in https://gitlab.com/gitlab-org/gitlab/-/issues/213712
+ # In the meantime, using this method from the view is frowned upon and this line will likely be removed
+ # in the near future
+ helper_method :track_event
+ end
+
protected
def track_event(action = action_name, **args)
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 6c98f8f5585..f4afcd962af 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -68,9 +68,11 @@ module Gitlab
clusters_enabled: count(::Clusters::Cluster.enabled),
project_clusters_enabled: count(::Clusters::Cluster.enabled.project_type),
group_clusters_enabled: count(::Clusters::Cluster.enabled.group_type),
+ instance_clusters_enabled: count(::Clusters::Cluster.enabled.instance_type),
clusters_disabled: count(::Clusters::Cluster.disabled),
project_clusters_disabled: count(::Clusters::Cluster.disabled.project_type),
group_clusters_disabled: count(::Clusters::Cluster.disabled.group_type),
+ instance_clusters_disabled: count(::Clusters::Cluster.disabled.instance_type),
clusters_platforms_eks: count(::Clusters::Cluster.aws_installed.enabled),
clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled),
clusters_platforms_user: count(::Clusters::Cluster.user_provided.enabled),
@@ -83,6 +85,7 @@ module Gitlab
clusters_applications_knative: count(::Clusters::Applications::Knative.available),
clusters_applications_elastic_stack: count(::Clusters::Applications::ElasticStack.available),
clusters_applications_jupyter: count(::Clusters::Applications::Jupyter.available),
+ clusters_management_project: count(::Clusters::Cluster.with_management_project),
in_review_folder: count(::Environment.in_review_folder),
grafana_integrated_projects: count(GrafanaIntegration.enabled),
groups: count(Group),
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3aedf132df0..68210a23b60 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -931,6 +931,9 @@ msgstr ""
msgid "Accept terms"
msgstr ""
+msgid "Acceptable for use in this project"
+msgstr ""
+
msgid "Accepted MR"
msgstr ""
@@ -4001,6 +4004,9 @@ msgstr ""
msgid "Class"
msgstr ""
+msgid "Class:"
+msgstr ""
+
msgid "Classification Label (optional)"
msgstr ""
@@ -6645,6 +6651,11 @@ msgid_plural "Dependencies|%d vulnerabilities"
msgstr[0] ""
msgstr[1] ""
+msgid "Dependencies|%d vulnerability detected"
+msgid_plural "Dependencies|%d vulnerabilities detected"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Dependencies|%{remainingLicensesCount} more"
msgstr ""
@@ -6684,6 +6695,9 @@ msgstr ""
msgid "Dependencies|The %{codeStartTag}dependency_scanning%{codeEndTag} job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again."
msgstr ""
+msgid "Dependencies|Toggle vulnerability list"
+msgstr ""
+
msgid "Dependencies|Unsupported file(s) detected"
msgstr ""
@@ -8999,6 +9013,9 @@ msgstr ""
msgid "File upload error."
msgstr ""
+msgid "File:"
+msgstr ""
+
msgid "Files"
msgstr ""
@@ -12000,6 +12017,15 @@ msgstr ""
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
+msgid "LicenseManagement|Allowed"
+msgstr ""
+
+msgid "LicenseManagement|Denied"
+msgstr ""
+
+msgid "LicenseManagement|Uncategorized"
+msgstr ""
+
msgid "Licensed Features"
msgstr ""
@@ -12173,6 +12199,9 @@ msgstr ""
msgid "Localization"
msgstr ""
+msgid "Location"
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -12752,6 +12781,9 @@ msgstr ""
msgid "Method"
msgstr ""
+msgid "Method:"
+msgstr ""
+
msgid "Metric was successfully added."
msgstr ""
@@ -13556,6 +13588,9 @@ msgstr ""
msgid "No pods available"
msgstr ""
+msgid "No policy matches this license"
+msgstr ""
+
msgid "No preview for this file type"
msgstr ""
@@ -14086,6 +14121,9 @@ msgstr ""
msgid "Other visibility settings have been disabled by the administrator."
msgstr ""
+msgid "Out-of-compliance with this project's policies and should be removed"
+msgstr ""
+
msgid "Outbound requests"
msgstr ""
@@ -17850,9 +17888,6 @@ msgstr ""
msgid "SecurityConfiguration|Status"
msgstr ""
-msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
-msgstr ""
-
msgid "SecurityDashboard|%{firstProject} and %{secondProject}"
msgstr ""
@@ -17913,6 +17948,9 @@ msgstr ""
msgid "SecurityDashboard|The security dashboard displays the latest security findings for projects you wish to monitor. Select \"Edit dashboard\" to add and remove projects."
msgstr ""
+msgid "SecurityDashboard|The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
+msgstr ""
+
msgid "SecurityDashboard|Unable to add %{invalidProjects}"
msgstr ""
@@ -18324,6 +18362,12 @@ msgstr ""
msgid "Set up CI/CD"
msgstr ""
+msgid "Set up Jira Integration"
+msgstr ""
+
+msgid "Set up Jira Integration illustration"
+msgstr ""
+
msgid "Set up a %{type} Runner automatically"
msgstr ""
@@ -23583,6 +23627,9 @@ msgstr ""
msgid "You will be removed from existing projects/groups"
msgstr ""
+msgid "You will first need to set up Jira Integration to use this feature."
+msgstr ""
+
msgid "You will lose all changes you've made to this file. This action cannot be undone."
msgstr ""
diff --git a/package.json b/package.json
index 82a757e3a54..a0abec5643f 100644
--- a/package.json
+++ b/package.json
@@ -39,8 +39,8 @@
"@babel/plugin-syntax-import-meta": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@gitlab/at.js": "1.5.5",
- "@gitlab/svgs": "1.117.0",
- "@gitlab/ui": "11.1.0",
+ "@gitlab/svgs": "1.119.0",
+ "@gitlab/ui": "11.2.1",
"@gitlab/visual-review-tools": "1.5.1",
"@sentry/browser": "^5.10.2",
"@sourcegraph/code-host-integration": "0.0.34",
diff --git a/spec/controllers/groups/shared_projects_controller_spec.rb b/spec/controllers/groups/shared_projects_controller_spec.rb
index a4b2efa7c43..a31b5682ae0 100644
--- a/spec/controllers/groups/shared_projects_controller_spec.rb
+++ b/spec/controllers/groups/shared_projects_controller_spec.rb
@@ -13,7 +13,7 @@ describe Groups::SharedProjectsController do
Projects::GroupLinks::CreateService.new(
project,
user,
- link_group_access: ProjectGroupLink::DEVELOPER
+ link_group_access: Gitlab::Access::DEVELOPER
).execute(group)
end
diff --git a/spec/factories/group_group_links.rb b/spec/factories/group_group_links.rb
index 0711a15b8dd..6f98886faff 100644
--- a/spec/factories/group_group_links.rb
+++ b/spec/factories/group_group_links.rb
@@ -4,6 +4,12 @@ FactoryBot.define do
factory :group_group_link do
shared_group { create(:group) }
shared_with_group { create(:group) }
- group_access { GroupMember::DEVELOPER }
+ group_access { Gitlab::Access::DEVELOPER }
+
+ trait(:guest) { group_access { Gitlab::Access::GUEST } }
+ trait(:reporter) { group_access { Gitlab::Access::REPORTER } }
+ trait(:developer) { group_access { Gitlab::Access::DEVELOPER } }
+ trait(:owner) { group_access { Gitlab::Access::OWNER } }
+ trait(:maintainer) { group_access { Gitlab::Access::MAINTAINER } }
end
end
diff --git a/spec/factories/project_group_links.rb b/spec/factories/project_group_links.rb
index 3c8c7a34680..b9119a5788b 100644
--- a/spec/factories/project_group_links.rb
+++ b/spec/factories/project_group_links.rb
@@ -5,5 +5,10 @@ FactoryBot.define do
project
group
expires_at { nil }
+
+ trait(:guest) { group_access { Gitlab::Access::GUEST } }
+ trait(:reporter) { group_access { Gitlab::Access::REPORTER } }
+ trait(:developer) { group_access { Gitlab::Access::DEVELOPER } }
+ trait(:maintainer) { group_access { Gitlab::Access::MAINTAINER } }
end
end
diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb
index da69a0fb844..b633038b83b 100644
--- a/spec/factories/usage_data.rb
+++ b/spec/factories/usage_data.rb
@@ -39,12 +39,14 @@ FactoryBot.define do
gcp_cluster = create(:cluster_provider_gcp, :created).cluster
create(:cluster_provider_aws, :created)
create(:cluster_platform_kubernetes)
+ create(:cluster, :management_project, management_project: projects[0])
create(:cluster, :group)
+ create(:cluster, :instance, :production_environment)
# Disabled clusters
create(:cluster, :disabled)
create(:cluster, :group, :disabled)
- create(:cluster, :group, :disabled)
+ create(:cluster, :instance, :disabled)
# Applications
create(:clusters_applications_helm, :installed, cluster: gcp_cluster)
diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb
index 8d3564ca3c0..a08772c6e7e 100644
--- a/spec/finders/group_descendants_finder_spec.rb
+++ b/spec/finders/group_descendants_finder_spec.rb
@@ -123,7 +123,7 @@ describe GroupDescendantsFinder do
project = create(:project, namespace: group)
other_project = create(:project)
other_project.project_group_links.create(group: group,
- group_access: ProjectGroupLink::MAINTAINER)
+ group_access: Gitlab::Access::MAINTAINER)
expect(finder.execute).to contain_exactly(project)
end
diff --git a/spec/frontend/jira_import/components/jira_import_app_spec.js b/spec/frontend/jira_import/components/jira_import_app_spec.js
new file mode 100644
index 00000000000..fb3ffe1ede3
--- /dev/null
+++ b/spec/frontend/jira_import/components/jira_import_app_spec.js
@@ -0,0 +1,38 @@
+import { shallowMount } from '@vue/test-utils';
+import JiraImportApp from '~/jira_import/components/jira_import_app.vue';
+import JiraImportSetup from '~/jira_import/components/jira_import_setup.vue';
+
+describe('JiraImportApp', () => {
+ let wrapper;
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('set up Jira integration page', () => {
+ beforeEach(() => {
+ wrapper = shallowMount(JiraImportApp, {
+ propsData: {
+ isJiraConfigured: true,
+ projectPath: 'gitlab-org/gitlab-test',
+ setupIllustration: 'illustration.svg',
+ },
+ });
+ });
+
+ it('is shown when Jira integration is not configured', () => {
+ wrapper.setProps({
+ isJiraConfigured: false,
+ });
+
+ return wrapper.vm.$nextTick(() => {
+ expect(wrapper.find(JiraImportSetup).exists()).toBe(true);
+ });
+ });
+
+ it('is not shown when Jira integration is configured', () => {
+ expect(wrapper.find(JiraImportSetup).exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/jira_import/components/jira_import_setup_spec.js b/spec/frontend/jira_import/components/jira_import_setup_spec.js
new file mode 100644
index 00000000000..27366bd7e8a
--- /dev/null
+++ b/spec/frontend/jira_import/components/jira_import_setup_spec.js
@@ -0,0 +1,28 @@
+import { shallowMount } from '@vue/test-utils';
+import JiraImportSetup from '~/jira_import/components/jira_import_setup.vue';
+
+describe('JiraImportSetup', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(JiraImportSetup, {
+ propsData: {
+ illustration: 'illustration.svg',
+ },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('displays a message to the user', () => {
+ const message = 'You will first need to set up Jira Integration to use this feature.';
+ expect(wrapper.find('p').text()).toBe(message);
+ });
+
+ it('contains button to set up Jira integration', () => {
+ expect(wrapper.find('a').text()).toBe('Set up Jira Integration');
+ });
+});
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 12199bc6d5a..9457e2cd549 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -58,13 +58,14 @@ describe Gitlab::UsageData, :aggregate_failures do
expect(count_data[:issues_with_embedded_grafana_charts_approx]).to eq(2)
expect(count_data[:incident_issues]).to eq(4)
- expect(count_data[:clusters_enabled]).to eq(4)
- expect(count_data[:project_clusters_enabled]).to eq(3)
+ expect(count_data[:clusters_enabled]).to eq(6)
+ expect(count_data[:project_clusters_enabled]).to eq(4)
expect(count_data[:group_clusters_enabled]).to eq(1)
+ expect(count_data[:instance_clusters_enabled]).to eq(1)
expect(count_data[:clusters_disabled]).to eq(3)
expect(count_data[:project_clusters_disabled]).to eq(1)
- expect(count_data[:group_clusters_disabled]).to eq(2)
- expect(count_data[:group_clusters_enabled]).to eq(1)
+ expect(count_data[:group_clusters_disabled]).to eq(1)
+ expect(count_data[:instance_clusters_disabled]).to eq(1)
expect(count_data[:clusters_platforms_eks]).to eq(1)
expect(count_data[:clusters_platforms_gke]).to eq(1)
expect(count_data[:clusters_platforms_user]).to eq(1)
@@ -78,6 +79,7 @@ describe Gitlab::UsageData, :aggregate_failures do
expect(count_data[:clusters_applications_elastic_stack]).to eq(1)
expect(count_data[:grafana_integrated_projects]).to eq(2)
expect(count_data[:clusters_applications_jupyter]).to eq(1)
+ expect(count_data[:clusters_management_project]).to eq(1)
end
it 'works when queries time out' do
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 8685838fdde..29c75186110 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -156,6 +156,22 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
+ describe '.with_management_project' do
+ subject { described_class.with_management_project }
+
+ context 'cluster has a management project' do
+ let!(:cluster) { create(:cluster, :management_project) }
+
+ it { is_expected.to include(cluster) }
+ end
+
+ context 'cluster does not have a management project' do
+ let!(:cluster) { create(:cluster) }
+
+ it { is_expected.not_to include(cluster) }
+ end
+ end
+
describe '.for_project_namespace' do
subject { described_class.for_project_namespace(namespace_id) }
diff --git a/spec/models/group_group_link_spec.rb b/spec/models/group_group_link_spec.rb
index a877cc803dd..1fbd399e82b 100644
--- a/spec/models/group_group_link_spec.rb
+++ b/spec/models/group_group_link_spec.rb
@@ -15,6 +15,22 @@ describe GroupGroupLink do
it { is_expected.to belong_to(:shared_with_group) }
end
+ describe 'scopes' do
+ describe '.non_guests' do
+ let!(:group_group_link_reporter) { create :group_group_link, :reporter }
+ let!(:group_group_link_maintainer) { create :group_group_link, :maintainer }
+ let!(:group_group_link_owner) { create :group_group_link, :owner }
+ let!(:group_group_link_guest) { create :group_group_link, :guest }
+
+ it 'returns all records which are greater than Guests access' do
+ expect(described_class.non_guests).to match_array([
+ group_group_link_reporter, group_group_link,
+ group_group_link_maintainer, group_group_link_owner
+ ])
+ end
+ end
+ end
+
describe 'validation' do
it { is_expected.to validate_presence_of(:shared_group) }
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index 63ce08c4d30..9c51180b55b 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -32,6 +32,23 @@ describe ProjectGroupLink do
end
end
+ describe 'scopes' do
+ describe '.non_guests' do
+ let!(:project_group_link_reporter) { create :project_group_link, :reporter }
+ let!(:project_group_link_maintainer) { create :project_group_link, :maintainer }
+ let!(:project_group_link_developer) { create :project_group_link }
+ let!(:project_group_link_guest) { create :project_group_link, :guest }
+
+ it 'returns all records which are greater than Guests access' do
+ expect(described_class.non_guests).to match_array([
+ project_group_link_reporter,
+ project_group_link_developer,
+ project_group_link_maintainer
+ ])
+ end
+ end
+ end
+
describe "destroying a record", :delete do
it "refreshes group users' authorized projects" do
project = create(:project, :private)
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 0629d51154b..dc75fdab639 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -582,7 +582,7 @@ describe API::Internal::Base do
{
'action' => 'geo_proxy_to_primary',
'data' => {
- 'api_endpoints' => %w{geo/proxy_git_push_ssh/info_refs geo/proxy_git_push_ssh/push},
+ 'api_endpoints' => %w{geo/proxy_git_ssh/info_refs_receive_pack geo/proxy_git_ssh/receive_pack},
'gl_username' => 'testuser',
'primary_repo' => 'http://localhost:3000/testuser/repo.git'
}
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 4d63cf12575..eff252ddda0 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -43,9 +43,11 @@ module UsageDataHelpers
clusters_enabled
project_clusters_enabled
group_clusters_enabled
+ instance_clusters_enabled
clusters_disabled
project_clusters_disabled
group_clusters_disabled
+ instance_clusters_disabled
clusters_platforms_eks
clusters_platforms_gke
clusters_platforms_user
@@ -58,6 +60,7 @@ module UsageDataHelpers
clusters_applications_knative
clusters_applications_elastic_stack
clusters_applications_jupyter
+ clusters_management_project
in_review_folder
grafana_integrated_projects
groups
diff --git a/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb b/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb
index 62b1b5791dc..8372ee9ac4a 100644
--- a/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb
@@ -52,6 +52,7 @@ RSpec.shared_examples 'a blob replicator' do
replicator.calculate_checksum!
expect(model_record.reload.verification_checksum).not_to be_nil
+ expect(model_record.reload.verified_at).not_to be_nil
end
it 'saves the error message and increments retry counter' do
diff --git a/yarn.lock b/yarn.lock
index acd0bcfd7e3..42a5c0d29c3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -781,15 +781,15 @@
eslint-plugin-vue "^6.2.1"
vue-eslint-parser "^7.0.0"
-"@gitlab/svgs@1.117.0":
- version "1.117.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.117.0.tgz#05239ddcf529c62ca29e1ec1a25a7e24efb98207"
- integrity sha512-dGy/VWuRAFCTZX3Yqu1+RnAHTSUWafteIk/RMfUCN9B/EMbYzjhYsNy0NLVoZ23Rj/KGv1bUGHvyQCoPP6VzpA==
-
-"@gitlab/ui@11.1.0":
- version "11.1.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-11.1.0.tgz#72dd6d4309909fd1874f8abc42be8365e875822e"
- integrity sha512-lmHTIUIYuUSGiisvnndhpUASvXZokP8/1LBfws9qkbEFPnMIvJAxGRHo3F7dLjcDBCvy4xepgDFFjI6HNFhLWA==
+"@gitlab/svgs@1.119.0":
+ version "1.119.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.119.0.tgz#ff35c160b1e726f4f1702a4a152712fb669cde4e"
+ integrity sha512-bI+kewDLJy1N0//BpUPMx3h5AqG6lfIiV53lxQP9ttn8j/jhyxigZXq1wZ901PI4/ALv9IY1DOSSuLouK4sJsQ==
+
+"@gitlab/ui@11.2.1":
+ version "11.2.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-11.2.1.tgz#c031c92204eb2a6036ae3c7482387f66c75524fd"
+ integrity sha512-AhDPYmrBtaW6Qu+inSjoMCWx+Ou3CVIhhGAEjyVsAG7rSOilevVMZui2IlSb6fPtLcXS8F78DTWjp3R1YgxxbQ==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"