From 75b31931661d403b086a93ea906cb0c751f358de Mon Sep 17 00:00:00 2001 From: James Johnson Date: Mon, 7 Dec 2020 17:42:48 -0800 Subject: [PATCH 1/2] Rebase to master before addressing feedback --- .../project_feature_labeled_toggle.vue | 72 + .../permissions/components/settings_panel.vue | 22 + app/helpers/issuables_helper.rb | 1 + .../issuable_sidebar_basic_entity.rb | 1 + app/views/shared/issuable/_sidebar.html.haml | 2 + ...3024_add_cve_id_request_project_setting.rb | 13 + db/schema_migrations/20200816133024 | 1 + db/structure.sql | 3 +- .../cve_id_request/cve_id_request_sidebar.vue | 202 +++ .../javascripts/sidebar/mount_sidebar.js | 39 +- ee/app/assets/stylesheets/pages/issuable.scss | 39 + ee/app/controllers/ee/projects_controller.rb | 28 +- ee/app/helpers/ee/cve_request_helper.rb | 20 + ee/app/helpers/ee/projects_helper.rb | 13 +- ee/app/models/ee/project.rb | 1 + .../ee/issue_sidebar_basic_entity.rb | 4 + .../issuable/_sidebar_cve_id_request.haml | 4 + .../unreleased/add_request_cve_issue.yml | 5 + .../factories/project_security_settings.rb | 1 + .../projects/settings/issues_settings_spec.rb | 52 + .../sidebar/components/cve_id_request_spec.js | 97 ++ ee/spec/helpers/ee/cve_request_helper_spec.rb | 64 + ee/spec/helpers/projects_helper_spec.rb | 39 + .../ee/issue_sidebar_basic_entity_spec.rb | 23 + locale/gitlab.pot | 18 + .../project_feature_labeled_toggle_spec.js | 73 + .../components/settings_panel_spec.js | 3 +- .../import_export/safe_model_attributes.yml | 1491 +++++++++-------- 28 files changed, 1565 insertions(+), 766 deletions(-) create mode 100644 app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_labeled_toggle.vue create mode 100644 db/migrate/20200816133024_add_cve_id_request_project_setting.rb create mode 100644 db/schema_migrations/20200816133024 create mode 100644 ee/app/assets/javascripts/sidebar/components/cve_id_request/cve_id_request_sidebar.vue create mode 100644 ee/app/helpers/ee/cve_request_helper.rb create mode 100644 ee/app/views/shared/issuable/_sidebar_cve_id_request.haml create mode 100644 ee/changelogs/unreleased/add_request_cve_issue.yml create mode 100644 ee/spec/frontend/sidebar/components/cve_id_request_spec.js create mode 100644 ee/spec/helpers/ee/cve_request_helper_spec.rb create mode 100644 ee/spec/serializers/ee/issue_sidebar_basic_entity_spec.rb create mode 100644 spec/frontend/pages/projects/shared/permissions/components/project_feature_labeled_toggle_spec.js diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_labeled_toggle.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_labeled_toggle.vue new file mode 100644 index 000000000000..9ffaa3ed7654 --- /dev/null +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_labeled_toggle.vue @@ -0,0 +1,72 @@ + + + diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index acbe94ad1439..e362ed0325b8 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -4,6 +4,7 @@ import { GlIcon, GlSprintf, GlLink, GlFormCheckbox } from '@gitlab/ui'; import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/settings_pannel_mixin'; import { s__ } from '~/locale'; import projectFeatureSetting from './project_feature_setting.vue'; +import projectFeatureLabeledToggle from './project_feature_labeled_toggle.vue'; import projectFeatureToggle from '~/vue_shared/components/toggle_button.vue'; import projectSettingRow from './project_setting_row.vue'; import { @@ -21,6 +22,7 @@ export default { components: { projectFeatureSetting, projectFeatureToggle, + projectFeatureLabeledToggle, projectSettingRow, GlIcon, GlSprintf, @@ -30,6 +32,11 @@ export default { mixins: [settingsMixin], props: { + requestCveAvailable: { + type: Boolean, + required: false, + default: false, + }, currentSettings: { type: Object, required: true, @@ -93,6 +100,11 @@ export default { required: false, default: '', }, + cveIdRequestHelpPath: { + type: String, + required: false, + default: 'https://about.gitlab.com/security/cve', + }, registryHelpPath: { type: String, required: false, @@ -143,6 +155,7 @@ export default { requestAccessEnabled: true, highlightChangesClass: false, emailsDisabled: false, + cveIdRequestEnabled: true, featureAccessLevelEveryone, featureAccessLevelMembers, }; @@ -372,6 +385,15 @@ export default { :options="featureAccessLevelOptions" name="project[project_feature_attributes][issues_access_level]" /> + +import { GlIcon } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; // eslint-disable-line no-unused-vars +import { joinPaths } from '~/lib/utils/url_utility'; +import tooltip from '~/vue_shared/directives/tooltip'; +import eventHub from '~/sidebar/event_hub'; + +export default { + components: { + GlIcon, + }, + + directives: { + tooltip, + }, + + inject: { + initialConfidential: { + required: true, + type: Boolean, + }, + iid: { + required: true, + type: String, + }, + fullPath: { + required: true, + type: String, + }, + issueTitle: { + required: true, + type: String, + }, + }, + + data() { + return { + showHelp: false, + confidential: this.initialConfidential, + }; + }, + + computed: { + helpHref() { + return joinPaths( + gon.relative_url_root || '', + '/help/user/application_security/cve_id_request.md', + ); + }, + showHelpState() { + return Boolean(this.showHelp); + }, + }, + + mounted() { + eventHub.$on('updateIssuableConfidentiality', this.changeIssueConfidentiality); + }, + + beforeDestroy() { + eventHub.$off('updateIssuableConfidentiality', this.changeIssueConfidentiality); + }, + + methods: { + toggleHelpState(show) { + this.showHelp = show; + }, + + changeIssueConfidentiality(newVal) { + this.confidential = newVal; + }, + + createCveIdRequestUrl() { + const currUrl = new URL(window.location.href); + const newUrl = new URL(currUrl.origin); + newUrl.pathname = '/gitlab-org/cves/-/issues/new'; + + const params = { + 'issue[confidential]': 'true', + // eslint-disable-next-line @gitlab/require-i18n-strings + 'issue[title]': `CVE ID Request - ${this.fullPath}`, + 'issue[description]': ` +**NOTE:** Only maintainers of GitLab-hosted projects may request a CVE for +a vulnerability within their project. + +Project issue: ${this.fullPath}#${this.iid} + +After a CVE request is validated, a CVE identifier will be assigned. On what +schedule should the details of the CVE be published? + +* [ ] Publish immediately +* [ ] Wait to publish + + + +\`\`\`yaml +vulnerability: + description: "TODO" # "[VULNTYPE] in [COMPONENT] in [VENDOR][PRODUCT] [VERSION] allows [ATTACKER] to [IMPACT] via [VECTOR]" + cwe: "TODO" # "CWE-22" # Path Traversal + product: + gitlab_path: "${this.fullPath}" + vendor: "TODO" # "Deluxe Sandwich Maker Company" + name: "TODO" # "Deluxe Sandwich Maker 2" + affected_versions: + - "TODO" # "1.2.3" + - "TODO" # ">1.3.0, <=1.3.9" + fixed_versions: + - "TODO" # "1.2.4" + - "TODO" # "1.3.10" + impact: "TODO" # "CVSS v3 string" # https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator + solution: "TODO" # "Upgrade to version 1.2.4 or 1.3.10" + credit: "TODO" + references: + - "TODO" # "https://some.domain.tld/a/reference" +\`\`\` + +CVSS scores can be computed by means of the [NVD CVSS Calculator](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). + +/relate ${this.fullPath}#${this.iid} +/label ~"devops::secure" ~"group::vulnerability research" ~"vulnerability research::cve" ~"advisory::queued" + `, + }; + Object.keys(params).forEach((k) => newUrl.searchParams.append(k, params[k])); + + return newUrl.toString(); + }, + }, +}; + + + diff --git a/ee/app/assets/javascripts/sidebar/mount_sidebar.js b/ee/app/assets/javascripts/sidebar/mount_sidebar.js index 3dc692d3d010..4910b72880d5 100644 --- a/ee/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/ee/app/assets/javascripts/sidebar/mount_sidebar.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { parseBoolean } from '~/lib/utils/common_utils'; +import CveIdRequest from './components/cve_id_request/cve_id_request_sidebar.vue'; import * as CEMountSidebar from '~/sidebar/mount_sidebar'; import SidebarItemEpicsSelect from './components/sidebar_item_epics_select.vue'; import SidebarStatus from './components/status/sidebar_status.vue'; @@ -12,7 +13,7 @@ import { store } from '~/notes/stores'; Vue.use(VueApollo); -const mountWeightComponent = mediator => { +const mountWeightComponent = (mediator) => { const el = document.querySelector('.js-sidebar-weight-entry-point'); if (!el) return false; @@ -22,7 +23,7 @@ const mountWeightComponent = mediator => { components: { SidebarWeight, }, - render: createElement => + render: (createElement) => createElement('sidebar-weight', { props: { mediator, @@ -31,7 +32,7 @@ const mountWeightComponent = mediator => { }); }; -const mountStatusComponent = mediator => { +const mountStatusComponent = (mediator) => { const el = document.querySelector('.js-sidebar-status-entry-point'); if (!el) { @@ -44,7 +45,7 @@ const mountStatusComponent = mediator => { components: { SidebarStatus, }, - render: createElement => + render: (createElement) => createElement('sidebar-status', { props: { mediator, @@ -53,6 +54,31 @@ const mountStatusComponent = mediator => { }); }; +function mountCveIdRequestComponent() { + const el = document.getElementById('js-sidebar-cve-id-request-entry-point'); + + if (!el) return; + const { iid, fullPath, title } = CEMountSidebar.getSidebarOptions(); + + const dataNode = document.getElementById('js-sidebar-cve-id-request-issue-data'); + const { is_confidential } = JSON.parse(dataNode.innerHTML); + + // eslint-disable-next-line no-new + new Vue({ + el, + components: { + CveIdRequest, + }, + provide: { + iid: String(iid), + fullPath, + issueTitle: title, + initialConfidential: is_confidential, + }, + render: (createElement) => createElement('cve-id-request'), + }); +} + const mountEpicsSelect = () => { const el = document.querySelector('#js-vue-sidebar-item-epics-select'); @@ -66,7 +92,7 @@ const mountEpicsSelect = () => { components: { SidebarItemEpicsSelect, }, - render: createElement => + render: (createElement) => createElement('sidebar-item-epics-select', { props: { sidebarStore, @@ -97,7 +123,7 @@ function mountIterationSelect() { components: { IterationSelect, }, - render: createElement => + render: (createElement) => createElement('iteration-select', { props: { groupPath, @@ -115,4 +141,5 @@ export default function mountSidebar(mediator) { mountStatusComponent(mediator); mountEpicsSelect(); mountIterationSelect(); + mountCveIdRequestComponent(); } diff --git a/ee/app/assets/stylesheets/pages/issuable.scss b/ee/app/assets/stylesheets/pages/issuable.scss index 0f3df33677a4..cde1881b4d5f 100644 --- a/ee/app/assets/stylesheets/pages/issuable.scss +++ b/ee/app/assets/stylesheets/pages/issuable.scss @@ -87,6 +87,45 @@ } } +.cve-id-request { + padding-bottom: 0; + border-bottom: 0; + + .help-button, + .close-help-button { + cursor: pointer; + } + + .help-state-toggle-enter-active { + transition: all 0.8s ease; + } + + .help-state-toggle-leave-active { + transition: all 0.5s ease; + } + + .help-state-toggle-enter, + .help-state-toggle-leave-active { + opacity: 0; + } + + .cve-id-request-content { + margin-top: 16px; + } + + .cve-id-request-help-state { + background: $white; + margin: 16px -20px -20px; + padding: 16px 20px; + border-top: 1px solid $border-gray-light; + border-bottom: 1px solid $border-gray-light; + + a:hover { + color: $btn-white-active; + } + } +} + // This override is needed because `display: flex` // on `.epic` above causes hiding logic in global // stylesheet is of lower specificity. diff --git a/ee/app/controllers/ee/projects_controller.rb b/ee/app/controllers/ee/projects_controller.rb index 7c9fddfec79c..38fea5c0ae99 100644 --- a/ee/app/controllers/ee/projects_controller.rb +++ b/ee/app/controllers/ee/projects_controller.rb @@ -74,18 +74,22 @@ def active_new_project_tab private def project_params_ee - attrs = %i[ - approvals_before_merge - approver_group_ids - approver_ids - issues_template - merge_requests_template - repository_size_limit - reset_approvals_on_push - ci_cd_only - use_custom_template - require_password_to_approve - group_with_project_templates_id + attrs = [ + :approvals_before_merge, + :approver_group_ids, + :approver_ids, + :issues_template, + :merge_requests_template, + :repository_size_limit, + :reset_approvals_on_push, + :ci_cd_only, + :use_custom_template, + :require_password_to_approve, + :group_with_project_templates_id, + + security_setting_attributes: %i[ + cve_id_request_enabled + ] ] attrs << %i[merge_pipelines_enabled] if allow_merge_pipelines_params? diff --git a/ee/app/helpers/ee/cve_request_helper.rb b/ee/app/helpers/ee/cve_request_helper.rb new file mode 100644 index 000000000000..9ba12418254d --- /dev/null +++ b/ee/app/helpers/ee/cve_request_helper.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module EE + module CveRequestHelper + def request_cve_enabled_for_user?(issue, user) + can?(user, :admin_project, issue.project) && + request_cve_enabled?(issue.project) + end + + def request_cve_enabled?(project) + project.visibility_level == ::Gitlab::VisibilityLevel::PUBLIC && + request_cve_available?(project) && + ProjectSecuritySetting.safe_find_or_create_for(project).cve_id_request_enabled == true + end + + def request_cve_available?(project) + ::Gitlab.dev_env_or_com? + end + end +end diff --git a/ee/app/helpers/ee/projects_helper.rb b/ee/app/helpers/ee/projects_helper.rb index 3af014b9ef52..c34bb80b330f 100644 --- a/ee/app/helpers/ee/projects_helper.rb +++ b/ee/app/helpers/ee/projects_helper.rb @@ -4,6 +4,16 @@ module EE module ProjectsHelper extend ::Gitlab::Utils::Override + include CveRequestHelper + + override :project_permissions_settings + def project_permissions_settings(project) + security_settings ||= ProjectSecuritySetting.safe_find_or_create_for(project) + super(project).merge({ + cveIdRequestEnabled: !!security_settings.cve_id_request_enabled + }) + end + override :sidebar_settings_paths def sidebar_settings_paths super + %w[ @@ -67,7 +77,8 @@ def project_permissions_settings(project) override :project_permissions_panel_data def project_permissions_panel_data(project) super.merge( - requirementsAvailable: project.feature_available?(:requirements) + requirementsAvailable: project.feature_available?(:requirements), + requestCveAvailable: request_cve_available?(project) ) end diff --git a/ee/app/models/ee/project.rb b/ee/app/models/ee/project.rb index ae6e2650ed1a..3aed99ca4fc1 100644 --- a/ee/app/models/ee/project.rb +++ b/ee/app/models/ee/project.rb @@ -226,6 +226,7 @@ def requirements_enabled=(value) accepts_nested_attributes_for :status_page_setting, update_only: true, allow_destroy: true accepts_nested_attributes_for :compliance_framework_setting, update_only: true, allow_destroy: true + accepts_nested_attributes_for :security_setting, update_only: true alias_attribute :fallback_approvals_required, :approvals_before_merge end diff --git a/ee/app/serializers/ee/issue_sidebar_basic_entity.rb b/ee/app/serializers/ee/issue_sidebar_basic_entity.rb index adfb33b4de61..bd037ce0fc69 100644 --- a/ee/app/serializers/ee/issue_sidebar_basic_entity.rb +++ b/ee/app/serializers/ee/issue_sidebar_basic_entity.rb @@ -18,6 +18,10 @@ module IssueSidebarBasicEntity issuable.project&.group&.feature_available?(:epics) end end + + expose :request_cve_enabled_for_user do |issue| + request_cve_enabled_for_user?(issue, current_user) + end end end end diff --git a/ee/app/views/shared/issuable/_sidebar_cve_id_request.haml b/ee/app/views/shared/issuable/_sidebar_cve_id_request.haml new file mode 100644 index 000000000000..fc015bd0ef53 --- /dev/null +++ b/ee/app/views/shared/issuable/_sidebar_cve_id_request.haml @@ -0,0 +1,4 @@ +- if issuable_sidebar[:request_cve_enabled_for_user] + %script#js-sidebar-cve-id-request-issue-data{ type: "application/json" }= { is_confidential: issuable_sidebar[:confidential] }.to_json.html_safe + #js-sidebar-cve-id-request-entry-point + diff --git a/ee/changelogs/unreleased/add_request_cve_issue.yml b/ee/changelogs/unreleased/add_request_cve_issue.yml new file mode 100644 index 000000000000..45b2c1bf384b --- /dev/null +++ b/ee/changelogs/unreleased/add_request_cve_issue.yml @@ -0,0 +1,5 @@ +--- +title: Adds Request CVE ID button to issue sidebar +merge_request: 41203 +author: +type: added diff --git a/ee/spec/factories/project_security_settings.rb b/ee/spec/factories/project_security_settings.rb index 55afe901aa0c..1e56eaf8146b 100644 --- a/ee/spec/factories/project_security_settings.rb +++ b/ee/spec/factories/project_security_settings.rb @@ -7,5 +7,6 @@ auto_fix_dast { true } auto_fix_dependency_scanning { true } auto_fix_sast { true } + cve_id_request_enabled { true } end end diff --git a/ee/spec/features/projects/settings/issues_settings_spec.rb b/ee/spec/features/projects/settings/issues_settings_spec.rb index d4ddff77f325..329836107980 100644 --- a/ee/spec/features/projects/settings/issues_settings_spec.rb +++ b/ee/spec/features/projects/settings/issues_settings_spec.rb @@ -72,4 +72,56 @@ expect(page).to have_selector('#project_issues_template') end end + + context 'when viewing CVE request settings' do + using RSpec::Parameterized::TableSyntax + + where(:gitlab_com, :project_visibility, :cve_enabled, :toggle_checked, :toggle_disabled, :has_toggle) do + true | :PUBLIC | true | true | false | true + true | :INTERNAL | true | nil | true | true + true | :PRIVATE | true | nil | true | true + false | :PUBLIC | true | nil | nil | false + end + + with_them do + before do + allow(::Gitlab).to receive(:com?).and_return(gitlab_com) + + vis_val = Gitlab::VisibilityLevel.const_get(project_visibility, false) + project.visibility_level = vis_val + project.save! + + security_setting = ProjectSecuritySetting.safe_find_or_create_for(project) + security_setting.cve_id_request_enabled = cve_enabled + security_setting.save! + + visit edit_project_path(project) + end + + it "CVE ID Request toggle should be correctly visible" do + if has_toggle + expect(page).to have_selector('#cve_id_request_toggle') + else + expect(page).not_to have_selector('#cve_id_request_toggle') + next + end + + toggle_btn = find('#cve_id_request_toggle button.project-feature-toggle') + + if toggle_disabled + expect(toggle_btn).to match_css('.is-disabled', wait: 0) + else + expect(toggle_btn).not_to match_css('.is-disabled', wait: 0) + end + + next if toggle_checked.nil? + + if toggle_checked + expect(toggle_btn).to match_css('.is-checked', wait: 0) + else + expect(toggle_btn).not_to match_css('.is-checked', wait: 0) + end + end + end + end end diff --git a/ee/spec/frontend/sidebar/components/cve_id_request_spec.js b/ee/spec/frontend/sidebar/components/cve_id_request_spec.js new file mode 100644 index 000000000000..96339b238c80 --- /dev/null +++ b/ee/spec/frontend/sidebar/components/cve_id_request_spec.js @@ -0,0 +1,97 @@ +import Vue from 'vue'; +import CveIdRequest from 'ee/sidebar/components/cve_id_request/cve_id_request_sidebar.vue'; +import { shallowMount } from '@vue/test-utils'; + +describe('CveIdRequest', () => { + let wrapper; + + const initCveIdRequest = () => { + setFixtures(` +
+
+
+ `); + + const provide = { + iid: 'test', + fullPath: 'some/path', + issueTitle: 'Issue Title', + initialConfidential: true, + }; + + const CveIdRequestComponent = Vue.extend({ + ...CveIdRequest, + components: { + ...CveIdRequest.components, + transition: { + // disable animations + render(h) { + return h('div', this.$slots.default); + }, + }, + }, + }); + wrapper = shallowMount(CveIdRequestComponent, { + provide, + }); + }; + + beforeEach(() => { + initCveIdRequest(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('Renders the main "Request CVE ID" button', () => { + expect(wrapper.find('.js-cve-id-request-button').element).not.toBeNull(); + }); + + it('Renders the "help-button" by default', () => { + expect(wrapper.find('.help-button').element).not.toBeNull(); + }); + + describe('Help Pane', () => { + const helpButton = () => wrapper.find('.help-button').element; + const closeHelpButton = () => wrapper.find('.close-help-button').element; + const helpPane = () => wrapper.find('.cve-id-request-help-state').element; + + beforeEach(() => { + initCveIdRequest(); + return wrapper.vm.$nextTick(); + }); + + it('should not show the "Help" pane by default', () => { + expect(wrapper.vm.showHelpState).toBe(false); + expect(helpPane()).toBeUndefined(); + }); + + it('should show the "Help" pane when help button is clicked', () => { + helpButton().click(); + + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.vm.showHelpState).toBe(true); + + // let animations run + jest.advanceTimersByTime(500); + + expect(helpPane()).not.toBeUndefined(); + }); + }); + + it('should not show the "Help" pane when help button is clicked and then closed', (done) => { + helpButton().click(); + + Vue.nextTick() + .then(() => closeHelpButton().click()) + .then(() => Vue.nextTick()) + .then(() => { + expect(wrapper.vm.showHelpState).toBe(false); + expect(helpPane()).toBeUndefined(); + }) + .then(done) + .catch(done.fail); + }); + }); +}); diff --git a/ee/spec/helpers/ee/cve_request_helper_spec.rb b/ee/spec/helpers/ee/cve_request_helper_spec.rb new file mode 100644 index 000000000000..10e37325709c --- /dev/null +++ b/ee/spec/helpers/ee/cve_request_helper_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe EE::CveRequestHelper do + let(:project) { create(:project) } + + describe '#request_cve_enabled_for_user?' do + where( + maintainer: [true, false], + request_cve_enabled: [true, false] + ) + with_them do + let(:user) { create_user(maintainer ? :maintainer : :developer) } + let(:issue) do + create(:issue, project: project, assignees: [user]) + end + + before do + allow(helper).to receive(:can?) do |_user, perm, project| + perm == :admin_project ? maintainer : false + end + allow(helper).to receive(:request_cve_enabled?).and_return(request_cve_enabled) + end + + it "returns the correct value" do + res = helper.request_cve_enabled_for_user?(issue, user) + expected = maintainer && request_cve_enabled + expect(res).to equal(expected) + end + end + end + + describe '#request_cve_enabled?' do + where( + gitlab_com: [true, false], + setting_enabled: [true, false], + visibility: [:PUBLIC, :INTERNAL, :PRIVATE] + ) + with_them do + before do + allow(::Gitlab).to receive(:com?).and_return(gitlab_com) + vis_val = Gitlab::VisibilityLevel.const_get(visibility, false) + project.visibility_level = vis_val + project.save! + + security_setting = ProjectSecuritySetting.safe_find_or_create_for(project) + security_setting.cve_id_request_enabled = setting_enabled + security_setting.save! + end + + it "returns the correct value" do + expected = gitlab_com && setting_enabled && visibility == :PUBLIC + expect(helper.request_cve_enabled?(project)).to equal(expected) + end + end + end + + def create_user(access_level_trait) + user = create(:user) + create(:project_member, access_level_trait, user: user, project: project) + user + end +end diff --git a/ee/spec/helpers/projects_helper_spec.rb b/ee/spec/helpers/projects_helper_spec.rb index 3913cc9b8faa..976bbc1ff806 100644 --- a/ee/spec/helpers/projects_helper_spec.rb +++ b/ee/spec/helpers/projects_helper_spec.rb @@ -354,6 +354,45 @@ end end + describe '#project_permissions_settings' do + let(:perm_settings) { helper.project_permissions_settings(project) } + + context 'default settings' do + it 'cveIdRequestEnabled defaults to true' do + expect(perm_settings[:cveIdRequestEnabled]).to equal(true) + end + end + + context 'security_settings.cve_id_request_enabled' do + tests = [true, false] + tests.each do |cve_enabled| + context "is #{cve_enabled}" do + before do + security_setting = create(:project_security_setting, project: project, cve_id_request_enabled: cve_enabled) + security_setting.save! + end + + it "sets cveIdRequestEnabled to #{cve_enabled}" do + expect(perm_settings[:cveIdRequestEnabled]).to equal(cve_enabled) + end + end + end + end + end + + describe '#project_permissions_panel_data' do + let(:panel_data) { helper.project_permissions_panel_data(project) } + + before do + allow(helper).to receive(:current_user).and_return(project.owner) + allow(helper).to receive(:can?).and_return(false) + end + + it 'sets requestCveAvailable' do + expect(panel_data).to include(requestCveAvailable: helper.request_cve_available?(project)) + end + end + describe '#can_view_operations_tab?' do let_it_be(:user) { create(:user) } diff --git a/ee/spec/serializers/ee/issue_sidebar_basic_entity_spec.rb b/ee/spec/serializers/ee/issue_sidebar_basic_entity_spec.rb new file mode 100644 index 000000000000..820f9a469bbf --- /dev/null +++ b/ee/spec/serializers/ee/issue_sidebar_basic_entity_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe EE::IssueSidebarBasicEntity do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:issue) { create(:issue, project: project, assignees: [user]) } + + context "When serializing" do + let(:test_value) { 'TEST VALUE' } + + before do + allow_any_instance_of(EE::CveRequestHelper).to receive(:request_cve_enabled_for_user?).and_return(test_value) + end + + it 'uses the value from request_cve_enabled_for_user' do + serializer = IssueSerializer.new(current_user: user, project: project) + data = serializer.represent(issue, serializer: 'sidebar') + expect(data[:request_cve_enabled_for_user]).to equal(test_value) + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 53e92f806eea..dfe5fbb47043 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4912,6 +4912,24 @@ msgstr "" msgid "CPU" msgstr "" +msgid "CVE|As a maintainer, requesting a CVE for a vulnerability in your project will help your users stay secure and informed." +msgstr "" + +msgid "CVE|Common Vulnerability Enumeration (CVE) identifiers are used to track distinct vulnerabilities in specific versions of code." +msgstr "" + +msgid "CVE|Create CVE ID Request" +msgstr "" + +msgid "CVE|Enable CVE ID requests in the issue sidebar" +msgstr "" + +msgid "CVE|Request CVE ID" +msgstr "" + +msgid "CVE|Why Request a CVE ID?" +msgstr "" + msgid "Callback URL" msgstr "" diff --git a/spec/frontend/pages/projects/shared/permissions/components/project_feature_labeled_toggle_spec.js b/spec/frontend/pages/projects/shared/permissions/components/project_feature_labeled_toggle_spec.js new file mode 100644 index 000000000000..c8fba52f288d --- /dev/null +++ b/spec/frontend/pages/projects/shared/permissions/components/project_feature_labeled_toggle_spec.js @@ -0,0 +1,73 @@ +import { mount, shallowMount } from '@vue/test-utils'; + +import projectFeatureLabeledToggle from '~/pages/projects/shared/permissions/components/project_feature_labeled_toggle.vue'; +import projectFeatureToggle from '~/vue_shared/components/toggle_button.vue'; + +describe('Project Feature Labeled Toggle', () => { + const defaultProps = { + name: 'test', + label: 'TEST', + value: true, + disabledInput: false, + helpPath: 'HELP PATH', + }; + let wrapper; + + const mountComponent = (customProps) => { + const propsData = { ...defaultProps, ...customProps }; + return shallowMount(projectFeatureLabeledToggle, { propsData }); + }; + + beforeEach(() => { + wrapper = mountComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('Feature label', () => { + it('should render the correct label', () => { + expect(wrapper.find('span').element.innerHTML.trim()).toBe(defaultProps.label); + }); + }); + + describe('Feature toggle', () => { + it('should enable the feature toggle if the value is true', () => { + wrapper.setProps({ value: true }); + expect(wrapper.find(projectFeatureToggle).props().value).toBe(true); + }); + + it('should disable the feature toggle if the value is false', () => { + wrapper.setProps({ value: false }); + + return wrapper.vm.$nextTick(() => { + expect(wrapper.find(projectFeatureToggle).props().value).toBe(false); + }); + }); + + it('should disable the feature toggle if disabledInput is set', () => { + wrapper.setProps({ disabledInput: true }); + + return wrapper.vm.$nextTick(() => { + expect(wrapper.find(projectFeatureToggle).props().disabledInput).toBe(true); + }); + }); + + it('should emit a change event when the feature toggle changes', () => { + // Needs to be fully mounted to be able to trigger the click event on the internal button + wrapper = mount(projectFeatureLabeledToggle, { propsData: defaultProps }); + + expect(wrapper.emitted().change).toBeUndefined(); + wrapper + .find(projectFeatureToggle) + .find('button') + .trigger('click'); + + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.emitted().change.length).toBe(1); + expect(wrapper.emitted().change[0]).toEqual([false]); + }); + }); + }); +}); diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js index e760cead760a..5c6f5fe81f0c 100644 --- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js +++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js @@ -27,6 +27,7 @@ const defaultProps = { packagesEnabled: true, showDefaultAwardEmojis: true, }, + isGitlabCom: true, canDisableEmails: true, canChangeVisibilityLevel: true, allowedVisibilityOptions: [0, 10, 20], @@ -355,7 +356,7 @@ describe('Settings Panel', () => { const repositoryFeatureToggleButton = findRepositoryFeatureSetting().find('button'); const lfsFeatureToggleButton = findLFSFeatureToggle().find('button'); - const isToggleButtonChecked = toggleButton => toggleButton.classes('is-checked'); + const isToggleButtonChecked = (toggleButton) => toggleButton.classes('is-checked'); // assert the initial state expect(isToggleButtonChecked(lfsFeatureToggleButton)).toBe(true); diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 93f4405fc9c4..a337981fa3e8 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -1,816 +1,816 @@ --- Issue: -- id -- title -- assignee_id -- author_id -- project_id -- created_at -- updated_at -- position -- branch_name -- description -- state -- state_id -- iid -- updated_by_id -- confidential -- closed_at -- closed_by_id -- due_date -- moved_to_id -- duplicated_to_id -- promoted_to_epic_id -- lock_version -- milestone_id -- weight -- time_estimate -- relative_position -- external_author -- last_edited_at -- last_edited_by_id -- discussion_locked -- health_status -- external_key -- issue_type + - id + - title + - assignee_id + - author_id + - project_id + - created_at + - updated_at + - position + - branch_name + - description + - state + - state_id + - iid + - updated_by_id + - confidential + - closed_at + - closed_by_id + - due_date + - moved_to_id + - duplicated_to_id + - promoted_to_epic_id + - lock_version + - milestone_id + - weight + - time_estimate + - relative_position + - external_author + - last_edited_at + - last_edited_by_id + - discussion_locked + - health_status + - external_key + - issue_type Event: -- id -- target_type -- project_id -- group_id -- created_at -- updated_at -- action -- author_id -- fingerprint + - id + - target_type + - project_id + - group_id + - created_at + - updated_at + - action + - author_id + - fingerprint WikiPage::Meta: -- id -- title -- project_id + - id + - title + - project_id WikiPage::Slug: -- id -- wiki_page_meta_id -- slug + - id + - wiki_page_meta_id + - slug PushEventPayload: -- commit_count -- action -- ref_type -- commit_from -- commit_to -- ref -- commit_title -- ref_count + - commit_count + - action + - ref_type + - commit_from + - commit_to + - ref + - commit_title + - ref_count Note: -- id -- note -- noteable_type -- author_id -- created_at -- updated_at -- project_id -- attachment -- line_code -- commit_id -- system -- st_diff -- updated_by_id -- type -- position -- original_position -- change_position -- resolved_at -- resolved_by_id -- resolved_by_push -- discussion_id -- original_discussion_id -- confidential + - id + - note + - noteable_type + - author_id + - created_at + - updated_at + - project_id + - attachment + - line_code + - commit_id + - system + - st_diff + - updated_by_id + - type + - position + - original_position + - change_position + - resolved_at + - resolved_by_id + - resolved_by_push + - discussion_id + - original_discussion_id + - confidential LabelLink: -- id -- target_type -- created_at -- updated_at + - id + - target_type + - created_at + - updated_at ProjectLabel: -- id -- title -- color -- group_id -- project_id -- type -- created_at -- updated_at -- template -- description -- priority + - id + - title + - color + - group_id + - project_id + - type + - created_at + - updated_at + - template + - description + - priority Milestone: -- id -- title -- project_id -- group_id -- description -- due_date -- start_date -- created_at -- updated_at -- state -- iid + - id + - title + - project_id + - group_id + - description + - due_date + - start_date + - created_at + - updated_at + - state + - iid ProjectSnippet: -- id -- title -- description -- content -- author_id -- project_id -- created_at -- updated_at -- file_name -- type -- visibility_level + - id + - title + - description + - content + - author_id + - project_id + - created_at + - updated_at + - file_name + - type + - visibility_level Release: -- id -- name -- tag -- sha -- description -- author_id -- project_id -- created_at -- updated_at -- released_at + - id + - name + - tag + - sha + - description + - author_id + - project_id + - created_at + - updated_at + - released_at Releases::Evidence: -- id -- summary -- created_at -- updated_at + - id + - summary + - created_at + - updated_at Releases::Link: -- id -- url -- name -- filepath -- link_type -- created_at -- updated_at + - id + - url + - name + - filepath + - link_type + - created_at + - updated_at ProjectMember: -- id -- access_level -- source_type -- user_id -- notification_level -- type -- created_at -- updated_at -- created_by_id -- invite_email -- invite_token -- invite_accepted_at -- requested_at -- expires_at -- ldap -- override + - id + - access_level + - source_type + - user_id + - notification_level + - type + - created_at + - updated_at + - created_by_id + - invite_email + - invite_token + - invite_accepted_at + - requested_at + - expires_at + - ldap + - override User: -- id -- username -- email + - id + - username + - email MergeRequest: -- id -- target_branch -- source_branch -- source_project_id -- author_id -- assignee_id -- title -- created_at -- updated_at -- state -- state_id -- merge_status -- target_project_id -- iid -- description -- position -- updated_by_id -- merge_error -- merge_params -- merge_when_pipeline_succeeds -- merge_user_id -- merge_commit_sha -- squash_commit_sha -- in_progress_merge_commit_sha -- lock_version -- milestone_id -- approvals_before_merge -- rebase_commit_sha -- time_estimate -- squash -- last_edited_at -- last_edited_by_id -- head_pipeline_id -- discussion_locked -- allow_maintainer_to_push -- merge_ref_sha + - id + - target_branch + - source_branch + - source_project_id + - author_id + - assignee_id + - title + - created_at + - updated_at + - state + - state_id + - merge_status + - target_project_id + - iid + - description + - position + - updated_by_id + - merge_error + - merge_params + - merge_when_pipeline_succeeds + - merge_user_id + - merge_commit_sha + - squash_commit_sha + - in_progress_merge_commit_sha + - lock_version + - milestone_id + - approvals_before_merge + - rebase_commit_sha + - time_estimate + - squash + - last_edited_at + - last_edited_by_id + - head_pipeline_id + - discussion_locked + - allow_maintainer_to_push + - merge_ref_sha MergeRequestDiff: -- id -- state -- merge_request_id -- created_at -- updated_at -- base_commit_sha -- real_size -- head_commit_sha -- start_commit_sha -- commits_count -- files_count + - id + - state + - merge_request_id + - created_at + - updated_at + - base_commit_sha + - real_size + - head_commit_sha + - start_commit_sha + - commits_count + - files_count MergeRequestDiffCommit: -- merge_request_diff_id -- relative_order -- sha -- authored_date -- committed_date -- author_name -- author_email -- committer_name -- committer_email -- message + - merge_request_diff_id + - relative_order + - sha + - authored_date + - committed_date + - author_name + - author_email + - committer_name + - committer_email + - message MergeRequestDiffFile: -- merge_request_diff_id -- relative_order -- new_file -- renamed_file -- deleted_file -- new_path -- old_path -- a_mode -- b_mode -- too_large -- binary + - merge_request_diff_id + - relative_order + - new_file + - renamed_file + - deleted_file + - new_path + - old_path + - a_mode + - b_mode + - too_large + - binary MergeRequestContextCommit: -- id -- authored_date -- committed_date -- relative_order -- sha -- author_name -- author_email -- committer_name -- committer_email -- message -- merge_request_id + - id + - authored_date + - committed_date + - relative_order + - sha + - author_name + - author_email + - committer_name + - committer_email + - message + - merge_request_id MergeRequestContextCommitDiffFile: -- sha -- relative_order -- new_file -- renamed_file -- deleted_file -- new_path -- old_path -- a_mode -- b_mode -- too_large -- binary -- text + - sha + - relative_order + - new_file + - renamed_file + - deleted_file + - new_path + - old_path + - a_mode + - b_mode + - too_large + - binary + - text MergeRequest::Metrics: -- id -- created_at -- updated_at -- merge_request_id -- pipeline_id -- latest_closed_by_id -- latest_closed_at -- merged_by_id -- merged_at -- latest_build_started_at -- latest_build_finished_at -- first_deployed_to_production_at -- first_comment_at -- first_commit_at -- last_commit_at -- diff_size -- modified_paths_size -- commits_count -- first_approved_at -- first_reassigned_at -- added_lines -- target_project_id -- removed_lines + - id + - created_at + - updated_at + - merge_request_id + - pipeline_id + - latest_closed_by_id + - latest_closed_at + - merged_by_id + - merged_at + - latest_build_started_at + - latest_build_finished_at + - first_deployed_to_production_at + - first_comment_at + - first_commit_at + - last_commit_at + - diff_size + - modified_paths_size + - commits_count + - first_approved_at + - first_reassigned_at + - added_lines + - target_project_id + - removed_lines Ci::Pipeline: -- id -- project_id -- source -- ref -- sha -- before_sha -- source_sha -- target_sha -- push_data -- created_at -- updated_at -- tag -- yaml_errors -- committed_at -- status -- started_at -- finished_at -- duration -- user_id -- lock_version -- auto_canceled_by_id -- pipeline_schedule_id -- config_source -- failure_reason -- protected -- iid -- merge_request_id -- external_pull_request_id + - id + - project_id + - source + - ref + - sha + - before_sha + - source_sha + - target_sha + - push_data + - created_at + - updated_at + - tag + - yaml_errors + - committed_at + - status + - started_at + - finished_at + - duration + - user_id + - lock_version + - auto_canceled_by_id + - pipeline_schedule_id + - config_source + - failure_reason + - protected + - iid + - merge_request_id + - external_pull_request_id Ci::Stage: -- id -- name -- status -- position -- lock_version -- project_id -- pipeline_id -- created_at -- updated_at + - id + - name + - status + - position + - lock_version + - project_id + - pipeline_id + - created_at + - updated_at CommitStatus: -- id -- project_id -- status -- finished_at -- trace -- created_at -- updated_at -- started_at -- runner_id -- coverage -- commit_id -- commands -- job_id -- name -- deploy -- options -- allow_failure -- stage -- trigger_request_id -- stage_idx -- stage_id -- tag -- ref -- user_id -- type -- target_url -- description -- artifacts_file -- artifacts_file_store -- artifacts_metadata -- artifacts_metadata_store -- erased_by_id -- erased_at -- artifacts_expire_at -- environment -- artifacts_size -- when -- yaml_variables -- queued_at -- token -- lock_version -- coverage_regex -- auto_canceled_by_id -- retried -- protected -- failure_reason -- scheduled_at -- upstream_pipeline_id -- interruptible -- processed -- scheduling_type + - id + - project_id + - status + - finished_at + - trace + - created_at + - updated_at + - started_at + - runner_id + - coverage + - commit_id + - commands + - job_id + - name + - deploy + - options + - allow_failure + - stage + - trigger_request_id + - stage_idx + - stage_id + - tag + - ref + - user_id + - type + - target_url + - description + - artifacts_file + - artifacts_file_store + - artifacts_metadata + - artifacts_metadata_store + - erased_by_id + - erased_at + - artifacts_expire_at + - environment + - artifacts_size + - when + - yaml_variables + - queued_at + - token + - lock_version + - coverage_regex + - auto_canceled_by_id + - retried + - protected + - failure_reason + - scheduled_at + - upstream_pipeline_id + - interruptible + - processed + - scheduling_type Ci::Variable: -- id -- project_id -- key -- value -- encrypted_value -- encrypted_value_salt -- encrypted_value_iv + - id + - project_id + - key + - value + - encrypted_value + - encrypted_value_salt + - encrypted_value_iv Ci::Trigger: -- id -- token -- project_id -- created_at -- updated_at -- owner_id -- description -- ref + - id + - token + - project_id + - created_at + - updated_at + - owner_id + - description + - ref Ci::PipelineSchedule: -- id -- description -- ref -- cron -- cron_timezone -- next_run_at -- project_id -- owner_id -- active -- created_at -- updated_at + - id + - description + - ref + - cron + - cron_timezone + - next_run_at + - project_id + - owner_id + - active + - created_at + - updated_at Clusters::Cluster: -- id -- user_id -- enabled -- name -- provider_type -- platform_type -- created_at -- updated_at + - id + - user_id + - enabled + - name + - provider_type + - platform_type + - created_at + - updated_at Clusters::Project: -- id -- project_id -- cluster_id -- created_at -- updated_at + - id + - project_id + - cluster_id + - created_at + - updated_at Clusters::Providers::Gcp: -- id -- cluster_id -- status -- status_reason -- gcp_project_id -- zone -- num_nodes -- machine_type -- operation_id -- endpoint -- encrypted_access_token -- encrypted_access_token_iv -- created_at -- updated_at + - id + - cluster_id + - status + - status_reason + - gcp_project_id + - zone + - num_nodes + - machine_type + - operation_id + - endpoint + - encrypted_access_token + - encrypted_access_token_iv + - created_at + - updated_at Clusters::Platforms::Kubernetes: -- id -- cluster_id -- api_url -- ca_cert -- namespace -- username -- encrypted_password -- encrypted_password_iv -- encrypted_token -- encrypted_token_iv -- created_at -- updated_at + - id + - cluster_id + - api_url + - ca_cert + - namespace + - username + - encrypted_password + - encrypted_password_iv + - encrypted_token + - encrypted_token_iv + - created_at + - updated_at DeployKey: -- id -- user_id -- created_at -- updated_at -- key -- title -- type -- fingerprint -- public -- can_push -- last_used_at + - id + - user_id + - created_at + - updated_at + - key + - title + - type + - fingerprint + - public + - can_push + - last_used_at ProjectHook: -- id -- url -- project_id -- created_at -- updated_at -- type -- service_id -- push_events -- push_events_branch_filter -- issues_events -- merge_requests_events -- tag_push_events -- note_events -- pipeline_events -- enable_ssl_verification -- job_events -- wiki_page_events -- token -- group_id -- confidential_issues_events -- confidential_note_events -- repository_update_events -- releases_events + - id + - url + - project_id + - created_at + - updated_at + - type + - service_id + - push_events + - push_events_branch_filter + - issues_events + - merge_requests_events + - tag_push_events + - note_events + - pipeline_events + - enable_ssl_verification + - job_events + - wiki_page_events + - token + - group_id + - confidential_issues_events + - confidential_note_events + - repository_update_events + - releases_events ProtectedBranch: -- id -- project_id -- name -- created_at -- updated_at -- code_owner_approval_required + - id + - project_id + - name + - created_at + - updated_at + - code_owner_approval_required ProtectedTag: -- id -- project_id -- name -- created_at -- updated_at + - id + - project_id + - name + - created_at + - updated_at Project: -- description -- issues_enabled -- merge_requests_enabled -- wiki_enabled -- snippets_enabled -- visibility_level -- archived -- created_at -- updated_at -- last_activity_at -- star_count -- ci_id -- shared_runners_enabled -- build_coverage_regex -- build_allow_git_fetchs -- build_timeout -- pending_delete -- public_builds -- last_repository_check_failed -- last_repository_check_at -- collapse_outdated_diff_comments -- container_registry_enabled -- only_allow_merge_if_pipeline_succeeds -- has_external_issue_tracker -- request_access_enabled -- has_external_wiki -- only_allow_merge_if_all_discussions_are_resolved -- remove_source_branch_after_merge -- auto_cancel_pending_pipelines -- printing_merge_request_link_enabled -- resolve_outdated_diff_discussions -- build_allow_git_fetch -- merge_requests_template -- merge_requests_rebase_enabled -- approvals_before_merge -- merge_requests_author_approval -- reset_approvals_on_push -- disable_overriding_approvers_per_merge_request -- merge_requests_ff_only_enabled -- issues_template -- repository_size_limit -- sync_time -- service_desk_enabled -- last_repository_updated_at -- ci_config_path -- delete_error -- merge_requests_ff_only_enabled -- merge_requests_rebase_enabled -- jobs_cache_index -- external_authorization_classification_label -- external_webhook_token -- pages_https_only -- merge_requests_disable_committers_approval -- require_password_to_approve -- autoclose_referenced_issues -- suggestion_commit_message + - description + - issues_enabled + - merge_requests_enabled + - wiki_enabled + - snippets_enabled + - visibility_level + - archived + - created_at + - updated_at + - last_activity_at + - star_count + - ci_id + - shared_runners_enabled + - build_coverage_regex + - build_allow_git_fetchs + - build_timeout + - pending_delete + - public_builds + - last_repository_check_failed + - last_repository_check_at + - collapse_outdated_diff_comments + - container_registry_enabled + - only_allow_merge_if_pipeline_succeeds + - has_external_issue_tracker + - request_access_enabled + - has_external_wiki + - only_allow_merge_if_all_discussions_are_resolved + - remove_source_branch_after_merge + - auto_cancel_pending_pipelines + - printing_merge_request_link_enabled + - resolve_outdated_diff_discussions + - build_allow_git_fetch + - merge_requests_template + - merge_requests_rebase_enabled + - approvals_before_merge + - merge_requests_author_approval + - reset_approvals_on_push + - disable_overriding_approvers_per_merge_request + - merge_requests_ff_only_enabled + - issues_template + - repository_size_limit + - sync_time + - service_desk_enabled + - last_repository_updated_at + - ci_config_path + - delete_error + - merge_requests_ff_only_enabled + - merge_requests_rebase_enabled + - jobs_cache_index + - external_authorization_classification_label + - external_webhook_token + - pages_https_only + - merge_requests_disable_committers_approval + - require_password_to_approve + - autoclose_referenced_issues + - suggestion_commit_message ProjectTracingSetting: -- external_url + - external_url Author: -- name + - name ProjectFeature: -- id -- project_id -- merge_requests_access_level -- forking_access_level -- issues_access_level -- wiki_access_level -- snippets_access_level -- builds_access_level -- repository_access_level -- pages_access_level -- metrics_dashboard_access_level -- requirements_access_level -- operations_access_level -- created_at -- updated_at + - id + - project_id + - merge_requests_access_level + - forking_access_level + - issues_access_level + - wiki_access_level + - snippets_access_level + - builds_access_level + - repository_access_level + - pages_access_level + - metrics_dashboard_access_level + - requirements_access_level + - operations_access_level + - created_at + - updated_at ProtectedBranch::MergeAccessLevel: -- id -- protected_branch_id -- access_level -- created_at -- updated_at -- user_id -- group_id + - id + - protected_branch_id + - access_level + - created_at + - updated_at + - user_id + - group_id ProtectedBranch::PushAccessLevel: -- id -- protected_branch_id -- access_level -- created_at -- updated_at -- user_id -- group_id -- deploy_key_id + - id + - protected_branch_id + - access_level + - created_at + - updated_at + - user_id + - group_id + - deploy_key_id ProtectedBranch::UnprotectAccessLevel: -- id -- protected_branch_id -- access_level -- created_at -- updated_at -- user_id -- group_id + - id + - protected_branch_id + - access_level + - created_at + - updated_at + - user_id + - group_id ProtectedTag::CreateAccessLevel: -- id -- protected_tag_id -- access_level -- created_at -- updated_at -- user_id -- group_id + - id + - protected_tag_id + - access_level + - created_at + - updated_at + - user_id + - group_id AwardEmoji: -- id -- user_id -- name -- awardable_type -- created_at -- updated_at + - id + - user_id + - name + - awardable_type + - created_at + - updated_at LabelPriority: -- id -- project_id -- priority -- created_at -- updated_at + - id + - project_id + - priority + - created_at + - updated_at Timelog: -- id -- time_spent -- merge_request_id -- user_id -- spent_at -- created_at -- updated_at + - id + - time_spent + - merge_request_id + - user_id + - spent_at + - created_at + - updated_at ProjectAutoDevops: -- id -- enabled -- domain -- deploy_strategy -- project_id -- created_at -- updated_at + - id + - enabled + - domain + - deploy_strategy + - project_id + - created_at + - updated_at IssueAssignee: -- user_id + - user_id ProjectCustomAttribute: -- id -- created_at -- updated_at -- project_id -- key -- value + - id + - created_at + - updated_at + - project_id + - key + - value PrometheusMetric: -- id -- created_at -- updated_at -- project_id -- y_label -- unit -- legend -- title -- query -- group -- common -- identifier -- dashboard_path + - id + - created_at + - updated_at + - project_id + - y_label + - unit + - legend + - title + - query + - group + - common + - identifier + - dashboard_path PrometheusAlert: -- threshold -- operator -- environment_id -- project_id -- prometheus_metric_id + - threshold + - operator + - environment_id + - project_id + - prometheus_metric_id Badge: -- id -- name -- link_url -- image_url -- project_id -- group_id -- created_at -- updated_at -- type + - id + - name + - link_url + - image_url + - project_id + - group_id + - created_at + - updated_at + - type ProjectCiCdSetting: -- group_runners_enabled + - group_runners_enabled ProjectSetting: -- allow_merge_on_skipped_pipeline -- has_confluence -- has_vulnerabilities + - allow_merge_on_skipped_pipeline + - has_confluence + - has_vulnerabilities ProtectedEnvironment: -- id -- project_id -- name -- created_at -- updated_at + - id + - project_id + - name + - created_at + - updated_at ProtectedEnvironment::DeployAccessLevel: -- id -- protected_environment_id -- access_level -- created_at -- updated_at -- user_id -- group_id + - id + - protected_environment_id + - access_level + - created_at + - updated_at + - user_id + - group_id ResourceLabelEvent: -- id -- action -- merge_request_id -- label_id -- user_id -- created_at + - id + - action + - merge_request_id + - label_id + - user_id + - created_at ErrorTracking::ProjectErrorTrackingSetting: -- api_url -- project_id -- project_name -- organization_name + - api_url + - project_id + - project_name + - organization_name SentryIssue: -- id -- sentry_issue_identifier + - id + - sentry_issue_identifier Suggestion: -- id -- relative_order -- applied -- commit_id -- from_content -- to_content -- outdated -- lines_above -- lines_below + - id + - relative_order + - applied + - commit_id + - from_content + - to_content + - outdated + - lines_above + - lines_below MergeRequestAssignee: -- id -- user_id -- merge_request_id + - id + - user_id + - merge_request_id ProjectMetricsSetting: -- project_id -- external_dashboard_url -- created_at -- updated_at -- dashboard_timezone + - project_id + - external_dashboard_url + - created_at + - updated_at + - dashboard_timezone Board: -- id -- project_id -- created_at -- updated_at -- group_id -- milestone_id -- iteration_id -- weight -- name -- hide_backlog_list -- hide_closed_list + - id + - project_id + - created_at + - updated_at + - group_id + - milestone_id + - iteration_id + - weight + - name + - hide_backlog_list + - hide_closed_list List: -- id -- board_id -- label_id -- list_type -- position -- created_at -- updated_at -- milestone_id -- user_id -- max_issue_count -- max_issue_weight -- limit_metric + - id + - board_id + - label_id + - list_type + - position + - created_at + - updated_at + - milestone_id + - user_id + - max_issue_count + - max_issue_weight + - limit_metric ExternalPullRequest: -- id -- created_at -- updated_at -- project_id -- pull_request_iid -- status -- source_branch -- target_branch -- source_repository -- target_repository -- source_sha -- target_sha + - id + - created_at + - updated_at + - project_id + - pull_request_iid + - status + - source_branch + - target_branch + - source_repository + - target_repository + - source_sha + - target_sha DesignManagement::Design: -- id -- iid -- project_id -- filename -- relative_position + - id + - iid + - project_id + - filename + - relative_position DesignManagement::Action: -- id -- event -- image_v432x230 + - id + - event + - image_v432x230 DesignManagement::Version: -- id -- created_at -- sha -- author_id + - id + - created_at + - sha + - author_id ZoomMeeting: -- id -- project_id -- issue_status -- url -- created_at -- updated_at + - id + - project_id + - issue_status + - url + - created_at + - updated_at ServiceDeskSetting: -- project_id -- issue_template_key -- project_key + - project_id + - issue_template_key + - project_key ContainerExpirationPolicy: -- created_at -- updated_at -- next_run_at -- project_id -- name_regex -- name_regex_keep -- cadence -- older_than -- keep_n -- enabled + - created_at + - updated_at + - next_run_at + - project_id + - name_regex + - name_regex_keep + - cadence + - older_than + - keep_n + - enabled Epic: - id - milestone_id @@ -861,6 +861,7 @@ ProjectSecuritySetting: - auto_fix_sast - created_at - updated_at + - cve_id_request_enabled IssuableSla: - issue_id - due_at @@ -881,8 +882,8 @@ PushRule: - commit_committer_check - regexp_uses_re2 MergeRequest::CleanupSchedule: -- id -- scheduled_at -- completed_at -- created_at -- updated_at + - id + - scheduled_at + - completed_at + - created_at + - updated_at -- GitLab From f1ee0ccbd48f3036b4229a7b3bf145a83b4fb12c Mon Sep 17 00:00:00 2001 From: James Johnson Date: Fri, 11 Dec 2020 13:20:33 -0800 Subject: [PATCH 2/2] Addressed all feedback from the merge request --- .../confidential/confidential_issue_sidebar.vue | 2 +- .../unreleased/add_request_cve_issue.yml | 0 .../cve_id_request/cve_id_request_sidebar.vue | 11 +++++++---- ee/app/helpers/ee/cve_request_helper.rb | 8 ++++---- ee/app/helpers/ee/projects_helper.rb | 11 ++--------- ee/app/models/ee/project.rb | 4 ++++ ee/app/serializers/ee/issue_sidebar_basic_entity.rb | 2 +- ee/spec/helpers/ee/cve_request_helper_spec.rb | 11 +++++++++-- 8 files changed, 28 insertions(+), 21 deletions(-) rename {ee/changelogs => changelogs}/unreleased/add_request_cve_issue.yml (100%) diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue index ce120ff82f38..902161e43227 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -73,7 +73,7 @@ export default {