From f09384eaf87490f71a4d62ff91eb081dd5bfe1f6 Mon Sep 17 00:00:00 2001 From: Miranda Fluharty Date: Thu, 11 Dec 2025 14:40:51 -0700 Subject: [PATCH 1/2] Borrow feature flag and component --- .../scanner_profile_configuration.vue | 124 ++++++++++++++++++ .../wip/security_scan_profiles_feature.yml | 10 ++ 2 files changed, 134 insertions(+) create mode 100644 app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue create mode 100644 ee/config/feature_flags/wip/security_scan_profiles_feature.yml diff --git a/app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue b/app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue new file mode 100644 index 00000000000000..f7a7c4bf1a44ff --- /dev/null +++ b/app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue @@ -0,0 +1,124 @@ + + + diff --git a/ee/config/feature_flags/wip/security_scan_profiles_feature.yml b/ee/config/feature_flags/wip/security_scan_profiles_feature.yml new file mode 100644 index 00000000000000..5522daf4602539 --- /dev/null +++ b/ee/config/feature_flags/wip/security_scan_profiles_feature.yml @@ -0,0 +1,10 @@ +--- +name: security_scan_profiles_feature +description: +feature_issue_url: +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/215433 +rollout_issue_url: +milestone: '18.7' +group: group::security platform management +type: wip +default_enabled: false -- GitLab From e88944def6bbb675f8f27cbab08357e7c3290a8c Mon Sep 17 00:00:00 2001 From: Miranda Fluharty Date: Thu, 11 Dec 2025 14:45:46 -0700 Subject: [PATCH 2/2] Add scanners bulk edit drawer to inventory Behind the security_scan_profiles_feature feature flag and the can_apply_profile permission (stubbed true for now) Show a dropdown menu for bulk actions and an action next to subgroups that both open a full-width drawer for applying/removing profiles --- .../bulk_scanner_profile_configuration.vue | 163 ++++++++++++++++++ .../scanner_profile_configuration.vue | 124 ------------- .../components/action_cell.vue | 68 ++++++-- .../components/bulk_edit_actions_dropdown.vue | 52 ++++++ .../bulk_scanners_update_drawer.vue | 71 ++++++++ .../components/inventory_dashboard.vue | 24 ++- .../components/security_inventory_table.vue | 45 +++-- .../security_inventory/constants.js | 3 + .../javascripts/security_inventory/index.js | 2 + .../groups/security/inventory_controller.rb | 1 + .../groups/security_features_helper.rb | 1 + locale/gitlab.pot | 44 +++++ 12 files changed, 443 insertions(+), 155 deletions(-) create mode 100644 app/assets/javascripts/security_configuration/components/bulk_scanner_profile_configuration.vue delete mode 100644 app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue create mode 100644 ee/app/assets/javascripts/security_inventory/components/bulk_edit_actions_dropdown.vue create mode 100644 ee/app/assets/javascripts/security_inventory/components/bulk_scanners_update_drawer.vue diff --git a/app/assets/javascripts/security_configuration/components/bulk_scanner_profile_configuration.vue b/app/assets/javascripts/security_configuration/components/bulk_scanner_profile_configuration.vue new file mode 100644 index 00000000000000..cccdcb6197601b --- /dev/null +++ b/app/assets/javascripts/security_configuration/components/bulk_scanner_profile_configuration.vue @@ -0,0 +1,163 @@ + + + diff --git a/app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue b/app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue deleted file mode 100644 index f7a7c4bf1a44ff..00000000000000 --- a/app/assets/javascripts/security_configuration/components/scanner_profile_configuration.vue +++ /dev/null @@ -1,124 +0,0 @@ - - - diff --git a/ee/app/assets/javascripts/security_inventory/components/action_cell.vue b/ee/app/assets/javascripts/security_inventory/components/action_cell.vue index e22361aabe2e3c..3e206e59affcc8 100644 --- a/ee/app/assets/javascripts/security_inventory/components/action_cell.vue +++ b/ee/app/assets/javascripts/security_inventory/components/action_cell.vue @@ -1,5 +1,5 @@ + + diff --git a/ee/app/assets/javascripts/security_inventory/components/bulk_scanners_update_drawer.vue b/ee/app/assets/javascripts/security_inventory/components/bulk_scanners_update_drawer.vue new file mode 100644 index 00000000000000..efb172f3187562 --- /dev/null +++ b/ee/app/assets/javascripts/security_inventory/components/bulk_scanners_update_drawer.vue @@ -0,0 +1,71 @@ + + + diff --git a/ee/app/assets/javascripts/security_inventory/components/inventory_dashboard.vue b/ee/app/assets/javascripts/security_inventory/components/inventory_dashboard.vue index 50b22ae7e2453a..bd261e63329bb0 100644 --- a/ee/app/assets/javascripts/security_inventory/components/inventory_dashboard.vue +++ b/ee/app/assets/javascripts/security_inventory/components/inventory_dashboard.vue @@ -4,6 +4,7 @@ import { debounce } from 'lodash'; import { fetchPolicies } from '~/lib/graphql'; import * as Sentry from '~/sentry/sentry_browser_wrapper'; import { s__, n__, sprintf } from '~/locale'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { createAlert } from '~/alert'; import { getLocationHash, @@ -21,6 +22,7 @@ import SubgroupSidebar from './sidebar/subgroup_sidebar.vue'; import EmptyState from './empty_state.vue'; import SecurityInventoryTable from './security_inventory_table.vue'; import InventoryDashboardFilteredSearchBar from './inventory_dashboard_filtered_search_bar.vue'; +import BulkEditActionsDropdown from './bulk_edit_actions_dropdown.vue'; const PAGE_SIZE = 20; @@ -35,11 +37,20 @@ export default { EmptyState, SecurityInventoryTable, InventoryDashboardFilteredSearchBar, + BulkEditActionsDropdown, }, directives: { GlTooltip: GlTooltipDirective, }, - inject: ['groupFullPath', 'groupId', 'newProjectPath', 'canReadAttributes'], + mixins: [glFeatureFlagMixin()], + inject: [ + 'groupFullPath', + 'groupId', + 'newProjectPath', + 'canReadAttributes', + 'canManageAttributes', + 'canApplyProfiles', + ], i18n: { errorFetchingChildren: s__( 'SecurityInventory|An error occurred while fetching subgroups and projects. Please try again.', @@ -335,9 +346,9 @@ export default { url: urlWithHashPreserved, }); }, - bulkEdit() { + bulkEdit(type) { this.$nextTick(() => { - this.$refs.inventoryTable.bulkEdit(); + this.$refs.inventoryTable.bulkEdit(type); }); }, handleSelectedCount(count) { @@ -397,7 +408,12 @@ export default { variant="warning" /> - {{ + + {{ s__('SecurityAttributes|Edit security attributes') }} diff --git a/ee/app/assets/javascripts/security_inventory/components/security_inventory_table.vue b/ee/app/assets/javascripts/security_inventory/components/security_inventory_table.vue index 6aaf48ce8c8774..3d5342e242d5fd 100644 --- a/ee/app/assets/javascripts/security_inventory/components/security_inventory_table.vue +++ b/ee/app/assets/javascripts/security_inventory/components/security_inventory_table.vue @@ -4,7 +4,8 @@ import { __, s__ } from '~/locale'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import ProjectAttributesUpdateDrawer from 'ee_component/security_configuration/security_attributes/components/project_attributes_update_drawer.vue'; import BulkAttributesUpdateDrawer from 'ee_component/security_configuration/security_attributes/components/bulk_attributes_update_drawer.vue'; -import { MAX_SELECTED_COUNT } from '../constants'; +import { MAX_SELECTED_COUNT, ACTION_TYPE_BULK_EDIT_SCANNERS } from '../constants'; +import BulkScannersUpdateDrawer from './bulk_scanners_update_drawer.vue'; import NameCell from './name_cell.vue'; import VulnerabilityCell from './vulnerability_cell.vue'; import ToolCoverageCell from './tool_coverage_cell.vue'; @@ -27,12 +28,13 @@ export default { CheckboxCell, ProjectAttributesUpdateDrawer, BulkAttributesUpdateDrawer, + BulkScannersUpdateDrawer, }, directives: { GlTooltip: GlTooltipDirective, }, mixins: [glFeatureFlagMixin()], - inject: ['canReadAttributes', 'canManageAttributes'], + inject: ['canReadAttributes', 'canManageAttributes', 'canApplyProfiles'], props: { items: { type: Array, @@ -62,7 +64,10 @@ export default { : this.items; }, shouldShowBulkEdit() { - return this.glFeatures.securityContextLabels && this.canManageAttributes; + return ( + (this.glFeatures.securityContextLabels && this.canManageAttributes) || + (this.glFeatures.securityScanProfilesFeature && this.canApplyProfiles) + ); }, isAnyItemSelected() { return this.items.some((item) => this.isSelected(item)); @@ -121,15 +126,19 @@ export default { } this.$emit('selectedCount', this.selectedItems.length); }, - // eslint-disable-next-line vue/no-unused-properties clearSelection() { this.selectAll(false); }, - // eslint-disable-next-line vue/no-unused-properties - bulkEdit() { - this.$nextTick(() => { - this.$refs.bulkAttributesDrawer.openDrawer(); - }); + bulkEdit(type) { + if (type === ACTION_TYPE_BULK_EDIT_SCANNERS) { + this.$nextTick(() => { + this.$refs.bulkScannersDrawer.openDrawer(); + }); + } else { + this.$nextTick(() => { + this.$refs.bulkAttributesDrawer.openDrawer(); + }); + } }, openAttributesDrawer(item) { this.selectedProject = item; @@ -137,6 +146,11 @@ export default { this.$refs.attributesDrawer.openDrawer(); }); }, + openScannersDrawer(item) { + this.clearSelection(); + this.selectedItems = [item]; + this.bulkEdit(ACTION_TYPE_BULK_EDIT_SCANNERS); + }, refreshDashboard() { this.$emit('refetch'); }, @@ -215,7 +229,12 @@ export default { - + @@ -232,5 +251,11 @@ export default { :item-ids="selectedItems" @refetch="refreshDashboard" /> + diff --git a/ee/app/assets/javascripts/security_inventory/constants.js b/ee/app/assets/javascripts/security_inventory/constants.js index 364ecc25f7c2f5..3df2d7aac36c29 100644 --- a/ee/app/assets/javascripts/security_inventory/constants.js +++ b/ee/app/assets/javascripts/security_inventory/constants.js @@ -159,3 +159,6 @@ export const SEVERITY_FILTER_OPERATOR_TO_CONST = { }; export const MAX_SELECTED_COUNT = 100; + +export const ACTION_TYPE_BULK_EDIT_SCANNERS = 'SCANNERS'; +export const ACTION_TYPE_BULK_EDIT_ATTRIBUTES = 'ATTRIBUTES'; diff --git a/ee/app/assets/javascripts/security_inventory/index.js b/ee/app/assets/javascripts/security_inventory/index.js index cc32ec84c19043..74b7d8be672908 100644 --- a/ee/app/assets/javascripts/security_inventory/index.js +++ b/ee/app/assets/javascripts/security_inventory/index.js @@ -18,6 +18,7 @@ export default () => { groupName, canManageAttributes, canReadAttributes, + canApplyProfiles, groupManageAttributesPath, newProjectPath, } = el.dataset; @@ -31,6 +32,7 @@ export default () => { groupName, canManageAttributes: parseBoolean(canManageAttributes), canReadAttributes: parseBoolean(canReadAttributes), + canApplyProfiles: parseBoolean(canApplyProfiles), groupManageAttributesPath, newProjectPath, }, diff --git a/ee/app/controllers/groups/security/inventory_controller.rb b/ee/app/controllers/groups/security/inventory_controller.rb index ea49c38a7ba971..2b5b8d5d3d5c87 100644 --- a/ee/app/controllers/groups/security/inventory_controller.rb +++ b/ee/app/controllers/groups/security/inventory_controller.rb @@ -10,6 +10,7 @@ class InventoryController < Groups::ApplicationController before_action do push_frontend_feature_flag(:security_context_labels, @group.root_ancestor) push_frontend_feature_flag(:security_inventory_filtering, @group.root_ancestor) + push_frontend_feature_flag(:security_scan_profiles_feature, @group.root_ancestor) end feature_category :security_asset_inventories diff --git a/ee/app/helpers/groups/security_features_helper.rb b/ee/app/helpers/groups/security_features_helper.rb index 8b159d14611598..45dad19dc2ee31 100644 --- a/ee/app/helpers/groups/security_features_helper.rb +++ b/ee/app/helpers/groups/security_features_helper.rb @@ -42,6 +42,7 @@ def group_level_security_inventory_data(group) group_name: group.name, can_manage_attributes: can?(current_user, :admin_security_attributes, group.root_ancestor).to_s, can_read_attributes: can?(current_user, :read_security_attribute, group).to_s, + can_apply_profiles: true.to_s, # can?(current_user, :apply_security_profile, group).to_s, group_manage_attributes_path: group_security_configuration_path(group.root_ancestor), new_project_path: new_project_path(namespace_id: group.id) } diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cc82c7ae14a5c6..668e3213a99afc 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9216,6 +9216,9 @@ msgstr "" msgid "Apply a template" msgstr "" +msgid "Apply default profile to all" +msgstr "" + msgid "Apply selected attributes to projects (keeps existing attributes)" msgstr "" @@ -24897,6 +24900,9 @@ msgstr "" msgid "Disable fetching cloud alerts if this GitLab instance is isolated from the public internet, or does not use observe.gitlab.com for Observability features." msgstr "" +msgid "Disable for all" +msgstr "" + msgid "Disable inviting new members to group or project by group/project owners or project maintainers" msgstr "" @@ -42616,6 +42622,9 @@ msgstr "" msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile" msgstr "" +msgid "Mixed" +msgstr "" + msgid "MlExperimentTracking|%d experiment" msgid_plural "MlExperimentTracking|%d experiments" msgstr[0] "" @@ -44572,6 +44581,9 @@ msgstr "" msgid "No prioritized labels yet!" msgstr "" +msgid "No profile applied" +msgstr "" + msgid "No project found" msgstr "" @@ -50949,6 +50961,9 @@ msgstr "" msgid "Preview changes" msgstr "" +msgid "Preview default profile" +msgstr "" + msgid "Preview diagram" msgstr "" @@ -58198,6 +58213,9 @@ msgstr "" msgid "SCIM|System for Cross-Domain Identity Management." msgstr "" +msgid "SD" +msgstr "" + msgid "SHA256" msgstr "" @@ -59459,6 +59477,12 @@ msgstr "" msgid "Secret Detection" msgstr "" +msgid "Secret Push Protection" +msgstr "" + +msgid "Secret Push Protection (default)" +msgstr "" + msgid "Secret detection" msgstr "" @@ -60469,6 +60493,11 @@ msgstr "" msgid "SecurityInventory|Edit security attributes" msgstr "" +msgid "SecurityInventory|Edit security scanners for %d item" +msgid_plural "SecurityInventory|Edit security scanners for %d items" +msgstr[0] "" +msgstr[1] "" + msgid "SecurityInventory|GitLab Advanced SAST" msgstr "" @@ -60487,6 +60516,12 @@ msgstr "" msgid "SecurityInventory|Load more" msgstr "" +msgid "SecurityInventory|Manage security scanners" +msgstr "" + +msgid "SecurityInventory|Manage security scanners for subgroup projects" +msgstr "" + msgid "SecurityInventory|Manage tool coverage" msgstr "" @@ -60526,6 +60561,9 @@ msgstr "" msgid "SecurityInventory|Security inventory" msgstr "" +msgid "SecurityInventory|Select bulk action" +msgstr "" + msgid "SecurityInventory|Severity critical" msgstr "" @@ -68235,6 +68273,9 @@ msgstr "" msgid "The selected project is not available" msgstr "" +msgid "The selected projects use different configuration profiles. Choosing a new profile will replace their existing ones." +msgstr "" + msgid "The snippet can be accessed without any authentication." msgstr "" @@ -76025,6 +76066,9 @@ msgstr "" msgid "What is repository mirroring?" msgstr "" +msgid "What is secret push protection?" +msgstr "" + msgid "What is squashing?" msgstr "" -- GitLab