Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-04-30 18:15:48 +00:00
parent 35305bfc83
commit 79c812847f
54 changed files with 8087 additions and 254 deletions

View File

@ -1,6 +0,0 @@
---
# Cop supports --autocorrect.
Layout/LeadingCommentSpace:
Exclude:
- 'config/initializers/kaminari_active_record_relation_methods_with_limit.rb'
- 'lib/uploaded_file.rb'

View File

@ -2,17 +2,6 @@
# Cop supports --autocorrect.
Layout/SpaceInsideParens:
Exclude:
- 'ee/app/models/ee/dependency_proxy/blob.rb'
- 'ee/app/models/ee/dependency_proxy/manifest.rb'
- 'ee/app/services/gitlab_subscriptions/notify_seats_exceeded_batch_service.rb'
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
- 'ee/lib/gitlab/auth/smartcard/session.rb'
- 'ee/lib/system_check/geo/current_node_check.rb'
- 'ee/spec/controllers/projects/mirrors_controller_spec.rb'
- 'ee/spec/features/groups/saml_enforcement_spec.rb'
- 'ee/spec/finders/ee/alert_management/http_integrations_finder_spec.rb'
- 'ee/spec/finders/epics_finder_spec.rb'
- 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
- 'ee/spec/finders/security/vulnerability_feedbacks_finder_spec.rb'
- 'ee/spec/frontend/fixtures/analytics/devops_reports/devops_adoption/enabled_namespaces.rb'
- 'ee/spec/frontend/fixtures/epic.rb'

View File

@ -133,7 +133,7 @@ gem 'net-ldap', '~> 0.17.1' # rubocop:todo Gemfile/MissingFeatureCategory
# API
gem 'grape', '~> 2.0.0', feature_category: :api
gem 'grape-entity', '~> 0.10.2', feature_category: :api
gem 'grape-entity', '~> 1.0.1', feature_category: :api
gem 'grape-swagger', '~> 2.0.2', group: [:development, :test], feature_category: :api
gem 'grape-swagger-entity', '~> 0.5.1', group: [:development, :test], feature_category: :api
gem 'grape-path-helpers', '~> 2.0.1', feature_category: :api

View File

@ -270,7 +270,7 @@
{"name":"googleauth","version":"1.8.1","platform":"ruby","checksum":"814adadaaa1221dce72a67131e3ecbd6d23491a161ec84fb15fd353b87d8c9e7"},
{"name":"gpgme","version":"2.0.23","platform":"ruby","checksum":"c87bbafdb8719da7c58ebcac08297aa1fb227022ac6cd2972829ba68adc91c04"},
{"name":"grape","version":"2.0.0","platform":"ruby","checksum":"3aeff94c17e84ccead4ff98833df691e7da0c108878cc128ca31f80c1047494a"},
{"name":"grape-entity","version":"0.10.2","platform":"ruby","checksum":"9eb584548135419d1c8ada7d21f7c174a7644e56a8b8e5bfc65d1a7a3421b571"},
{"name":"grape-entity","version":"1.0.1","platform":"ruby","checksum":"e00f9e94e407aff77aa2945d741f544d07e48501927942988799913151d02634"},
{"name":"grape-path-helpers","version":"2.0.1","platform":"ruby","checksum":"ad5216e52c6e796738a9118087352ab4c962900dbad1d8f8c0f96e093c6702d7"},
{"name":"grape-swagger","version":"2.0.2","platform":"ruby","checksum":"a7139a56ba36fab2e8465f10d668a8c73c30cf44ebe8af960f5a4e3beb200805"},
{"name":"grape-swagger-entity","version":"0.5.1","platform":"ruby","checksum":"f51e372d00ac96cf90d948f87b3f4eb287ab053976ca57ad503d442ad8605523"},

View File

@ -846,7 +846,7 @@ GEM
mustermann-grape (~> 1.0.0)
rack (>= 1.3.0)
rack-accept
grape-entity (0.10.2)
grape-entity (1.0.1)
activesupport (>= 3.0.0)
multi_json (>= 1.3.2)
grape-path-helpers (2.0.1)
@ -2047,7 +2047,7 @@ DEPENDENCIES
googleauth (~> 1.8.1)
gpgme (~> 2.0.23)
grape (~> 2.0.0)
grape-entity (~> 0.10.2)
grape-entity (~> 1.0.1)
grape-path-helpers (~> 2.0.1)
grape-swagger (~> 2.0.2)
grape-swagger-entity (~> 0.5.1)

View File

@ -0,0 +1,90 @@
<script>
import { GlBanner, GlTableLite, GlBadge } from '@gitlab/ui';
import emptyStateIllustration from '@gitlab/svgs/dist/illustrations/lock_promotion.svg?url';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import { s__, __ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
export default {
components: {
UserCalloutDismisser,
GlBanner,
GlTableLite,
GlBadge,
},
props: {
bannerTitle: {
type: String,
required: false,
default: s__('Deployment|Upgrade to get more our of your deployments'),
},
buttonText: {
type: String,
required: false,
default: __('Learn more'),
},
buttonLink: {
type: String,
required: false,
default: helpPagePath('ci/environments/deployment_approvals'),
},
illustration: {
type: String,
required: false,
default: emptyStateIllustration,
},
},
fields: [
{ key: 'approvers', label: s__('DeploymentApprovals|Approvers') },
{ key: 'approvals', label: s__('DeploymentApprovals|Approvals') },
{ key: 'approvedBy', label: s__('DeploymentApprovals|Approved By') },
],
items: [
{ approvers: s__('Deployment|You'), approvals: '0/1' },
{ approvers: s__('Deployment|A colleague'), approvals: '0/1' },
],
i18n: {
bannerDescription: s__(
'Deployment|Improve your continuous delivery practices with deployment approvals. Configure rules for required approvals, control which users can deploy to your environments, and collaborate throughout the delivery process.',
),
tableHeader: s__('Deployment|Deployment approvals require a Premium or Ultimate subscription'),
readyText: s__('Deployment|Ready to use deployment approvals?'),
premiumTitle: __('GitLab Premium'),
},
};
</script>
<template>
<user-callout-dismisser feature-name="deployment_approvals_empty_state">
<template #default="{ dismiss, shouldShowCallout }">
<gl-banner
v-if="shouldShowCallout"
:title="bannerTitle"
:button-text="buttonText"
:button-link="buttonLink"
:svg-path="illustration"
class="gl-mt-5"
@close="dismiss"
>
<p>{{ $options.i18n.bannerDescription }}</p>
<div class="gl-border-l gl-border-t gl-border-r gl-rounded-base gl-mb-5">
<div class="gl-m-5">
<slot name="table-header">
<gl-badge size="md" class="gl-mr-2 gl-align-middle" icon="license" variant="tier">{{
$options.i18n.premiumTitle
}}</gl-badge>
<strong class="gl-align-middle">{{ $options.i18n.tableHeader }}</strong>
</slot>
</div>
<gl-table-lite :fields="$options.fields" :items="$options.items" />
</div>
<template #actions>
<slot name="banner-actions">
<strong class="gl-ml-2 gl-align-middle">{{ $options.i18n.readyText }}</strong>
</slot>
</template>
</gl-banner>
</template>
</user-callout-dismisser>
</template>

View File

@ -23,6 +23,8 @@ export default {
DeploymentApprovals: () =>
import('ee_component/deployments/components/deployment_approvals.vue'),
DeploymentTimeline: () => import('ee_component/deployments/components/deployment_timeline.vue'),
ApprovalsEmptyState: () =>
import('ee_else_ce/deployments/components/approvals_empty_state.vue'),
},
inject: ['projectPath', 'deploymentIid', 'environmentName', 'graphqlEtagKey'],
apollo: {
@ -70,6 +72,9 @@ export default {
isManual() {
return this.deployment.job?.manualJob;
},
isLoading() {
return this.$apollo.queries.deployment.loading;
},
},
mounted() {
toggleQueryPollingByVisibility(
@ -99,7 +104,7 @@ export default {
v-else
:deployment="deployment"
:environment="environment"
:loading="$apollo.queries.deployment.loading"
:loading="isLoading"
/>
<details-feedback class="gl-mt-6 gl-w-90p" />
<deployment-approvals
@ -119,10 +124,15 @@ export default {
:approval-summary="deployment.approvalSummary"
class="gl-w-90p"
/>
<approvals-empty-state
v-if="!isLoading"
:approval-summary="deployment.approvalSummary"
class="gl-w-90p"
/>
</div>
<deployment-aside
v-if="!hasError"
:loading="$apollo.queries.deployment.loading"
:loading="isLoading"
:deployment="deployment"
:environment="environment"
class="gl-w-20p"

View File

@ -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 ShowDeployment from './components/show_deployment.vue';
Vue.use(VueApollo);
@ -11,7 +12,14 @@ export const initializeShowDeployment = (selector = 'js-deployment-details') =>
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
const { projectPath, deploymentIid, environmentName, graphqlEtagKey } = el.dataset;
const {
projectPath,
deploymentIid,
environmentName,
graphqlEtagKey,
protectedEnvironmentsAvailable,
protectedEnvironmentsSettingsPath,
} = el.dataset;
return new Vue({
el,
@ -21,6 +29,8 @@ export const initializeShowDeployment = (selector = 'js-deployment-details') =>
deploymentIid,
environmentName,
graphqlEtagKey,
protectedEnvironmentsAvailable: parseBoolean(protectedEnvironmentsAvailable),
protectedEnvironmentsSettingsPath,
},
render(h) {
return h(ShowDeployment);

View File

@ -13,6 +13,9 @@ import {
} from '../utils/trigger_successful_invite_alert';
import {
BLOCKED_SEAT_OVERAGES_ERROR_REASON,
BLOCKED_SEAT_OVERAGES_BODY,
BLOCKED_SEAT_OVERAGES_CTA,
PROJECT_SELECT_LABEL_ID,
IMPORT_PROJECT_MEMBERS_MODAL_TRACKING_CATEGORY,
IMPORT_PROJECT_MEMBERS_MODAL_TRACKING_LABEL,
@ -42,6 +45,11 @@ export default {
label: IMPORT_PROJECT_MEMBERS_MODAL_TRACKING_LABEL,
}),
],
inject: {
addSeatsHref: {
default: '',
},
},
props: {
projectId: {
type: String,
@ -64,6 +72,7 @@ export default {
},
data() {
return {
errorReason: '',
projectToBeImported: {},
invalidFeedbackMessage: '',
totalMembersCount: 0,
@ -137,6 +146,9 @@ export default {
count: this.errorsExpanded.length,
});
},
shouldShowSeatOverageNotification() {
return this.errorReason === BLOCKED_SEAT_OVERAGES_ERROR_REASON && this.addSeatsHref;
},
},
mounted() {
if (this.reloadPageOnSubmit) {
@ -178,8 +190,9 @@ export default {
this.onInviteSuccess();
}
} catch (error) {
const message = error.response.data?.message;
const { message, reason } = error.response.data || {};
this.errorReason = reason;
this.showErrorAlert(message);
} finally {
this.isLoading = false;
@ -213,6 +226,7 @@ export default {
this.track('click_x');
},
clearValidation() {
this.errorReason = '';
this.invalidFeedbackMessage = '';
this.invalidMembers = {};
},
@ -240,6 +254,8 @@ export default {
modalCancelButton: __('Cancel'),
defaultError: s__('ImportAProjectModal|Unable to import project members'),
successMessage: s__('ImportAProjectModal|Successfully imported'),
BLOCKED_SEAT_OVERAGES_BODY,
BLOCKED_SEAT_OVERAGES_CTA,
},
errorsLimit: 2,
projectSelectLabelId: PROJECT_SELECT_LABEL_ID,
@ -329,6 +345,18 @@ export default {
>
<project-select v-model="projectToBeImported" />
</gl-form-group>
<gl-alert
v-if="shouldShowSeatOverageNotification"
id="import-project-members-seat-overages-alert"
class="gl-mb-4"
dismissable
data-testid="import-project-members-seat-overages-alert"
:primary-button-link="addSeatsHref"
:primary-button-text="$options.i18n.BLOCKED_SEAT_OVERAGES_CTA"
@dismiss="errorReason = false"
>
{{ $options.i18n.BLOCKED_SEAT_OVERAGES_BODY }}
</gl-alert>
<p>{{ $options.i18n.modalHelpText }}</p>
</gl-modal>
</template>

View File

@ -9,12 +9,19 @@ export default function initImportProjectMembersModal() {
return false;
}
const { projectId, projectName, reloadPageOnSubmit, usersLimitDataset } = el.dataset;
const {
projectId,
projectName,
reloadPageOnSubmit,
usersLimitDataset,
addSeatsHref,
} = el.dataset;
return new Vue({
el,
provide: {
name: projectName,
addSeatsHref,
},
render: (createElement) =>
createElement(ImportProjectMembersModal, {

View File

@ -24,7 +24,7 @@ export default {
i18n: {
toggleLabelTitle: s__('CICD|Limit access %{italicStart}to%{italicEnd} this project'),
toggleHelpText: s__(
`CICD|Prevent access to this project from other project CI/CD job tokens, unless the other project is added to the allowlist. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}`,
`CICD|Prevent access to this project from other project CI/CD job tokens, unless the other project is added to the allowlist. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more%{linkEnd}.`,
),
cardHeaderTitle: s__(
'CICD|Allow CI job tokens from the following projects to access this project',
@ -218,9 +218,9 @@ export default {
<template #help>
<gl-sprintf :message="$options.i18n.toggleHelpText">
<template #link="{ content }">
<gl-link :href="ciJobTokenHelpPage" class="inline-link" target="_blank">
{{ content }}
</gl-link>
<gl-link :href="ciJobTokenHelpPage" class="inline-link" target="_blank">{{
content
}}</gl-link>
</template>
</gl-sprintf>
</template>

View File

@ -88,7 +88,8 @@ module Users
duo_pro_trial_alert: 86, # EE-only
deployment_details_feedback: 87,
duo_chat_ga_alert: 88, # EE-only
board_add_new_column_trigger_popover: 89
board_add_new_column_trigger_popover: 89,
deployment_approvals_empty_state: 90
}
validates :feature_name,

View File

@ -27,24 +27,5 @@ module Groups
def remove_unallowed_params
# overridden in EE
end
# This is a temporary shim to address an issue with
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135959 and should
# be removed when the issue is resolved.
#
# rubocop:disable Style/IfUnlessModifier -- you're entirely wrong
# rubocop:disable Style/NegatedIf -- you're entirely wrong
def invert_emails_disabled_to_emails_enabled
return unless Feature.enabled?(:invert_emails_disabled_to_emails_enabled)
return unless params.key?(:emails_disabled)
if !params[:emails_disabled].nil?
params[:emails_enabled] = !Gitlab::Utils.to_boolean(params[:emails_disabled])
end
params.delete(:emails_disabled)
end
# rubocop:enable Style/IfUnlessModifier
# rubocop:enable Style/NegatedIf
end
end

View File

@ -48,7 +48,6 @@ module Groups
def build_group
remove_unallowed_params
invert_emails_disabled_to_emails_enabled
set_visibility_level

View File

@ -10,8 +10,6 @@ module Groups
reject_parent_id!
remove_unallowed_params
invert_emails_disabled_to_emails_enabled
before_assignment_hook(group, params)
if renaming_group_with_container_registry_images?

View File

@ -21,7 +21,7 @@ module Members
result
rescue ArgumentError, ImportProjectTeamForbiddenError, SeatLimitExceededError => e
ServiceResponse.error(message: e.message, reason: :unprocessable_entity)
ServiceResponse.error(message: e.message, reason: e.class.name.demodulize.underscore.to_sym)
end
private
@ -34,7 +34,7 @@ module Members
if members.is_a?(Array)
members.each { |member| check_member_validity(member) }
else
@result = ServiceResponse.error(message: 'Import failed', reason: :unprocessable_entity)
@result = ServiceResponse.error(message: 'Import failed', reason: :import_failed_error)
end
end

View File

@ -6,4 +6,6 @@
#js-deployment-details{ data: { project_path: @project.full_path,
environment_name: @environment.name,
deployment_iid: @deployment.iid,
graphql_etag_key: @environment.etag_cache_key } }
graphql_etag_key: @environment.etag_cache_key,
protected_environments_available: @project.licensed_feature_available?(:protected_environments).to_s,
protected_environments_settings_path: can?(current_user, :admin_pipeline, @project) && project_settings_ci_cd_path(@project, anchor: 'js-protected-environments-settings') } }

View File

@ -18,8 +18,8 @@
.js-import-project-members-trigger{ data: { classes: 'gl-md-w-auto gl-w-full' } }
.js-import-project-members-modal{ data: { project_id: @project.id,
project_name: @project.name,
reload_page_on_submit: true.to_s,
users_limit_dataset: common_invite_modal_dataset(@project)[:users_limit_dataset] } }
reload_page_on_submit: true.to_s }
.merge(common_invite_modal_dataset(@project).slice(:users_limit_dataset, :add_seats_href)) }
- invite_group_top_margin = 'gl-md-mt-0 gl-mt-3'
- if @project.allowed_to_share_with_group?
.js-invite-group-trigger{ data: { classes: "gl-md-w-auto gl-w-full gl-md-ml-3 #{invite_group_top_margin}", display_text: _('Invite a group') } }

View File

@ -17,6 +17,14 @@
- if @scope == 'projects'
.term
= render 'shared/projects/list', projects: @search_objects, pipeline_status: false
- elsif @scope === 'users'
%table.table.b-table.gl-table.b-table-stacked-md{ role: 'table' }
%thead
%tr
%th= _('User')
%th.text-right= _('Activity')
%tbody
= render_if_exists partial: "search/results/user", collection: @search_objects
- else
= render_if_exists partial: "search/results/#{@scope.singularize}", collection: @search_objects

View File

@ -1,10 +1,20 @@
%ul.content-list
%li
.avatar-cell
= user_avatar(user: user, size: 40, user_name: user.name)
.user-info
= link_to user_path(user) do
.item-title
= simple_search_highlight_and_truncate(user.name, @search_term)
= user_status(user)
.cgray= simple_search_highlight_and_truncate(user.to_reference, @search_term)
%tr
%td{ data: { label: _('User') } }
%div
%div{ class: 'gl-display-inline-flex!' }
= render Pajamas::AvatarComponent.new(user, size: 32, alt: '')
.gl-ml-3{ class: 'gl-text-left!' }
= link_to user_path(user), class: 'gl-text-body' do
.gl-display-inline-block.gl-font-weight-bold= simple_search_highlight_and_truncate(user.name, @search_term)
= user_status(user)
%div{ class: 'gl-text-left!' }= simple_search_highlight_and_truncate(user.to_reference, @search_term)
%td.gl-text-right{ data: { label: _('Activity') } }
%div
%span.gl-font-weight-bold= _('User created:')
= l(user.created_at.to_date, format: :long)
%div
%span.gl-font-weight-bold= _('Last activity:')
- if user.last_activity_on
= l(user.last_activity_on.to_date, format: :long)
- else
= _('Never')

View File

@ -1,9 +0,0 @@
---
name: invert_emails_disabled_to_emails_enabled
feature_issue_url:
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148577
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/454371
milestone: '16.11'
group: group::code review
type: gitlab_com_derisk
default_enabled: false

View File

@ -10,7 +10,7 @@ module Kaminari
# that limit the COUNT query to a configurable value to avoid query timeouts.
# The default limit value is 10,000 records
# rubocop: disable Gitlab/ModuleWithInstanceVariables
def total_count_with_limit(column_name = :all, options = {}) #:nodoc:
def total_count_with_limit(column_name = :all, options = {}) # :nodoc:
return @total_count if defined?(@total_count) && @total_count
# There are some cases that total count can be deduced from loaded records

View File

@ -957,6 +957,10 @@ response to events happening in the registry.
Read more about the container registry notifications configuration options in the
[Docker Registry notifications documentation](https://distribution.github.io/distribution/about/notifications/).
WARNING:
Support for the `threshold` parameter was [deprecated](https://gitlab.com/gitlab-org/container-registry/-/issues/1243)
in GitLab 17.0, and is planned for removal in 18.0. Use `maxretries` instead.
You can configure multiple endpoints for the container registry.
::Tabs
@ -973,7 +977,8 @@ To configure a notification endpoint for a Linux package installation:
'name' => 'test_endpoint',
'url' => 'https://gitlab.example.com/notify',
'timeout' => '500ms',
'threshold' => 5,
'threshold' => 5, # DEPRECATED: use `maxretries` instead.
'maxretries' => 5,
'backoff' => '1s',
'headers' => {
"Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"]
@ -999,7 +1004,8 @@ notifications:
url: https://my.listener.com/event
headers: <http.Header>
timeout: 500
threshold: 5
threshold: 5 # DEPRECATED: use `maxretries` instead.
maxretries: 5
backoff: 1000
```

View File

@ -34414,6 +34414,7 @@ Name of the feature that the callout is for.
| <a id="usercalloutfeaturenameenumci_deprecation_warning_for_types_keyword"></a>`CI_DEPRECATION_WARNING_FOR_TYPES_KEYWORD` | Callout feature name for ci_deprecation_warning_for_types_keyword. |
| <a id="usercalloutfeaturenameenumcloud_licensing_subscription_activation_banner"></a>`CLOUD_LICENSING_SUBSCRIPTION_ACTIVATION_BANNER` | Callout feature name for cloud_licensing_subscription_activation_banner. |
| <a id="usercalloutfeaturenameenumcluster_security_warning"></a>`CLUSTER_SECURITY_WARNING` | Callout feature name for cluster_security_warning. |
| <a id="usercalloutfeaturenameenumdeployment_approvals_empty_state"></a>`DEPLOYMENT_APPROVALS_EMPTY_STATE` | Callout feature name for deployment_approvals_empty_state. |
| <a id="usercalloutfeaturenameenumdeployment_details_feedback"></a>`DEPLOYMENT_DETAILS_FEEDBACK` | Callout feature name for deployment_details_feedback. |
| <a id="usercalloutfeaturenameenumduo_chat_callout"></a>`DUO_CHAT_CALLOUT` | Callout feature name for duo_chat_callout. |
| <a id="usercalloutfeaturenameenumduo_chat_ga_alert"></a>`DUO_CHAT_GA_ALERT` | Callout feature name for duo_chat_ga_alert. |

View File

@ -31,6 +31,23 @@ in your local GitLab Development Kit or GitLab Docker instance.
For a real-life example, refer to
[this failed job](https://gitlab.com/gitlab-org/gitlab/-/jobs/6418619509#L4970).
#### Broken master
When a new required upgrade stop is added (every three or four milestones), it triggers the build of a new PostgreSQL Dump.
In some cases, this might cause the `db:migrate:multi-version-upgrade` job to fail in `master` pipeline.
For example, if new additional tables are seeded, it helps detect migration errors that might have been missed in older dumps without these seeded tables.
Workflow for the [broken master](https://handbook.gitlab.com/handbook/engineering/workflow/#broken-master) case:
1. Identify the root cause MR by searching for the migration that caused the error. For instance, [`db/migrate/20240416123401_add_security_policy_management_project_id_to_security_policies.rb` in failing job](https://gitlab.com/gitlab-org/gitlab-foss/-/jobs/6671417979#L1547)
- Debug locally if needed as described in the [Database reconfigure failures](#database-reconfigure-failures)
1. Reach out to the relevant team that introduced the migration to discuss whether the MR should be reverted or if a fix will be worked on
- If the team isn't available, post about it in the `#database` Slack channel
1. While a fix or revert is being worked on, `master` pipeline can be unblocked by disabling the job temporarily by setting `DISABLE_DB_MULTI_VERSION_UPGRADE=true` in [CI/CD Settings page](https://gitlab.com/gitlab-org/gitlab/-/settings/ci_cd)
- When disabling the job, announce it in the `#master-broken` Slack channel
1. Add a note to [job stability tracking issue#458402](https://gitlab.com/gitlab-org/gitlab/-/issues/458402)
1. Reinstate the job by removing `DISABLE_DB_MULTI_VERSION_UPGRADE` from CI/CD Settings
### Database import failures
If job is failing on setup stage prior to `gitlab:db:configure`

View File

@ -52,7 +52,7 @@ When working with directories and files:
1. When creating or renaming a file or directory and it has more than one word
in its name, use underscores (`_`) instead of spaces or dashes. For example,
proper naming would be `import_project/import_from_github.md`. This applies
to both [image files](../styleguide/index.md#images) and Markdown files.
to both [image files](../styleguide/index.md#illustrations) and Markdown files.
1. Do not upload video files to the product repositories.
[Link or embed videos](../styleguide/index.md#videos) instead.
1. In the `doc/user/` directory:

View File

@ -96,7 +96,7 @@ Also, keep the following guidance in mind:
use **custom settings for project integrations**.
- Format [dates and times](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/date-time-terms)
consistently and for an international audience.
- Use [images](#images), including screenshots, sparingly.
- Use [illustrations](#illustrations), including screenshots, sparingly.
- For [UI text](#ui-text), allow for up to 30% expansion and contraction in translation.
To see how much a string expands or contracts in another language, paste the string
into [Google Translate](https://translate.google.com/) and review the results.
@ -568,7 +568,7 @@ indentation as the list item. You can do this with:
- [Code blocks](#code-blocks)
- [Blockquotes](#blockquotes)
- [Alert boxes](#alert-boxes)
- [Images](#images)
- [Illustrations](#illustrations)
- [Tabs](#tabs)
Nested items should always align with the first character of the list
@ -1121,21 +1121,71 @@ To describe multiple fields, use unordered list items:
- **Branch name** must be a regular expression.
- **User** must be a user with at least the **Maintainer** role.
## Images
## Illustrations
Images, including screenshots, can help a reader better understand a concept.
However, they should be used sparingly because:
Use illustrations only to supplement text, not replace it.
- They tend to become out-of-date.
- They are difficult and expensive to localize.
- They cannot be read by screen readers.
When needed, use images to help the reader understand:
Illustrations can help the reader understand:
- A concept.
- Where they are in a complicated process.
- How they should interact with the application.
### Capture the image
Use illustrations sparingly because:
- They tend to become out-of-date.
- They are difficult and expensive to localize.
- Their content cannot be read by screen readers.
Types of illustrations used in GitLab documentation are:
- Diagram. Use a diagram to illustrate a process or the relationship between entities, for example.
- Screenshot. Use a screenshot when you need to show a portion of the GitLab user interface.
Use a diagram instead of a screenshot when possible because:
- A diagram's file size is usually much smaller than that of a screenshot.
- A screenshot often needs to be compressed, which generally reduces the image's quality.
- A diagram in SVG format can be displayed at any size without affecting the image's quality.
### Diagram
Use a diagram to illustrate a process or the relationship between entities, for example.
Use [Mermaid](https://mermaid.js.org/#/) to create a diagram. This method has several advantages
over a static image format (screenshot):
- The Mermaid format is easier to maintain because:
- Their definition is stored as a code block in the documentation's Markdown source.
- The diagram is rendered dynamically at runtime.
- Text content that may change over time, such as feature names, can be found using text search
tools and edited.
- The diagram is rendered as an scalable image, better suited to various output devices and sizes.
#### Create a diagram
To create a diagram:
1. Use the [Mermaid Live Editor](https://mermaid.live/) to create the diagram.
1. Copy the content of the **Code** pane into a `mermaid` code block in the Markdown file. For more
details, see [Mermaid](../../../user/markdown.md#mermaid).
1. To improve accessibility of diagrams, add a title and description. Add these lines on the next
line after declaring the type of diagram, like `flowchart` or `sequenceDiagram`:
```yaml
accTitle: your diagram title here
accDescr: describe what your diagram does in a single sentence, with no line breaks.
```
The Mermaid diagram syntax can be difficult to learn. To make this a little easier, see the Mermaid
[Beginner's Guide](https://mermaid.js.org/intro/getting-started.html) and the examples on the
Mermaid site.
### Screenshot
Use a screenshot when you need to show a portion of the GitLab user interface.
#### Capture the screenshot
When you take screenshots:
@ -1155,7 +1205,7 @@ When you take screenshots:
a documentation page for a consistent reading experience. Ensure your navigation theme
is **Indigo** and the syntax highlighting theme is **Light**. These are the default preferences.
### Add callouts
#### Add callouts
If you need to emphasize an area in a screenshot, use an arrow.
@ -1166,7 +1216,7 @@ If you need to emphasize an area in a screenshot, use an arrow.
![callout example](img/callouts.png)
### Save the image
#### Save the image
- Resize any wide or tall screenshots if needed, but make sure the screenshot is
still clear after being resized and compressed.
@ -1184,23 +1234,9 @@ If you need to emphasize an area in a screenshot, use an arrow.
the `.md` document that you're working on is located.
- Consider using PNG images instead of JPEG.
- Compress GIFs with <https://ezgif.com/optimize> or similar tool.
- Images should be used (only when necessary) to illustrate the description
of a process, not to replace it.
- See also how to link and embed [videos](#videos) to illustrate the documentation.
### Add the image link to content
The Markdown code for including an image in a document is:
`![Image description which will be the alt tag](img/document_image_title_vX_Y.png)`
The image description is the alt text for the rendered image on the
documentation site. For accessibility and SEO, use [descriptions](https://webaim.org/techniques/alttext/)
that:
- Are accurate, succinct, and unique.
- Don't use **image of** or **graphic of** to describe the image.
### Compress images
#### Compress images
You should always compress any new images you add to the documentation. One
known tool is [`pngquant`](https://pngquant.org/), which is cross-platform and
@ -1239,7 +1275,7 @@ copy of `https://gitlab.com/gitlab-org/gitlab`, run in a terminal:
bin/pngquant compress doc/user/img
```
### Animated images
#### Animated images
Avoid using animated images (such as animated GIFs). They can be distracting
and annoying for users.
@ -1250,7 +1286,18 @@ include a visual representation to help readers understand it, you can:
- Use a static image (screenshot) and if necessary, add callouts to emphasize an area of the screen.
- Create a short video of the interaction and link to it.
### Automatic screenshot generator
#### Add the image link to content
The Markdown code for including an image in a document is:
`![Image description, used for alt tag](img/document_image_title_vX_Y.png)`
The image description is the alt text for the rendered image on the
documentation site. For accessibility and SEO, use [descriptions](https://webaim.org/techniques/alttext/)
that are accurate, succinct, and unique.
Don't use **image of** or **graphic of** to describe the image.
#### Automatic screenshot generator
You can use an automatic screenshot generator to take and compress screenshots.
@ -1262,7 +1309,7 @@ You can use an automatic screenshot generator to take and compress screenshots.
1. Identify the location of the screenshots, based on the `gitlab/doc` location defined by the `it` parameter in your script.
1. Commit the newly created screenshots.
#### Extending the tool
##### Extending the tool
To add an additional screenshot generator:
@ -1289,7 +1336,7 @@ To add an additional screenshot generator:
You can take a screenshot of a page with `visit <path>`.
To avoid blank screenshots, use `expect` to wait for the content to load.
##### Single-element screenshots
###### Single-element screenshots
You can take a screenshot of a single element.

View File

@ -94,7 +94,7 @@ do not use `Tutorial` in the title.
## Screenshots
You can include screenshots in a tutorial to illustrate important steps in the process.
In the core product documentation, you should [use screenshots sparingly](../styleguide/index.md#images).
In the core product documentation, you should [use illustrations sparingly](../styleguide/index.md#illustrations).
However, in tutorials, screenshots can help users understand where they are in a complex process.
Try to balance the number of screenshots in the tutorial so they don't disrupt

View File

@ -204,7 +204,6 @@ possible selectors include:
- A semantic attribute like `name` (also verifies that `name` was setup properly)
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
optionally combined with [`shallowMountExtended` or `mountExtended`](#shallowmountextended-and-mountextended)
- a Vue `ref` (if using `@vue/test-utils`)
```javascript
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'
@ -223,9 +222,9 @@ it('exists', () => {
wrapper.find('input[name=foo]');
wrapper.find('[data-testid="my-foo-id"]');
wrapper.findByTestId('my-foo-id'); // with shallowMountExtended or mountExtended check below
wrapper.find({ ref: 'foo'});
// Bad
wrapper.find({ ref: 'foo'});
wrapper.find('.js-foo');
wrapper.find('.btn-primary');
});
@ -234,6 +233,7 @@ it('exists', () => {
You should use `kebab-case` for `data-testid` attribute.
It is not recommended that you add `.js-*` classes just for testing purposes. Only do this if there are no other feasible options available.
Avoid using Vue template refs to query DOM elements in tests because they're an implementation detail of the component, not a public API.
### Querying for child components

View File

@ -0,0 +1,131 @@
---
stage: Data Stores
group: Tenant Scale
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Get started organizing work with projects
In GitLab, the data related to a specific development effort goes in a project.
The project serves as a central hub for collaboration, version control, and project management.
Projects provide the environment for managing and collaborating
on software development projects, from planning and coding to testing and deployment.
Project creation and maintenance is part of a larger workflow:
![Workflow](img/get_started_projects_v16_11.png)
## Step 1: Create a project
Start by creating a new project in GitLab to contain your codebase,
documentation, and related resources.
A project contains a repository. A repository contains all the files,
directories, and data related to your work.
Set the appropriate visibility level (public, internal, or private) for your project,
based on your project's security and collaboration requirements.
Configure project settings, like merge request approvals, issue tracking,
and CI/CD pipelines, to align with your development workflow.
Use description templates to maintain consistency and provide essential information
when creating issues, merge requests, or other project entities.
For more information, see:
- [Create a project](../project/index.md)
- [Manage projects](../project/working_with_projects.md)
- [Project visibility](../public_access.md)
- [Project settings](../project/settings/index.md)
- [Description templates](../project/description_templates.md)
## Step 2: Secure and control access to projects
To grant specific access rights to automated tools or external systems,
helping ensure secure integration with your GitLab projects, generate project access tokens.
If you want to securely deploy your project to external systems,
create deploy keys. These keys can grant read-only access to your repositories.
And finally, to provide temporary and limited access to
your project's repository and registry, create deploy tokens, which
help enable secure deployments and automation.
For more information, see:
- [Project access tokens](../project/settings/project_access_tokens.md)
- [Deploy keys](../project/deploy_keys/index.md)
- [Deploy tokens](../project/deploy_tokens/index.md)
## Step 3: Collaborate and share projects
You can invite multiple projects to a group, sometimes called
`sharing a project with a group`. Each project has its own repository,
issues, merge requests, and other features.
When you have multiple projects in the same group, your team members can collaborate
on specific projects while still maintaining
a high-level overview of all the work being done in the group.
To further refine who has access to which projects, you can
add subgroups to your group.
For more information, see:
- [Share projects](../project/members/share_project_with_groups.md)
## Step 4: Enhance project discoverability and recognition
To create a consistent and easily recognizable naming scheme for your projects,
use reserved project and group names. Consistent names can help make projects
more discoverable.
Use the search functionality to quickly find specific projects,
issues, merge requests, or code snippets across your GitLab instance.
Another way to make your projects more discoverable is to add badges
to your project's `README` file. Badges can display important information,
like build status, test coverage, or version number. They provide a
quick overview of your project's health and status.
And finally, topics are labels that you can assign to projects
to help you organize and find them. You can assign a topic to several projects.
For more information, see:
- [Reserved project and group names](../reserved_names.md)
- [Search](../search/index.md)
- [Badges](../project/badges.md)
- [Project topics](../project/project_topics.md)
## Step 5: Boost development efficiency and maintain code quality
Use the code intelligence features, like code navigation,
hover information, and auto-completion, to enhance your productivity and
maintain a high-quality codebase. Code intelligence is a range of tools
that help you efficiently explore, analyze, and maintain your codebase.
To quickly locate and go to specific files in your project,
use the file finder.
For more information, see:
- [Code intelligence](../project/code_intelligence.md)
- [File finder](../project/repository/file_finder.md)
## Step 6: Migrate projects into GitLab
When necessary, use file exports to migrate projects to GitLab.
You can migrate from other version control systems or GitLab instances.
When you migrate a frequently accessed repository to GitLab, you can continue to
access it by its original name by using a project alias.
On GitLab.com, you can transfer a project from one namespace to another,
which is essentially moving it so that another group or team can have
access or ownership.
For more information, see:
- [Migrate projects by using file exports](../project/import/index.md)
- [Project aliases](../project/working_with_projects.md#project-aliases)
- [Transfer a project to another namespace](../project/settings/migrate_projects.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -26,7 +26,7 @@ It can assist various tasks of your daily work with the AI-generated content.
> - GitLab self-managed users with a Premium or Ultimate subscription.
> - GitLab Dedicated users.
>
> Eventually a subscription add-on will be required for continued access to GitLab Duo Chat.
> Eventually a subscription add-on will be required for continued access to GitLab Duo Chat.
> Learn more about [Duo Pro and Duo Enterprise pricing](https://about.gitlab.com/gitlab-duo/#pricing).
Here are examples of common use cases:
@ -168,7 +168,7 @@ This feature is available in VS Code, JetBrains IDEs, and the Web IDE only.
`/tests` is a special command to generate a testing suggestion for the selected code in your editor.
You can also add additional instructions to be considered, for example: `/tests using the Boost.Test framework`
See [Use GitLab Duo Chat in the VS Code](#use-gitlab-duo-chat-in-vs-code) for more information.
See [Use GitLab Duo Chat in VS Code](#use-gitlab-duo-chat-in-vs-code) for more information.
- Use a specific test framework, for example `/tests using the Boost.test framework` (C++) or `/tests using Jest` (JavaScript).
- Focus on extreme test cases, for example `/tests focus on extreme cases, force regression testing`.
@ -431,9 +431,13 @@ To use GitLab Duo Chat in the GitLab Duo plugin for JetBrains IDEs:
1. In a JetBrains IDE, open a project.
1. Open Chat by using one of the following methods:
- On the right tool window bar, select **GitLab Duo Chat**.
- In the file that you have open in the editor, select some code.
- Use a keyboard shortcut: <kbd>ALT</kbd> + <kbd>d</kbd> on Windows and Linux, or
<kbd>Option</kbd> + <kbd>d</kbd> on macOS.
- In the file that you have open in the editor:
1. Optional. Select some code.
1. Right-click and select **GitLab Duo Chat**.
1. Select **Explain Code** or **Generate Tests** or **Refactor Code**.
1. Select **Open Chat Window**.
1. Select **Explain Code**, **Generate Tests**, or **Refactor Code**.
- Add keyboard or mouse shortcuts for each action under **Keymap** in the **Settings**.
1. In the message box, enter your question and press **Enter** or select **Send**.

View File

@ -10,8 +10,6 @@ DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
> - Searching by image repository name was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31322) in GitLab 13.0.
You can use the integrated container registry to store container images for each GitLab project.
To enable the container registry for your GitLab instance, see the [administrator documentation](../../../administration/packages/container_registry.md).

View File

@ -99,16 +99,3 @@ Provide feedback on this experimental feature in [issue 408994](https://gitlab.c
- Contents of the file
- The filename
<!--- start_remove The following content will be removed on remove_date: '2024-04-12' -->
## Generate suggested tests in merge requests
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10366) in GitLab 16.0 as an [Experiment](../../../policy/experiment-beta-support.md#experiment).
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141086) to GitLab Duo Chat in GitLab 16.8.
This feature was [moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141086)
into GitLab Duo Chat in GitLab 16.8. Find more information in
[Write tests in the IDE](../../gitlab_duo_chat.md#write-tests-in-the-ide).
<!--- end_remove -->

View File

@ -59,20 +59,32 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
# This is a separate method so that EE can extend its behaviour, without
# having to modify this code directly.
#
def create_group
# This is a separate method so that EE can extend its behaviour, without
# having to modify this code directly.
::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute
::Groups::CreateService
.new(current_user, translate_params_for_compatibility)
.execute
end
# This is a separate method so that EE can extend its behaviour, without
# having to modify this code directly.
#
def update_group(group)
# This is a separate method so that EE can extend its behaviour, without
# having to modify this code directly.
::Groups::UpdateService
.new(group, current_user, declared_params(include_missing: false))
.new(group, current_user, translate_params_for_compatibility)
.execute
end
def translate_params_for_compatibility
temp_params = declared_params(include_missing: false)
temp_params[:emails_enabled] = !temp_params.delete(:emails_disabled) if temp_params.key?(:emails_disabled)
temp_params
end
def find_group_projects(params, finder_options)
group = find_group!(params[:id])

View File

@ -833,8 +833,8 @@ module API
if result.success?
{ status: result.status }
elsif result.reason == :unprocessable_entity
render_api_error!(result.message, result.reason)
elsif result.reason
render_structured_api_error!({ 'message' => result.message, 'reason' => result.reason }, :unprocessable_entity)
else
{ status: result.status, message: result.message, total_members_count: result.payload[:total_members_count] }
end

View File

@ -7,14 +7,14 @@ module Gitlab
module Validators
class SchemaValidator
SUPPORTED_VERSIONS = {
cluster_image_scanning: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0],
container_scanning: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0],
coverage_fuzzing: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0],
dast: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0],
api_fuzzing: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0],
dependency_scanning: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0],
sast: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0],
secret_detection: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0]
cluster_image_scanning: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1],
container_scanning: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1],
coverage_fuzzing: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1],
dast: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1],
api_fuzzing: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1],
dependency_scanning: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1],
sast: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1],
secret_detection: %w[15.0.0 15.0.1 15.0.2 15.0.4 15.0.5 15.0.6 15.0.7 15.1.0 15.1.1]
}.freeze
VERSIONS_TO_REMOVE_IN_17_0 = %w[].freeze

View File

@ -0,0 +1,998 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/container-scanning-report-format.json",
"title": "Report format for GitLab Container Scanning",
"description": "This schema provides the the report format for Container Scanning (https://docs.gitlab.com/ee/user/application_security/container_scanning).",
"definitions": {
"detail_type": {
"oneOf": [
{
"$ref": "#/definitions/named_list"
},
{
"$ref": "#/definitions/list"
},
{
"$ref": "#/definitions/table"
},
{
"$ref": "#/definitions/text"
},
{
"$ref": "#/definitions/url"
},
{
"$ref": "#/definitions/code"
},
{
"$ref": "#/definitions/value"
},
{
"$ref": "#/definitions/diff"
},
{
"$ref": "#/definitions/markdown"
},
{
"$ref": "#/definitions/commit"
},
{
"$ref": "#/definitions/file_location"
},
{
"$ref": "#/definitions/module_location"
}
]
},
"text_value": {
"type": "string"
},
"named_field": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/text_value",
"type": "string",
"minLength": 1
},
"description": {
"$ref": "#/definitions/text_value"
}
}
},
"named_list": {
"type": "object",
"description": "An object with named and typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "named-list"
},
"items": {
"type": "object",
"patternProperties": {
"^.*$": {
"allOf": [
{
"$ref": "#/definitions/named_field"
},
{
"$ref": "#/definitions/detail_type"
}
]
}
}
}
}
},
"list": {
"type": "object",
"description": "A list of typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "list"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
},
"table": {
"type": "object",
"description": "A table of typed fields",
"required": [
"type",
"rows"
],
"properties": {
"type": {
"const": "table"
},
"header": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
}
},
"text": {
"type": "object",
"description": "Raw text",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "text"
},
"value": {
"$ref": "#/definitions/text_value"
}
}
},
"url": {
"type": "object",
"description": "A single URL",
"required": [
"type",
"href"
],
"properties": {
"type": {
"const": "url"
},
"text": {
"$ref": "#/definitions/text_value"
},
"href": {
"type": "string",
"minLength": 1,
"examples": [
"http://mysite.com"
]
}
}
},
"code": {
"type": "object",
"description": "A codeblock",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "code"
},
"value": {
"type": "string"
},
"lang": {
"type": "string",
"description": "A programming language"
}
}
},
"value": {
"type": "object",
"description": "A field that can store a range of types of value",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "value"
},
"value": {
"type": [
"number",
"string",
"boolean"
]
}
}
},
"diff": {
"type": "object",
"description": "A diff",
"required": [
"type",
"before",
"after"
],
"properties": {
"type": {
"const": "diff"
},
"before": {
"type": "string"
},
"after": {
"type": "string"
}
}
},
"markdown": {
"type": "object",
"description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "markdown"
},
"value": {
"$ref": "#/definitions/text_value",
"examples": [
"Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
]
}
}
},
"commit": {
"type": "object",
"description": "A commit/tag/branch within the GitLab project",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "commit"
},
"value": {
"type": "string",
"description": "The commit SHA",
"minLength": 1
}
}
},
"file_location": {
"type": "object",
"description": "A location within a file in the project",
"required": [
"type",
"file_name",
"line_start"
],
"properties": {
"type": {
"const": "file-location"
},
"file_name": {
"type": "string",
"minLength": 1
},
"line_start": {
"type": "integer"
},
"line_end": {
"type": "integer"
}
}
},
"module_location": {
"type": "object",
"description": "A location within a binary module of the form module+relative_offset",
"required": [
"type",
"module_name",
"offset"
],
"properties": {
"type": {
"const": "module-location"
},
"module_name": {
"type": "string",
"minLength": 1,
"examples": [
"compiled_binary"
]
},
"offset": {
"type": "integer",
"examples": [
100
]
}
}
}
},
"self": {
"version": "15.1.1"
},
"type": "object",
"required": [
"scan",
"version",
"vulnerabilities"
],
"additionalProperties": true,
"properties": {
"scan": {
"type": "object",
"required": [
"analyzer",
"end_time",
"scanner",
"start_time",
"status",
"type"
],
"properties": {
"end_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-01-28T03:26:02"
]
},
"messages": {
"type": "array",
"items": {
"type": "object",
"description": "Communication intended for the initiator of a scan.",
"required": [
"level",
"value"
],
"properties": {
"level": {
"type": "string",
"description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
"enum": [
"info",
"warn",
"fatal"
],
"examples": [
"info"
]
},
"value": {
"type": "string",
"description": "The message to communicate.",
"minLength": 1,
"examples": [
"Permission denied, scanning aborted"
]
}
}
}
},
"options": {
"type": "array",
"items": {
"type": "object",
"description": "A configuration option used for this scan.",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"description": "The configuration option name.",
"maxLength": 255,
"minLength": 1,
"examples": [
"DAST_FF_ENABLE_BAS",
"DOCKER_TLS_CERTDIR",
"DS_MAX_DEPTH",
"SECURE_LOG_LEVEL"
]
},
"source": {
"type": "string",
"description": "The source of this option.",
"enum": [
"argument",
"file",
"env_variable",
"other"
]
},
"value": {
"type": [
"boolean",
"integer",
"null",
"string"
],
"description": "The value used for this scan.",
"examples": [
true,
2,
null,
"fatal",
""
]
}
}
}
},
"analyzer": {
"type": "object",
"description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the analyzer.",
"minLength": 1,
"examples": [
"gitlab-dast"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the analyzer, not required to be unique.",
"minLength": 1,
"examples": [
"GitLab DAST"
]
},
"url": {
"type": "string",
"pattern": "^https?://.+",
"description": "A link to more information about the analyzer.",
"examples": [
"https://docs.gitlab.com/ee/user/application_security/dast"
]
},
"vendor": {
"description": "The vendor/maintainer of the analyzer.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
},
"version": {
"type": "string",
"description": "The version of the analyzer.",
"minLength": 1,
"examples": [
"1.0.2"
]
}
}
},
"scanner": {
"type": "object",
"description": "Object defining the scanner used to perform the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the scanner.",
"minLength": 1,
"examples": [
"my-sast-scanner"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the scanner, not required to be unique.",
"minLength": 1,
"examples": [
"My SAST Scanner"
]
},
"url": {
"type": "string",
"description": "A link to more information about the scanner.",
"examples": [
"https://scanner.url"
]
},
"version": {
"type": "string",
"description": "The version of the scanner.",
"minLength": 1,
"examples": [
"1.0.2"
]
},
"vendor": {
"description": "The vendor/maintainer of the scanner.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
}
}
},
"start_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-02-14T16:01:59"
]
},
"status": {
"type": "string",
"description": "Result of the scan.",
"enum": [
"success",
"failure"
]
},
"type": {
"type": "string",
"description": "Type of the scan.",
"enum": [
"container_scanning",
"container_scanning_for_registry"
]
},
"primary_identifiers": {
"type": "array",
"description": "An unordered array containing an exhaustive list of primary identifiers for which the analyzer may return results",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
}
}
},
"schema": {
"type": "string",
"description": "URI pointing to the validating security report schema.",
"pattern": "^https?://.+"
},
"version": {
"type": "string",
"description": "The version of the schema to which the JSON report conforms.",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"vulnerabilities": {
"type": "array",
"description": "Array of vulnerability objects.",
"items": {
"type": "object",
"description": "Describes the vulnerability using GitLab Flavored Markdown",
"required": [
"id",
"identifiers",
"location"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
},
"name": {
"type": "string",
"maxLength": 255,
"description": "The name of the vulnerability. This must not include the finding's specific information."
},
"description": {
"type": "string",
"maxLength": 1048576,
"description": "A long text section describing the vulnerability more fully."
},
"severity": {
"type": "string",
"description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
"enum": [
"Info",
"Unknown",
"Low",
"Medium",
"High",
"Critical"
]
},
"solution": {
"type": "string",
"maxLength": 7000,
"description": "Explanation of how to fix the vulnerability."
},
"identifiers": {
"type": "array",
"minItems": 1,
"description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
},
"cvss_vectors": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"description": "An ordered array of CVSS vectors, each issued by a vendor to rate the vulnerability. The first item in the array is used as the primary CVSS vector, and is used to filter and sort the vulnerability.",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 16,
"maxLength": 128,
"pattern": "^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$"
}
},
"required": [
"vendor",
"vector"
]
},
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 32,
"maxLength": 128,
"pattern": "^CVSS:3[.][01]/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
}
},
"required": [
"vendor",
"vector"
]
}
]
}
},
"links": {
"type": "array",
"description": "An array of references to external documentation or articles that describe the vulnerability.",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the vulnerability details link."
},
"url": {
"type": "string",
"description": "URL of the vulnerability details document.",
"pattern": "^(https?|ftp)://.+"
}
}
}
},
"details": {
"$ref": "#/definitions/named_list/properties/items"
},
"tracking": {
"type": "object",
"description": "Describes how this vulnerability should be tracked as the project changes.",
"oneOf": [
{
"description": "Declares that a series of items should be tracked using source-specific tracking methods.",
"required": [
"items"
],
"properties": {
"type": {
"const": "source"
},
"items": {
"type": "array",
"items": {
"description": "An item that should be tracked using source-specific tracking methods.",
"type": "object",
"required": [
"signatures"
],
"properties": {
"file": {
"type": "string",
"description": "Path to the file where the vulnerability is located."
},
"start_line": {
"type": "number",
"description": "The first line of the file that includes the vulnerability."
},
"end_line": {
"type": "number",
"description": "The last line of the file that includes the vulnerability."
},
"signatures": {
"type": "array",
"description": "An array of calculated tracking signatures for this tracking item.",
"minItems": 1,
"items": {
"description": "A calculated tracking signature value and metadata.",
"type": "object",
"required": [
"algorithm",
"value"
],
"properties": {
"algorithm": {
"type": "string",
"description": "The algorithm used to generate the signature."
},
"value": {
"type": "string",
"description": "The result of this signature algorithm."
}
}
}
}
}
}
}
}
}
],
"properties": {
"type": {
"type": "string",
"description": "Each tracking type must declare its own type."
}
}
},
"flags": {
"description": "Flags that can be attached to vulnerabilities.",
"type": "array",
"items": {
"type": "object",
"description": "Informational flags identified and assigned to a vulnerability.",
"required": [
"type",
"origin",
"description"
],
"properties": {
"type": {
"type": "string",
"minLength": 1,
"description": "Result of the scan.",
"enum": [
"flagged-as-likely-false-positive"
]
},
"origin": {
"minLength": 1,
"description": "Tool that issued the flag.",
"type": "string"
},
"description": {
"minLength": 1,
"description": "What the flag is about.",
"type": "string"
}
}
}
},
"location": {
"type": "object",
"description": "Identifies the vulnerability's location.",
"required": [
"dependency",
"operating_system",
"image"
],
"properties": {
"dependency": {
"type": "object",
"description": "Describes the dependency of a project where the vulnerability is located.",
"required": [
"package",
"version"
],
"properties": {
"package": {
"type": "object",
"description": "Provides information on the package where the vulnerability is located.",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the package where the vulnerability is located."
}
}
},
"version": {
"type": "string",
"description": "Version of the vulnerable package."
},
"direct": {
"type": "boolean",
"description": "Tells whether this is a direct, top-level dependency of the scanned project."
}
}
},
"operating_system": {
"type": "string",
"minLength": 1,
"description": "The operating system that contains the vulnerable package."
},
"image": {
"type": "string",
"minLength": 1,
"description": "The analyzed Docker image."
},
"default_branch_image": {
"type": "string",
"maxLength": 255,
"description": "The name of the image on the default branch."
}
}
}
}
}
},
"remediations": {
"type": "array",
"description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
"items": {
"type": "object",
"required": [
"fixes",
"summary",
"diff"
],
"properties": {
"fixes": {
"type": "array",
"description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
}
}
}
},
"summary": {
"type": "string",
"minLength": 1,
"description": "An overview of how the vulnerabilities were fixed."
},
"diff": {
"type": "string",
"minLength": 1,
"description": "A base64-encoded remediation code diff, compatible with git apply."
}
}
}
}
}
}

View File

@ -0,0 +1,975 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/coverage-fuzzing-report-format.json",
"title": "Report format for GitLab Fuzz Testing",
"description": "This schema provides the report format for Coverage Guided Fuzz Testing (https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing).",
"definitions": {
"detail_type": {
"oneOf": [
{
"$ref": "#/definitions/named_list"
},
{
"$ref": "#/definitions/list"
},
{
"$ref": "#/definitions/table"
},
{
"$ref": "#/definitions/text"
},
{
"$ref": "#/definitions/url"
},
{
"$ref": "#/definitions/code"
},
{
"$ref": "#/definitions/value"
},
{
"$ref": "#/definitions/diff"
},
{
"$ref": "#/definitions/markdown"
},
{
"$ref": "#/definitions/commit"
},
{
"$ref": "#/definitions/file_location"
},
{
"$ref": "#/definitions/module_location"
}
]
},
"text_value": {
"type": "string"
},
"named_field": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/text_value",
"type": "string",
"minLength": 1
},
"description": {
"$ref": "#/definitions/text_value"
}
}
},
"named_list": {
"type": "object",
"description": "An object with named and typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "named-list"
},
"items": {
"type": "object",
"patternProperties": {
"^.*$": {
"allOf": [
{
"$ref": "#/definitions/named_field"
},
{
"$ref": "#/definitions/detail_type"
}
]
}
}
}
}
},
"list": {
"type": "object",
"description": "A list of typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "list"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
},
"table": {
"type": "object",
"description": "A table of typed fields",
"required": [
"type",
"rows"
],
"properties": {
"type": {
"const": "table"
},
"header": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
}
},
"text": {
"type": "object",
"description": "Raw text",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "text"
},
"value": {
"$ref": "#/definitions/text_value"
}
}
},
"url": {
"type": "object",
"description": "A single URL",
"required": [
"type",
"href"
],
"properties": {
"type": {
"const": "url"
},
"text": {
"$ref": "#/definitions/text_value"
},
"href": {
"type": "string",
"minLength": 1,
"examples": [
"http://mysite.com"
]
}
}
},
"code": {
"type": "object",
"description": "A codeblock",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "code"
},
"value": {
"type": "string"
},
"lang": {
"type": "string",
"description": "A programming language"
}
}
},
"value": {
"type": "object",
"description": "A field that can store a range of types of value",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "value"
},
"value": {
"type": [
"number",
"string",
"boolean"
]
}
}
},
"diff": {
"type": "object",
"description": "A diff",
"required": [
"type",
"before",
"after"
],
"properties": {
"type": {
"const": "diff"
},
"before": {
"type": "string"
},
"after": {
"type": "string"
}
}
},
"markdown": {
"type": "object",
"description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "markdown"
},
"value": {
"$ref": "#/definitions/text_value",
"examples": [
"Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
]
}
}
},
"commit": {
"type": "object",
"description": "A commit/tag/branch within the GitLab project",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "commit"
},
"value": {
"type": "string",
"description": "The commit SHA",
"minLength": 1
}
}
},
"file_location": {
"type": "object",
"description": "A location within a file in the project",
"required": [
"type",
"file_name",
"line_start"
],
"properties": {
"type": {
"const": "file-location"
},
"file_name": {
"type": "string",
"minLength": 1
},
"line_start": {
"type": "integer"
},
"line_end": {
"type": "integer"
}
}
},
"module_location": {
"type": "object",
"description": "A location within a binary module of the form module+relative_offset",
"required": [
"type",
"module_name",
"offset"
],
"properties": {
"type": {
"const": "module-location"
},
"module_name": {
"type": "string",
"minLength": 1,
"examples": [
"compiled_binary"
]
},
"offset": {
"type": "integer",
"examples": [
100
]
}
}
}
},
"self": {
"version": "15.1.1"
},
"type": "object",
"required": [
"scan",
"version",
"vulnerabilities"
],
"additionalProperties": true,
"properties": {
"scan": {
"type": "object",
"required": [
"analyzer",
"end_time",
"scanner",
"start_time",
"status",
"type"
],
"properties": {
"end_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-01-28T03:26:02"
]
},
"messages": {
"type": "array",
"items": {
"type": "object",
"description": "Communication intended for the initiator of a scan.",
"required": [
"level",
"value"
],
"properties": {
"level": {
"type": "string",
"description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
"enum": [
"info",
"warn",
"fatal"
],
"examples": [
"info"
]
},
"value": {
"type": "string",
"description": "The message to communicate.",
"minLength": 1,
"examples": [
"Permission denied, scanning aborted"
]
}
}
}
},
"options": {
"type": "array",
"items": {
"type": "object",
"description": "A configuration option used for this scan.",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"description": "The configuration option name.",
"maxLength": 255,
"minLength": 1,
"examples": [
"DAST_FF_ENABLE_BAS",
"DOCKER_TLS_CERTDIR",
"DS_MAX_DEPTH",
"SECURE_LOG_LEVEL"
]
},
"source": {
"type": "string",
"description": "The source of this option.",
"enum": [
"argument",
"file",
"env_variable",
"other"
]
},
"value": {
"type": [
"boolean",
"integer",
"null",
"string"
],
"description": "The value used for this scan.",
"examples": [
true,
2,
null,
"fatal",
""
]
}
}
}
},
"analyzer": {
"type": "object",
"description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the analyzer.",
"minLength": 1,
"examples": [
"gitlab-dast"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the analyzer, not required to be unique.",
"minLength": 1,
"examples": [
"GitLab DAST"
]
},
"url": {
"type": "string",
"pattern": "^https?://.+",
"description": "A link to more information about the analyzer.",
"examples": [
"https://docs.gitlab.com/ee/user/application_security/dast"
]
},
"vendor": {
"description": "The vendor/maintainer of the analyzer.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
},
"version": {
"type": "string",
"description": "The version of the analyzer.",
"minLength": 1,
"examples": [
"1.0.2"
]
}
}
},
"scanner": {
"type": "object",
"description": "Object defining the scanner used to perform the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the scanner.",
"minLength": 1,
"examples": [
"my-sast-scanner"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the scanner, not required to be unique.",
"minLength": 1,
"examples": [
"My SAST Scanner"
]
},
"url": {
"type": "string",
"description": "A link to more information about the scanner.",
"examples": [
"https://scanner.url"
]
},
"version": {
"type": "string",
"description": "The version of the scanner.",
"minLength": 1,
"examples": [
"1.0.2"
]
},
"vendor": {
"description": "The vendor/maintainer of the scanner.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
}
}
},
"start_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-02-14T16:01:59"
]
},
"status": {
"type": "string",
"description": "Result of the scan.",
"enum": [
"success",
"failure"
]
},
"type": {
"type": "string",
"description": "Type of the scan.",
"enum": [
"coverage_fuzzing"
]
},
"primary_identifiers": {
"type": "array",
"description": "An unordered array containing an exhaustive list of primary identifiers for which the analyzer may return results",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
}
}
},
"schema": {
"type": "string",
"description": "URI pointing to the validating security report schema.",
"pattern": "^https?://.+"
},
"version": {
"type": "string",
"description": "The version of the schema to which the JSON report conforms.",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"vulnerabilities": {
"type": "array",
"description": "Array of vulnerability objects.",
"items": {
"type": "object",
"description": "Describes the vulnerability using GitLab Flavored Markdown",
"required": [
"id",
"identifiers",
"location"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
},
"name": {
"type": "string",
"maxLength": 255,
"description": "The name of the vulnerability. This must not include the finding's specific information."
},
"description": {
"type": "string",
"maxLength": 1048576,
"description": "A long text section describing the vulnerability more fully."
},
"severity": {
"type": "string",
"description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
"enum": [
"Info",
"Unknown",
"Low",
"Medium",
"High",
"Critical"
]
},
"solution": {
"type": "string",
"maxLength": 7000,
"description": "Explanation of how to fix the vulnerability."
},
"identifiers": {
"type": "array",
"minItems": 1,
"description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
},
"cvss_vectors": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"description": "An ordered array of CVSS vectors, each issued by a vendor to rate the vulnerability. The first item in the array is used as the primary CVSS vector, and is used to filter and sort the vulnerability.",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 16,
"maxLength": 128,
"pattern": "^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$"
}
},
"required": [
"vendor",
"vector"
]
},
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 32,
"maxLength": 128,
"pattern": "^CVSS:3[.][01]/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
}
},
"required": [
"vendor",
"vector"
]
}
]
}
},
"links": {
"type": "array",
"description": "An array of references to external documentation or articles that describe the vulnerability.",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the vulnerability details link."
},
"url": {
"type": "string",
"description": "URL of the vulnerability details document.",
"pattern": "^(https?|ftp)://.+"
}
}
}
},
"details": {
"$ref": "#/definitions/named_list/properties/items"
},
"tracking": {
"type": "object",
"description": "Describes how this vulnerability should be tracked as the project changes.",
"oneOf": [
{
"description": "Declares that a series of items should be tracked using source-specific tracking methods.",
"required": [
"items"
],
"properties": {
"type": {
"const": "source"
},
"items": {
"type": "array",
"items": {
"description": "An item that should be tracked using source-specific tracking methods.",
"type": "object",
"required": [
"signatures"
],
"properties": {
"file": {
"type": "string",
"description": "Path to the file where the vulnerability is located."
},
"start_line": {
"type": "number",
"description": "The first line of the file that includes the vulnerability."
},
"end_line": {
"type": "number",
"description": "The last line of the file that includes the vulnerability."
},
"signatures": {
"type": "array",
"description": "An array of calculated tracking signatures for this tracking item.",
"minItems": 1,
"items": {
"description": "A calculated tracking signature value and metadata.",
"type": "object",
"required": [
"algorithm",
"value"
],
"properties": {
"algorithm": {
"type": "string",
"description": "The algorithm used to generate the signature."
},
"value": {
"type": "string",
"description": "The result of this signature algorithm."
}
}
}
}
}
}
}
}
}
],
"properties": {
"type": {
"type": "string",
"description": "Each tracking type must declare its own type."
}
}
},
"flags": {
"description": "Flags that can be attached to vulnerabilities.",
"type": "array",
"items": {
"type": "object",
"description": "Informational flags identified and assigned to a vulnerability.",
"required": [
"type",
"origin",
"description"
],
"properties": {
"type": {
"type": "string",
"minLength": 1,
"description": "Result of the scan.",
"enum": [
"flagged-as-likely-false-positive"
]
},
"origin": {
"minLength": 1,
"description": "Tool that issued the flag.",
"type": "string"
},
"description": {
"minLength": 1,
"description": "What the flag is about.",
"type": "string"
}
}
}
},
"location": {
"description": "The location of the error",
"type": "object",
"properties": {
"crash_address": {
"type": "string",
"description": "The relative address in memory were the crash occurred.",
"examples": [
"0xabababab"
]
},
"stacktrace_snippet": {
"type": "string",
"description": "The stack trace recorded during fuzzing resulting the crash.",
"examples": [
"func_a+0xabcd\nfunc_b+0xabcc"
]
},
"crash_state": {
"type": "string",
"description": "Minimised and normalized crash stack-trace (called crash_state).",
"examples": [
"func_a+0xa\nfunc_b+0xb\nfunc_c+0xc"
]
},
"crash_type": {
"type": "string",
"description": "Type of the crash.",
"examples": [
"Heap-Buffer-overflow",
"Division-by-zero"
]
}
}
}
}
}
},
"remediations": {
"type": "array",
"description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
"items": {
"type": "object",
"required": [
"fixes",
"summary",
"diff"
],
"properties": {
"fixes": {
"type": "array",
"description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
}
}
}
},
"summary": {
"type": "string",
"minLength": 1,
"description": "An overview of how the vulnerabilities were fixed."
},
"diff": {
"type": "string",
"minLength": 1,
"description": "A base64-encoded remediation code diff, compatible with git apply."
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,986 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/dependency-scanning-report-format.json",
"title": "Report format for GitLab Dependency Scanning",
"description": "This schema provides the the report format for Dependency Scanning analyzers (https://docs.gitlab.com/ee/user/application_security/dependency_scanning).",
"definitions": {
"detail_type": {
"oneOf": [
{
"$ref": "#/definitions/named_list"
},
{
"$ref": "#/definitions/list"
},
{
"$ref": "#/definitions/table"
},
{
"$ref": "#/definitions/text"
},
{
"$ref": "#/definitions/url"
},
{
"$ref": "#/definitions/code"
},
{
"$ref": "#/definitions/value"
},
{
"$ref": "#/definitions/diff"
},
{
"$ref": "#/definitions/markdown"
},
{
"$ref": "#/definitions/commit"
},
{
"$ref": "#/definitions/file_location"
},
{
"$ref": "#/definitions/module_location"
}
]
},
"text_value": {
"type": "string"
},
"named_field": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/text_value",
"type": "string",
"minLength": 1
},
"description": {
"$ref": "#/definitions/text_value"
}
}
},
"named_list": {
"type": "object",
"description": "An object with named and typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "named-list"
},
"items": {
"type": "object",
"patternProperties": {
"^.*$": {
"allOf": [
{
"$ref": "#/definitions/named_field"
},
{
"$ref": "#/definitions/detail_type"
}
]
}
}
}
}
},
"list": {
"type": "object",
"description": "A list of typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "list"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
},
"table": {
"type": "object",
"description": "A table of typed fields",
"required": [
"type",
"rows"
],
"properties": {
"type": {
"const": "table"
},
"header": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
}
},
"text": {
"type": "object",
"description": "Raw text",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "text"
},
"value": {
"$ref": "#/definitions/text_value"
}
}
},
"url": {
"type": "object",
"description": "A single URL",
"required": [
"type",
"href"
],
"properties": {
"type": {
"const": "url"
},
"text": {
"$ref": "#/definitions/text_value"
},
"href": {
"type": "string",
"minLength": 1,
"examples": [
"http://mysite.com"
]
}
}
},
"code": {
"type": "object",
"description": "A codeblock",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "code"
},
"value": {
"type": "string"
},
"lang": {
"type": "string",
"description": "A programming language"
}
}
},
"value": {
"type": "object",
"description": "A field that can store a range of types of value",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "value"
},
"value": {
"type": [
"number",
"string",
"boolean"
]
}
}
},
"diff": {
"type": "object",
"description": "A diff",
"required": [
"type",
"before",
"after"
],
"properties": {
"type": {
"const": "diff"
},
"before": {
"type": "string"
},
"after": {
"type": "string"
}
}
},
"markdown": {
"type": "object",
"description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "markdown"
},
"value": {
"$ref": "#/definitions/text_value",
"examples": [
"Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
]
}
}
},
"commit": {
"type": "object",
"description": "A commit/tag/branch within the GitLab project",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "commit"
},
"value": {
"type": "string",
"description": "The commit SHA",
"minLength": 1
}
}
},
"file_location": {
"type": "object",
"description": "A location within a file in the project",
"required": [
"type",
"file_name",
"line_start"
],
"properties": {
"type": {
"const": "file-location"
},
"file_name": {
"type": "string",
"minLength": 1
},
"line_start": {
"type": "integer"
},
"line_end": {
"type": "integer"
}
}
},
"module_location": {
"type": "object",
"description": "A location within a binary module of the form module+relative_offset",
"required": [
"type",
"module_name",
"offset"
],
"properties": {
"type": {
"const": "module-location"
},
"module_name": {
"type": "string",
"minLength": 1,
"examples": [
"compiled_binary"
]
},
"offset": {
"type": "integer",
"examples": [
100
]
}
}
}
},
"self": {
"version": "15.1.1"
},
"type": "object",
"required": [
"scan",
"version",
"vulnerabilities"
],
"additionalProperties": true,
"properties": {
"scan": {
"type": "object",
"required": [
"analyzer",
"end_time",
"scanner",
"start_time",
"status",
"type"
],
"properties": {
"end_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-01-28T03:26:02"
]
},
"messages": {
"type": "array",
"items": {
"type": "object",
"description": "Communication intended for the initiator of a scan.",
"required": [
"level",
"value"
],
"properties": {
"level": {
"type": "string",
"description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
"enum": [
"info",
"warn",
"fatal"
],
"examples": [
"info"
]
},
"value": {
"type": "string",
"description": "The message to communicate.",
"minLength": 1,
"examples": [
"Permission denied, scanning aborted"
]
}
}
}
},
"options": {
"type": "array",
"items": {
"type": "object",
"description": "A configuration option used for this scan.",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"description": "The configuration option name.",
"maxLength": 255,
"minLength": 1,
"examples": [
"DAST_FF_ENABLE_BAS",
"DOCKER_TLS_CERTDIR",
"DS_MAX_DEPTH",
"SECURE_LOG_LEVEL"
]
},
"source": {
"type": "string",
"description": "The source of this option.",
"enum": [
"argument",
"file",
"env_variable",
"other"
]
},
"value": {
"type": [
"boolean",
"integer",
"null",
"string"
],
"description": "The value used for this scan.",
"examples": [
true,
2,
null,
"fatal",
""
]
}
}
}
},
"analyzer": {
"type": "object",
"description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the analyzer.",
"minLength": 1,
"examples": [
"gitlab-dast"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the analyzer, not required to be unique.",
"minLength": 1,
"examples": [
"GitLab DAST"
]
},
"url": {
"type": "string",
"pattern": "^https?://.+",
"description": "A link to more information about the analyzer.",
"examples": [
"https://docs.gitlab.com/ee/user/application_security/dast"
]
},
"vendor": {
"description": "The vendor/maintainer of the analyzer.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
},
"version": {
"type": "string",
"description": "The version of the analyzer.",
"minLength": 1,
"examples": [
"1.0.2"
]
}
}
},
"scanner": {
"type": "object",
"description": "Object defining the scanner used to perform the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the scanner.",
"minLength": 1,
"examples": [
"my-sast-scanner"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the scanner, not required to be unique.",
"minLength": 1,
"examples": [
"My SAST Scanner"
]
},
"url": {
"type": "string",
"description": "A link to more information about the scanner.",
"examples": [
"https://scanner.url"
]
},
"version": {
"type": "string",
"description": "The version of the scanner.",
"minLength": 1,
"examples": [
"1.0.2"
]
},
"vendor": {
"description": "The vendor/maintainer of the scanner.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
}
}
},
"start_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-02-14T16:01:59"
]
},
"status": {
"type": "string",
"description": "Result of the scan.",
"enum": [
"success",
"failure"
]
},
"type": {
"type": "string",
"description": "Type of the scan.",
"enum": [
"dependency_scanning"
]
},
"primary_identifiers": {
"type": "array",
"description": "An unordered array containing an exhaustive list of primary identifiers for which the analyzer may return results",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
}
}
},
"schema": {
"type": "string",
"description": "URI pointing to the validating security report schema.",
"pattern": "^https?://.+"
},
"version": {
"type": "string",
"description": "The version of the schema to which the JSON report conforms.",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"vulnerabilities": {
"type": "array",
"description": "Array of vulnerability objects.",
"items": {
"type": "object",
"description": "Describes the vulnerability using GitLab Flavored Markdown",
"required": [
"id",
"identifiers",
"location"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
},
"name": {
"type": "string",
"maxLength": 255,
"description": "The name of the vulnerability. This must not include the finding's specific information."
},
"description": {
"type": "string",
"maxLength": 1048576,
"description": "A long text section describing the vulnerability more fully."
},
"severity": {
"type": "string",
"description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
"enum": [
"Info",
"Unknown",
"Low",
"Medium",
"High",
"Critical"
]
},
"solution": {
"type": "string",
"maxLength": 7000,
"description": "Explanation of how to fix the vulnerability."
},
"identifiers": {
"type": "array",
"minItems": 1,
"description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
},
"cvss_vectors": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"description": "An ordered array of CVSS vectors, each issued by a vendor to rate the vulnerability. The first item in the array is used as the primary CVSS vector, and is used to filter and sort the vulnerability.",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 16,
"maxLength": 128,
"pattern": "^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$"
}
},
"required": [
"vendor",
"vector"
]
},
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 32,
"maxLength": 128,
"pattern": "^CVSS:3[.][01]/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
}
},
"required": [
"vendor",
"vector"
]
}
]
}
},
"links": {
"type": "array",
"description": "An array of references to external documentation or articles that describe the vulnerability.",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the vulnerability details link."
},
"url": {
"type": "string",
"description": "URL of the vulnerability details document.",
"pattern": "^(https?|ftp)://.+"
}
}
}
},
"details": {
"$ref": "#/definitions/named_list/properties/items"
},
"tracking": {
"type": "object",
"description": "Describes how this vulnerability should be tracked as the project changes.",
"oneOf": [
{
"description": "Declares that a series of items should be tracked using source-specific tracking methods.",
"required": [
"items"
],
"properties": {
"type": {
"const": "source"
},
"items": {
"type": "array",
"items": {
"description": "An item that should be tracked using source-specific tracking methods.",
"type": "object",
"required": [
"signatures"
],
"properties": {
"file": {
"type": "string",
"description": "Path to the file where the vulnerability is located."
},
"start_line": {
"type": "number",
"description": "The first line of the file that includes the vulnerability."
},
"end_line": {
"type": "number",
"description": "The last line of the file that includes the vulnerability."
},
"signatures": {
"type": "array",
"description": "An array of calculated tracking signatures for this tracking item.",
"minItems": 1,
"items": {
"description": "A calculated tracking signature value and metadata.",
"type": "object",
"required": [
"algorithm",
"value"
],
"properties": {
"algorithm": {
"type": "string",
"description": "The algorithm used to generate the signature."
},
"value": {
"type": "string",
"description": "The result of this signature algorithm."
}
}
}
}
}
}
}
}
}
],
"properties": {
"type": {
"type": "string",
"description": "Each tracking type must declare its own type."
}
}
},
"flags": {
"description": "Flags that can be attached to vulnerabilities.",
"type": "array",
"items": {
"type": "object",
"description": "Informational flags identified and assigned to a vulnerability.",
"required": [
"type",
"origin",
"description"
],
"properties": {
"type": {
"type": "string",
"minLength": 1,
"description": "Result of the scan.",
"enum": [
"flagged-as-likely-false-positive"
]
},
"origin": {
"minLength": 1,
"description": "Tool that issued the flag.",
"type": "string"
},
"description": {
"minLength": 1,
"description": "What the flag is about.",
"type": "string"
}
}
}
},
"location": {
"type": "object",
"description": "Identifies the vulnerability's location.",
"required": [
"file",
"dependency"
],
"properties": {
"file": {
"type": "string",
"minLength": 1,
"description": "Path to the manifest or lock file where the dependency is declared (such as yarn.lock)."
},
"dependency": {
"type": "object",
"description": "Describes the dependency of a project where the vulnerability is located.",
"required": [
"package",
"version"
],
"properties": {
"package": {
"type": "object",
"description": "Provides information on the package where the vulnerability is located.",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the package where the vulnerability is located."
}
}
},
"version": {
"type": "string",
"description": "Version of the vulnerable package."
},
"direct": {
"type": "boolean",
"description": "Tells whether this is a direct, top-level dependency of the scanned project."
}
}
}
}
}
}
}
},
"remediations": {
"type": "array",
"description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
"items": {
"type": "object",
"required": [
"fixes",
"summary",
"diff"
],
"properties": {
"fixes": {
"type": "array",
"description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
}
}
}
},
"summary": {
"type": "string",
"minLength": 1,
"description": "An overview of how the vulnerabilities were fixed."
},
"diff": {
"type": "string",
"minLength": 1,
"description": "A base64-encoded remediation code diff, compatible with git apply."
}
}
}
}
}
}

View File

@ -0,0 +1,970 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/sast-report-format.json",
"title": "Report format for GitLab SAST",
"description": "This schema provides the report format for Static Application Security Testing analyzers (https://docs.gitlab.com/ee/user/application_security/sast).",
"definitions": {
"detail_type": {
"oneOf": [
{
"$ref": "#/definitions/named_list"
},
{
"$ref": "#/definitions/list"
},
{
"$ref": "#/definitions/table"
},
{
"$ref": "#/definitions/text"
},
{
"$ref": "#/definitions/url"
},
{
"$ref": "#/definitions/code"
},
{
"$ref": "#/definitions/value"
},
{
"$ref": "#/definitions/diff"
},
{
"$ref": "#/definitions/markdown"
},
{
"$ref": "#/definitions/commit"
},
{
"$ref": "#/definitions/file_location"
},
{
"$ref": "#/definitions/module_location"
}
]
},
"text_value": {
"type": "string"
},
"named_field": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/text_value",
"type": "string",
"minLength": 1
},
"description": {
"$ref": "#/definitions/text_value"
}
}
},
"named_list": {
"type": "object",
"description": "An object with named and typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "named-list"
},
"items": {
"type": "object",
"patternProperties": {
"^.*$": {
"allOf": [
{
"$ref": "#/definitions/named_field"
},
{
"$ref": "#/definitions/detail_type"
}
]
}
}
}
}
},
"list": {
"type": "object",
"description": "A list of typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "list"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
},
"table": {
"type": "object",
"description": "A table of typed fields",
"required": [
"type",
"rows"
],
"properties": {
"type": {
"const": "table"
},
"header": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
}
},
"text": {
"type": "object",
"description": "Raw text",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "text"
},
"value": {
"$ref": "#/definitions/text_value"
}
}
},
"url": {
"type": "object",
"description": "A single URL",
"required": [
"type",
"href"
],
"properties": {
"type": {
"const": "url"
},
"text": {
"$ref": "#/definitions/text_value"
},
"href": {
"type": "string",
"minLength": 1,
"examples": [
"http://mysite.com"
]
}
}
},
"code": {
"type": "object",
"description": "A codeblock",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "code"
},
"value": {
"type": "string"
},
"lang": {
"type": "string",
"description": "A programming language"
}
}
},
"value": {
"type": "object",
"description": "A field that can store a range of types of value",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "value"
},
"value": {
"type": [
"number",
"string",
"boolean"
]
}
}
},
"diff": {
"type": "object",
"description": "A diff",
"required": [
"type",
"before",
"after"
],
"properties": {
"type": {
"const": "diff"
},
"before": {
"type": "string"
},
"after": {
"type": "string"
}
}
},
"markdown": {
"type": "object",
"description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "markdown"
},
"value": {
"$ref": "#/definitions/text_value",
"examples": [
"Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
]
}
}
},
"commit": {
"type": "object",
"description": "A commit/tag/branch within the GitLab project",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "commit"
},
"value": {
"type": "string",
"description": "The commit SHA",
"minLength": 1
}
}
},
"file_location": {
"type": "object",
"description": "A location within a file in the project",
"required": [
"type",
"file_name",
"line_start"
],
"properties": {
"type": {
"const": "file-location"
},
"file_name": {
"type": "string",
"minLength": 1
},
"line_start": {
"type": "integer"
},
"line_end": {
"type": "integer"
}
}
},
"module_location": {
"type": "object",
"description": "A location within a binary module of the form module+relative_offset",
"required": [
"type",
"module_name",
"offset"
],
"properties": {
"type": {
"const": "module-location"
},
"module_name": {
"type": "string",
"minLength": 1,
"examples": [
"compiled_binary"
]
},
"offset": {
"type": "integer",
"examples": [
100
]
}
}
}
},
"self": {
"version": "15.1.1"
},
"type": "object",
"required": [
"scan",
"version",
"vulnerabilities"
],
"additionalProperties": true,
"properties": {
"scan": {
"type": "object",
"required": [
"analyzer",
"end_time",
"scanner",
"start_time",
"status",
"type"
],
"properties": {
"end_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-01-28T03:26:02"
]
},
"messages": {
"type": "array",
"items": {
"type": "object",
"description": "Communication intended for the initiator of a scan.",
"required": [
"level",
"value"
],
"properties": {
"level": {
"type": "string",
"description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
"enum": [
"info",
"warn",
"fatal"
],
"examples": [
"info"
]
},
"value": {
"type": "string",
"description": "The message to communicate.",
"minLength": 1,
"examples": [
"Permission denied, scanning aborted"
]
}
}
}
},
"options": {
"type": "array",
"items": {
"type": "object",
"description": "A configuration option used for this scan.",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"description": "The configuration option name.",
"maxLength": 255,
"minLength": 1,
"examples": [
"DAST_FF_ENABLE_BAS",
"DOCKER_TLS_CERTDIR",
"DS_MAX_DEPTH",
"SECURE_LOG_LEVEL"
]
},
"source": {
"type": "string",
"description": "The source of this option.",
"enum": [
"argument",
"file",
"env_variable",
"other"
]
},
"value": {
"type": [
"boolean",
"integer",
"null",
"string"
],
"description": "The value used for this scan.",
"examples": [
true,
2,
null,
"fatal",
""
]
}
}
}
},
"analyzer": {
"type": "object",
"description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the analyzer.",
"minLength": 1,
"examples": [
"gitlab-dast"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the analyzer, not required to be unique.",
"minLength": 1,
"examples": [
"GitLab DAST"
]
},
"url": {
"type": "string",
"pattern": "^https?://.+",
"description": "A link to more information about the analyzer.",
"examples": [
"https://docs.gitlab.com/ee/user/application_security/dast"
]
},
"vendor": {
"description": "The vendor/maintainer of the analyzer.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
},
"version": {
"type": "string",
"description": "The version of the analyzer.",
"minLength": 1,
"examples": [
"1.0.2"
]
}
}
},
"scanner": {
"type": "object",
"description": "Object defining the scanner used to perform the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the scanner.",
"minLength": 1,
"examples": [
"my-sast-scanner"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the scanner, not required to be unique.",
"minLength": 1,
"examples": [
"My SAST Scanner"
]
},
"url": {
"type": "string",
"description": "A link to more information about the scanner.",
"examples": [
"https://scanner.url"
]
},
"version": {
"type": "string",
"description": "The version of the scanner.",
"minLength": 1,
"examples": [
"1.0.2"
]
},
"vendor": {
"description": "The vendor/maintainer of the scanner.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
}
}
},
"start_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-02-14T16:01:59"
]
},
"status": {
"type": "string",
"description": "Result of the scan.",
"enum": [
"success",
"failure"
]
},
"type": {
"type": "string",
"description": "Type of the scan.",
"enum": [
"sast"
]
},
"primary_identifiers": {
"type": "array",
"description": "An unordered array containing an exhaustive list of primary identifiers for which the analyzer may return results",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
}
}
},
"schema": {
"type": "string",
"description": "URI pointing to the validating security report schema.",
"pattern": "^https?://.+"
},
"version": {
"type": "string",
"description": "The version of the schema to which the JSON report conforms.",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"vulnerabilities": {
"type": "array",
"description": "Array of vulnerability objects.",
"items": {
"type": "object",
"description": "Describes the vulnerability using GitLab Flavored Markdown",
"required": [
"id",
"identifiers",
"location"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
},
"name": {
"type": "string",
"maxLength": 255,
"description": "The name of the vulnerability. This must not include the finding's specific information."
},
"description": {
"type": "string",
"maxLength": 1048576,
"description": "A long text section describing the vulnerability more fully."
},
"severity": {
"type": "string",
"description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
"enum": [
"Info",
"Unknown",
"Low",
"Medium",
"High",
"Critical"
]
},
"solution": {
"type": "string",
"maxLength": 7000,
"description": "Explanation of how to fix the vulnerability."
},
"identifiers": {
"type": "array",
"minItems": 1,
"description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
},
"cvss_vectors": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"description": "An ordered array of CVSS vectors, each issued by a vendor to rate the vulnerability. The first item in the array is used as the primary CVSS vector, and is used to filter and sort the vulnerability.",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 16,
"maxLength": 128,
"pattern": "^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$"
}
},
"required": [
"vendor",
"vector"
]
},
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 32,
"maxLength": 128,
"pattern": "^CVSS:3[.][01]/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
}
},
"required": [
"vendor",
"vector"
]
}
]
}
},
"links": {
"type": "array",
"description": "An array of references to external documentation or articles that describe the vulnerability.",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the vulnerability details link."
},
"url": {
"type": "string",
"description": "URL of the vulnerability details document.",
"pattern": "^(https?|ftp)://.+"
}
}
}
},
"details": {
"$ref": "#/definitions/named_list/properties/items"
},
"tracking": {
"type": "object",
"description": "Describes how this vulnerability should be tracked as the project changes.",
"oneOf": [
{
"description": "Declares that a series of items should be tracked using source-specific tracking methods.",
"required": [
"items"
],
"properties": {
"type": {
"const": "source"
},
"items": {
"type": "array",
"items": {
"description": "An item that should be tracked using source-specific tracking methods.",
"type": "object",
"required": [
"signatures"
],
"properties": {
"file": {
"type": "string",
"description": "Path to the file where the vulnerability is located."
},
"start_line": {
"type": "number",
"description": "The first line of the file that includes the vulnerability."
},
"end_line": {
"type": "number",
"description": "The last line of the file that includes the vulnerability."
},
"signatures": {
"type": "array",
"description": "An array of calculated tracking signatures for this tracking item.",
"minItems": 1,
"items": {
"description": "A calculated tracking signature value and metadata.",
"type": "object",
"required": [
"algorithm",
"value"
],
"properties": {
"algorithm": {
"type": "string",
"description": "The algorithm used to generate the signature."
},
"value": {
"type": "string",
"description": "The result of this signature algorithm."
}
}
}
}
}
}
}
}
}
],
"properties": {
"type": {
"type": "string",
"description": "Each tracking type must declare its own type."
}
}
},
"flags": {
"description": "Flags that can be attached to vulnerabilities.",
"type": "array",
"items": {
"type": "object",
"description": "Informational flags identified and assigned to a vulnerability.",
"required": [
"type",
"origin",
"description"
],
"properties": {
"type": {
"type": "string",
"minLength": 1,
"description": "Result of the scan.",
"enum": [
"flagged-as-likely-false-positive"
]
},
"origin": {
"minLength": 1,
"description": "Tool that issued the flag.",
"type": "string"
},
"description": {
"minLength": 1,
"description": "What the flag is about.",
"type": "string"
}
}
}
},
"location": {
"type": "object",
"description": "Identifies the vulnerability's location.",
"properties": {
"file": {
"type": "string",
"description": "Path to the file where the vulnerability is located."
},
"start_line": {
"type": "number",
"description": "The first line of the code affected by the vulnerability."
},
"end_line": {
"type": "number",
"description": "The last line of the code affected by the vulnerability."
},
"class": {
"type": "string",
"description": "Provides the name of the class where the vulnerability is located."
},
"method": {
"type": "string",
"description": "Provides the name of the method where the vulnerability is located."
}
}
},
"raw_source_code_extract": {
"type": "string",
"description": "Provides an unsanitized excerpt of the affected source code."
}
}
}
},
"remediations": {
"type": "array",
"description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
"items": {
"type": "object",
"required": [
"fixes",
"summary",
"diff"
],
"properties": {
"fixes": {
"type": "array",
"description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
}
}
}
},
"summary": {
"type": "string",
"minLength": 1,
"description": "An overview of how the vulnerabilities were fixed."
},
"diff": {
"type": "string",
"minLength": 1,
"description": "A base64-encoded remediation code diff, compatible with git apply."
}
}
}
}
}
}

View File

@ -0,0 +1,994 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/secret-detection-report-format.json",
"title": "Report format for GitLab Secret Detection",
"description": "This schema provides the the report format for the Secret Detection analyzer (https://docs.gitlab.com/ee/user/application_security/secret_detection)",
"definitions": {
"detail_type": {
"oneOf": [
{
"$ref": "#/definitions/named_list"
},
{
"$ref": "#/definitions/list"
},
{
"$ref": "#/definitions/table"
},
{
"$ref": "#/definitions/text"
},
{
"$ref": "#/definitions/url"
},
{
"$ref": "#/definitions/code"
},
{
"$ref": "#/definitions/value"
},
{
"$ref": "#/definitions/diff"
},
{
"$ref": "#/definitions/markdown"
},
{
"$ref": "#/definitions/commit"
},
{
"$ref": "#/definitions/file_location"
},
{
"$ref": "#/definitions/module_location"
}
]
},
"text_value": {
"type": "string"
},
"named_field": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/text_value",
"type": "string",
"minLength": 1
},
"description": {
"$ref": "#/definitions/text_value"
}
}
},
"named_list": {
"type": "object",
"description": "An object with named and typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "named-list"
},
"items": {
"type": "object",
"patternProperties": {
"^.*$": {
"allOf": [
{
"$ref": "#/definitions/named_field"
},
{
"$ref": "#/definitions/detail_type"
}
]
}
}
}
}
},
"list": {
"type": "object",
"description": "A list of typed fields",
"required": [
"type",
"items"
],
"properties": {
"type": {
"const": "list"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
},
"table": {
"type": "object",
"description": "A table of typed fields",
"required": [
"type",
"rows"
],
"properties": {
"type": {
"const": "table"
},
"header": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
},
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/detail_type"
}
}
}
}
},
"text": {
"type": "object",
"description": "Raw text",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "text"
},
"value": {
"$ref": "#/definitions/text_value"
}
}
},
"url": {
"type": "object",
"description": "A single URL",
"required": [
"type",
"href"
],
"properties": {
"type": {
"const": "url"
},
"text": {
"$ref": "#/definitions/text_value"
},
"href": {
"type": "string",
"minLength": 1,
"examples": [
"http://mysite.com"
]
}
}
},
"code": {
"type": "object",
"description": "A codeblock",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "code"
},
"value": {
"type": "string"
},
"lang": {
"type": "string",
"description": "A programming language"
}
}
},
"value": {
"type": "object",
"description": "A field that can store a range of types of value",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "value"
},
"value": {
"type": [
"number",
"string",
"boolean"
]
}
}
},
"diff": {
"type": "object",
"description": "A diff",
"required": [
"type",
"before",
"after"
],
"properties": {
"type": {
"const": "diff"
},
"before": {
"type": "string"
},
"after": {
"type": "string"
}
}
},
"markdown": {
"type": "object",
"description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "markdown"
},
"value": {
"$ref": "#/definitions/text_value",
"examples": [
"Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
]
}
}
},
"commit": {
"type": "object",
"description": "A commit/tag/branch within the GitLab project",
"required": [
"type",
"value"
],
"properties": {
"type": {
"const": "commit"
},
"value": {
"type": "string",
"description": "The commit SHA",
"minLength": 1
}
}
},
"file_location": {
"type": "object",
"description": "A location within a file in the project",
"required": [
"type",
"file_name",
"line_start"
],
"properties": {
"type": {
"const": "file-location"
},
"file_name": {
"type": "string",
"minLength": 1
},
"line_start": {
"type": "integer"
},
"line_end": {
"type": "integer"
}
}
},
"module_location": {
"type": "object",
"description": "A location within a binary module of the form module+relative_offset",
"required": [
"type",
"module_name",
"offset"
],
"properties": {
"type": {
"const": "module-location"
},
"module_name": {
"type": "string",
"minLength": 1,
"examples": [
"compiled_binary"
]
},
"offset": {
"type": "integer",
"examples": [
100
]
}
}
}
},
"self": {
"version": "15.1.1"
},
"type": "object",
"required": [
"scan",
"version",
"vulnerabilities"
],
"additionalProperties": true,
"properties": {
"scan": {
"type": "object",
"required": [
"analyzer",
"end_time",
"scanner",
"start_time",
"status",
"type"
],
"properties": {
"end_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-01-28T03:26:02"
]
},
"messages": {
"type": "array",
"items": {
"type": "object",
"description": "Communication intended for the initiator of a scan.",
"required": [
"level",
"value"
],
"properties": {
"level": {
"type": "string",
"description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
"enum": [
"info",
"warn",
"fatal"
],
"examples": [
"info"
]
},
"value": {
"type": "string",
"description": "The message to communicate.",
"minLength": 1,
"examples": [
"Permission denied, scanning aborted"
]
}
}
}
},
"options": {
"type": "array",
"items": {
"type": "object",
"description": "A configuration option used for this scan.",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"description": "The configuration option name.",
"maxLength": 255,
"minLength": 1,
"examples": [
"DAST_FF_ENABLE_BAS",
"DOCKER_TLS_CERTDIR",
"DS_MAX_DEPTH",
"SECURE_LOG_LEVEL"
]
},
"source": {
"type": "string",
"description": "The source of this option.",
"enum": [
"argument",
"file",
"env_variable",
"other"
]
},
"value": {
"type": [
"boolean",
"integer",
"null",
"string"
],
"description": "The value used for this scan.",
"examples": [
true,
2,
null,
"fatal",
""
]
}
}
}
},
"analyzer": {
"type": "object",
"description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the analyzer.",
"minLength": 1,
"examples": [
"gitlab-dast"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the analyzer, not required to be unique.",
"minLength": 1,
"examples": [
"GitLab DAST"
]
},
"url": {
"type": "string",
"pattern": "^https?://.+",
"description": "A link to more information about the analyzer.",
"examples": [
"https://docs.gitlab.com/ee/user/application_security/dast"
]
},
"vendor": {
"description": "The vendor/maintainer of the analyzer.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
},
"version": {
"type": "string",
"description": "The version of the analyzer.",
"minLength": 1,
"examples": [
"1.0.2"
]
}
}
},
"scanner": {
"type": "object",
"description": "Object defining the scanner used to perform the scan.",
"required": [
"id",
"name",
"version",
"vendor"
],
"properties": {
"id": {
"type": "string",
"description": "Unique id that identifies the scanner.",
"minLength": 1,
"examples": [
"my-sast-scanner"
]
},
"name": {
"type": "string",
"description": "A human readable value that identifies the scanner, not required to be unique.",
"minLength": 1,
"examples": [
"My SAST Scanner"
]
},
"url": {
"type": "string",
"description": "A link to more information about the scanner.",
"examples": [
"https://scanner.url"
]
},
"version": {
"type": "string",
"description": "The version of the scanner.",
"minLength": 1,
"examples": [
"1.0.2"
]
},
"vendor": {
"description": "The vendor/maintainer of the scanner.",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the vendor.",
"minLength": 1,
"examples": [
"GitLab"
]
}
}
}
}
},
"start_time": {
"type": "string",
"description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
"examples": [
"2020-02-14T16:01:59"
]
},
"status": {
"type": "string",
"description": "Result of the scan.",
"enum": [
"success",
"failure"
]
},
"type": {
"type": "string",
"description": "Type of the scan.",
"enum": [
"secret_detection"
]
},
"primary_identifiers": {
"type": "array",
"description": "An unordered array containing an exhaustive list of primary identifiers for which the analyzer may return results",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
}
}
},
"schema": {
"type": "string",
"description": "URI pointing to the validating security report schema.",
"pattern": "^https?://.+"
},
"version": {
"type": "string",
"description": "The version of the schema to which the JSON report conforms.",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"vulnerabilities": {
"type": "array",
"description": "Array of vulnerability objects.",
"items": {
"type": "object",
"description": "Describes the vulnerability using GitLab Flavored Markdown",
"required": [
"id",
"identifiers",
"location"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
},
"name": {
"type": "string",
"maxLength": 255,
"description": "The name of the vulnerability. This must not include the finding's specific information."
},
"description": {
"type": "string",
"maxLength": 1048576,
"description": "A long text section describing the vulnerability more fully."
},
"severity": {
"type": "string",
"description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
"enum": [
"Info",
"Unknown",
"Low",
"Medium",
"High",
"Critical"
]
},
"solution": {
"type": "string",
"maxLength": 7000,
"description": "Explanation of how to fix the vulnerability."
},
"identifiers": {
"type": "array",
"minItems": 1,
"description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
"items": {
"type": "object",
"required": [
"type",
"name",
"value"
],
"properties": {
"type": {
"type": "string",
"description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
"minLength": 1
},
"name": {
"type": "string",
"description": "Human-readable name of the identifier.",
"minLength": 1
},
"url": {
"type": "string",
"description": "URL of the identifier's documentation.",
"pattern": "^(https?|ftp)://.+"
},
"value": {
"type": "string",
"description": "Value of the identifier, for matching purpose.",
"minLength": 1
}
}
}
},
"cvss_vectors": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"description": "An ordered array of CVSS vectors, each issued by a vendor to rate the vulnerability. The first item in the array is used as the primary CVSS vector, and is used to filter and sort the vulnerability.",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 16,
"maxLength": 128,
"pattern": "^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$"
}
},
"required": [
"vendor",
"vector"
]
},
{
"type": "object",
"properties": {
"vendor": {
"type": "string",
"minLength": 1,
"default": "unknown"
},
"vector": {
"type": "string",
"minLength": 32,
"maxLength": 128,
"pattern": "^CVSS:3[.][01]/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$"
}
},
"required": [
"vendor",
"vector"
]
}
]
}
},
"links": {
"type": "array",
"description": "An array of references to external documentation or articles that describe the vulnerability.",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the vulnerability details link."
},
"url": {
"type": "string",
"description": "URL of the vulnerability details document.",
"pattern": "^(https?|ftp)://.+"
}
}
}
},
"details": {
"$ref": "#/definitions/named_list/properties/items"
},
"tracking": {
"type": "object",
"description": "Describes how this vulnerability should be tracked as the project changes.",
"oneOf": [
{
"description": "Declares that a series of items should be tracked using source-specific tracking methods.",
"required": [
"items"
],
"properties": {
"type": {
"const": "source"
},
"items": {
"type": "array",
"items": {
"description": "An item that should be tracked using source-specific tracking methods.",
"type": "object",
"required": [
"signatures"
],
"properties": {
"file": {
"type": "string",
"description": "Path to the file where the vulnerability is located."
},
"start_line": {
"type": "number",
"description": "The first line of the file that includes the vulnerability."
},
"end_line": {
"type": "number",
"description": "The last line of the file that includes the vulnerability."
},
"signatures": {
"type": "array",
"description": "An array of calculated tracking signatures for this tracking item.",
"minItems": 1,
"items": {
"description": "A calculated tracking signature value and metadata.",
"type": "object",
"required": [
"algorithm",
"value"
],
"properties": {
"algorithm": {
"type": "string",
"description": "The algorithm used to generate the signature."
},
"value": {
"type": "string",
"description": "The result of this signature algorithm."
}
}
}
}
}
}
}
}
}
],
"properties": {
"type": {
"type": "string",
"description": "Each tracking type must declare its own type."
}
}
},
"flags": {
"description": "Flags that can be attached to vulnerabilities.",
"type": "array",
"items": {
"type": "object",
"description": "Informational flags identified and assigned to a vulnerability.",
"required": [
"type",
"origin",
"description"
],
"properties": {
"type": {
"type": "string",
"minLength": 1,
"description": "Result of the scan.",
"enum": [
"flagged-as-likely-false-positive"
]
},
"origin": {
"minLength": 1,
"description": "Tool that issued the flag.",
"type": "string"
},
"description": {
"minLength": 1,
"description": "What the flag is about.",
"type": "string"
}
}
}
},
"location": {
"required": [
"commit"
],
"type": "object",
"properties": {
"file": {
"type": "string",
"description": "Path to the file where the vulnerability is located"
},
"commit": {
"type": "object",
"description": "Represents the commit in which the vulnerability was detected",
"required": [
"sha"
],
"properties": {
"author": {
"type": "string"
},
"date": {
"type": "string"
},
"message": {
"type": "string"
},
"sha": {
"type": "string",
"minLength": 1
}
}
},
"start_line": {
"type": "number",
"description": "The first line of the code affected by the vulnerability"
},
"end_line": {
"type": "number",
"description": "The last line of the code affected by the vulnerability"
},
"class": {
"type": "string",
"description": "Provides the name of the class where the vulnerability is located"
},
"method": {
"type": "string",
"description": "Provides the name of the method where the vulnerability is located"
}
}
},
"raw_source_code_extract": {
"type": "string",
"description": "Provides an unsanitized excerpt of the affected source code."
}
}
}
},
"remediations": {
"type": "array",
"description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
"items": {
"type": "object",
"required": [
"fixes",
"summary",
"diff"
],
"properties": {
"fixes": {
"type": "array",
"description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
"examples": [
"642735a5-1425-428d-8d4e-3c854885a3c9"
]
}
}
}
},
"summary": {
"type": "string",
"minLength": 1,
"description": "An overview of how the vulnerabilities were fixed."
},
"diff": {
"type": "string",
"minLength": 1,
"description": "A base64-encoded remediation code diff, compatible with git apply."
}
}
}
}
}
}

View File

@ -111,11 +111,11 @@ class UploadedFile
alias_method :local_path, :path
def method_missing(method_name, *args, &block) #:nodoc:
def method_missing(method_name, *args, &block) # :nodoc:
@tempfile.__send__(method_name, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def respond_to?(method_name, include_private = false) #:nodoc:
def respond_to?(method_name, include_private = false) # :nodoc:
@tempfile.respond_to?(method_name, include_private) || super
end

View File

@ -9829,7 +9829,7 @@ msgstr ""
msgid "CICD|Prevent CI/CD job tokens from this project from being used to access other projects unless the other project is added to the allowlist. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more%{linkEnd}."
msgstr ""
msgid "CICD|Prevent access to this project from other project CI/CD job tokens, unless the other project is added to the allowlist. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}"
msgid "CICD|Prevent access to this project from other project CI/CD job tokens, unless the other project is added to the allowlist. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more%{linkEnd}."
msgstr ""
msgid "CICD|The %{boldStart}Limit access %{boldEnd}%{italicAndBoldStart}from%{italicAndBoldEnd}%{boldStart} this project%{boldEnd} setting is deprecated and will be removed in the 18.0 milestone. Use the %{boldStart}Limit access %{boldEnd}%{italicAndBoldStart}to%{italicAndBoldEnd}%{boldStart} this project%{boldEnd} setting and allowlist instead. %{linkStart}How do I do this?%{linkEnd}"
@ -17887,6 +17887,9 @@ msgstr ""
msgid "Deployments|No deployment history"
msgstr ""
msgid "Deployment|A colleague"
msgstr ""
msgid "Deployment|Add approval comment"
msgstr ""
@ -17920,6 +17923,9 @@ msgstr ""
msgid "Deployment|Deployment ID"
msgstr ""
msgid "Deployment|Deployment approvals require a Premium or Ultimate subscription"
msgstr ""
msgid "Deployment|Failed"
msgstr ""
@ -17944,6 +17950,9 @@ msgstr ""
msgid "Deployment|Flux sync status is unknown"
msgstr ""
msgid "Deployment|Improve your continuous delivery practices with deployment approvals. Configure rules for required approvals, control which users can deploy to your environments, and collaborate throughout the delivery process."
msgstr ""
msgid "Deployment|Job"
msgstr ""
@ -17962,6 +17971,9 @@ msgstr ""
msgid "Deployment|Ready to be deployed."
msgstr ""
msgid "Deployment|Ready to use deployment approvals?"
msgstr ""
msgid "Deployment|Reject"
msgstr ""
@ -17982,6 +17994,15 @@ msgstr[1] ""
msgid "Deployment|Running"
msgstr ""
msgid "Deployment|Set up Deployment Approvals"
msgstr ""
msgid "Deployment|Set up deployment approvals to get more our of your deployments"
msgstr ""
msgid "Deployment|Set up deployment approvals to get started"
msgstr ""
msgid "Deployment|Skipped"
msgstr ""
@ -18015,6 +18036,9 @@ msgstr ""
msgid "Deployment|Unknown"
msgstr ""
msgid "Deployment|Upgrade to get more our of your deployments"
msgstr ""
msgid "Deployment|Waiting"
msgstr ""
@ -18027,6 +18051,9 @@ msgstr ""
msgid "Deployment|What would you like to see here?"
msgstr ""
msgid "Deployment|You"
msgstr ""
msgid "Deprecated API rate limits"
msgstr ""
@ -23235,6 +23262,9 @@ msgstr ""
msgid "GitLab Pages has moved"
msgstr ""
msgid "GitLab Premium"
msgstr ""
msgid "GitLab Shell"
msgstr ""
@ -25917,6 +25947,9 @@ msgstr ""
msgid "How to track time"
msgstr ""
msgid "HttpDestinationValidator validates only http external audit event destinations."
msgstr ""
msgid "I am sorry, I am unable to find what you are looking for."
msgstr ""
@ -29956,6 +29989,9 @@ msgstr ""
msgid "Last activity"
msgstr ""
msgid "Last activity:"
msgstr ""
msgid "Last attempted number:"
msgstr ""
@ -55943,6 +55979,9 @@ msgstr ""
msgid "User created at"
msgstr ""
msgid "User created:"
msgstr ""
msgid "User deleted own account on %{timestamp}"
msgstr ""
@ -62253,6 +62292,9 @@ msgstr ""
msgid "should be greater than or equal to %{access} inherited membership from group %{group_name}"
msgstr ""
msgid "should have length between 16 to 24 characters."
msgstr ""
msgid "show %{count} more"
msgstr ""
@ -62409,6 +62451,9 @@ msgstr ""
msgid "uploads"
msgstr ""
msgid "url is already taken."
msgstr ""
msgid "user"
msgid_plural "users"
msgstr[0] ""

View File

@ -0,0 +1,116 @@
import { shallowMount } from '@vue/test-utils';
import { GlBanner, GlTableLite, GlBadge } from '@gitlab/ui';
import ApprovalsEmptyState from '~/deployments/components/approvals_empty_state.vue';
import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
describe('~/deployments/components/approvals_empty_state.vue', () => {
let wrapper;
let userCalloutDismissSpy;
const createComponent = ({ shouldShowCallout = true, propsData = {}, slots = {} } = {}) => {
wrapper = shallowMount(ApprovalsEmptyState, {
propsData,
slots,
stubs: {
UserCalloutDismisser: makeMockUserCalloutDismisser({
dismiss: userCalloutDismissSpy,
shouldShowCallout,
}),
GlBanner,
},
});
};
const findBanner = () => wrapper.findComponent(GlBanner);
const findBadge = () => wrapper.findComponent(GlBadge);
const findTable = () => wrapper.findComponent(GlTableLite);
describe('when the callout is not dismissed', () => {
it('shows the banner', () => {
createComponent();
expect(findBanner().exists()).toBe(true);
});
describe('with default values', () => {
beforeEach(() => {
createComponent();
});
it('renders default banner props', () => {
expect(findBanner().props()).toMatchObject({
title: 'Upgrade to get more our of your deployments',
buttonText: 'Learn more',
buttonLink: '/help/ci/environments/deployment_approvals',
});
});
it('renders table header with the correct text', () => {
expect(findBanner().text()).toContain(
'Deployment approvals require a Premium or Ultimate subscription',
);
});
it('renders table header with the premium badge', () => {
expect(findBadge().props()).toMatchObject({ icon: 'license', variant: 'tier' });
expect(findBadge().text()).toBe('GitLab Premium');
});
it('renders table with static data', () => {
expect(findTable().props('fields')).toEqual([
{ key: 'approvers', label: 'Approvers' },
{ key: 'approvals', label: 'Approvals' },
{ key: 'approvedBy', label: 'Approved By' },
]);
});
it('renders CTA text in banner actions', () => {
expect(findBanner().text()).toContain('Ready to use deployment approvals?');
});
});
describe('with custom values', () => {
it('renders default banner using values from props', () => {
const bannerTitle = 'Custom title';
const buttonText = 'Custom button text';
const buttonLink = '/custom/link';
createComponent({
propsData: { bannerTitle, buttonText, buttonLink },
});
expect(findBanner().props()).toMatchObject({
title: bannerTitle,
buttonText,
buttonLink,
});
});
it('renders custom table-header slot', () => {
createComponent({
slots: { 'table-header': '<div>Custom table-header</div>' },
});
expect(findBanner().text()).toContain('Custom table-header');
});
it('renders custom banner-actions slot', () => {
createComponent({
slots: { 'banner-actions': '<div>Custom banner-actions</div>' },
});
expect(findBanner().text()).toContain('Custom banner-actions');
});
});
});
describe('when the callout is dismissed', () => {
beforeEach(() => {
createComponent({ shouldShowCallout: false });
});
it("doesn't show the banner", () => {
expect(findBanner().exists()).toBe(false);
});
});
});

View File

@ -10,6 +10,8 @@ import ShowDeployment from '~/deployments/components/show_deployment.vue';
import DeploymentHeader from '~/deployments/components/deployment_header.vue';
import DeploymentDeployBlock from '~/deployments/components/deployment_deploy_block.vue';
import DetailsFeedback from '~/deployments/components/details_feedback.vue';
import DeploymentAside from '~/deployments/components/deployment_aside.vue';
import ApprovalsEmptyState from 'ee_else_ce/deployments/components/approvals_empty_state.vue';
import deploymentQuery from '~/deployments/graphql/queries/deployment.query.graphql';
import environmentQuery from '~/deployments/graphql/queries/environment.query.graphql';
import waitForPromises from 'helpers/wait_for_promises';
@ -24,6 +26,7 @@ const PROJECT_PATH = 'group/project';
const ENVIRONMENT_NAME = mockEnvironmentFixture.data.project.environment.name;
const DEPLOYMENT_IID = mockDeploymentFixture.data.project.deployment.iid;
const GRAPHQL_ETAG_KEY = 'project/environments';
const PROTECTED_ENVIRONMENTS_SETTINGS_PATH = '/settings/ci_cd#js-protected-environments-settings';
describe('~/deployments/components/show_deployment.vue', () => {
let wrapper;
@ -48,6 +51,8 @@ describe('~/deployments/components/show_deployment.vue', () => {
environmentName: ENVIRONMENT_NAME,
deploymentIid: DEPLOYMENT_IID,
graphqlEtagKey: GRAPHQL_ETAG_KEY,
protectedEnvironmentsAvailable: true,
protectedEnvironmentsSettingsPath: PROTECTED_ENVIRONMENTS_SETTINGS_PATH,
},
stubs: {
GlSprintf,
@ -58,6 +63,7 @@ describe('~/deployments/components/show_deployment.vue', () => {
const findHeader = () => wrapper.findComponent(DeploymentHeader);
const findAlert = () => wrapper.findComponent(GlAlert);
const findApprovalsEmptyState = () => wrapper.findComponent(ApprovalsEmptyState);
describe('errors', () => {
it('shows an error message when the deployment query fails', async () => {
@ -87,6 +93,24 @@ describe('~/deployments/components/show_deployment.vue', () => {
});
});
describe('loading', () => {
beforeEach(() => {
createComponent();
});
it('shows the header component in a loading state', () => {
expect(findHeader().props('loading')).toBe(true);
});
it('shows the aside component in a loading state', () => {
expect(wrapper.findComponent(DeploymentAside).props('loading')).toBe(true);
});
it("doesn't show the approvals empty state", () => {
expect(findApprovalsEmptyState().exists()).toBe(false);
});
});
describe('page', () => {
beforeEach(() => {
deploymentQueryResponse.mockResolvedValue(mockDeploymentFixture);
@ -116,6 +140,10 @@ describe('~/deployments/components/show_deployment.vue', () => {
deployment: mockDeploymentFixture.data.project.deployment,
});
});
it('shows the approvals empty state', () => {
expect(findApprovalsEmptyState().exists()).toBe(true);
});
});
describe('etag polling', () => {

View File

@ -12,7 +12,7 @@ import eventHub from '~/invite_members/event_hub';
import ImportProjectMembersModal from '~/invite_members/components/import_project_members_modal.vue';
import ProjectSelect from '~/invite_members/components/project_select.vue';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_CREATED } from '~/lib/utils/http_status';
import { HTTP_STATUS_CREATED, HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import {
displaySuccessfulInvitationAlert,
@ -54,8 +54,11 @@ const triggerOpenModal = async () => {
await nextTick();
};
const createComponent = ({ props = {} } = {}) => {
const createComponent = ({ props = {}, provide = {} } = {}) => {
wrapper = shallowMountExtended(ImportProjectMembersModal, {
provide: {
...provide,
},
propsData: {
projectId,
projectName,
@ -102,6 +105,8 @@ describe('ImportProjectMembersModal', () => {
const findMoreInviteErrorsButton = () => wrapper.findByTestId('accordion-button');
const findAccordion = () => wrapper.findComponent(GlCollapse);
const findErrorsIcon = () => wrapper.findComponent(GlIcon);
const findSeatOveragesAlert = () =>
wrapper.findByTestId('import-project-members-seat-overages-alert');
const findMemberErrorMessage = (element) =>
`@${Object.keys(importProjectMembersApiResponse.EXPANDED_IMPORT_ERRORS.message)[element]}: ${
Object.values(importProjectMembersApiResponse.EXPANDED_IMPORT_ERRORS.message)[element]
@ -387,5 +392,40 @@ describe('ImportProjectMembersModal', () => {
expect(findMoreInviteErrorsButton().exists()).toBe(false);
});
});
describe('when the import fails due to a seat overage', () => {
const mockInvitationsApi = (code, data) => {
mock.onPost(IMPORT_PROJECT_MEMBERS_PATH).reply(code, data);
};
beforeEach(() => {
createComponent({ provide: { addSeatsHref: 'add_seats_url' } });
findProjectSelect().vm.$emit('input', projectToBeImported);
});
it('clears the error when the modal is closed', async () => {
mockInvitationsApi(
HTTP_STATUS_UNPROCESSABLE_ENTITY,
importProjectMembersApiResponse.SEAT_OVERAGE_IMPORT_ERRORS,
);
clickImportButton();
await waitForPromises();
expect(formGroupInvalidFeedback()).toBe(
'There are not enough available seats to invite this many users.',
);
expect(formGroupErrorState()).toBe(false);
expect(findSeatOveragesAlert().exists()).toBe(true);
closeModal();
await nextTick();
expect(formGroupInvalidFeedback()).toBe('');
expect(formGroupErrorState()).not.toBe(false);
expect(findSeatOveragesAlert().exists()).toBe(false);
});
});
});
});

View File

@ -110,7 +110,12 @@ const NO_COLLAPSE_IMPORT_ERRORS = {
total_members_count: '2',
status: 'error',
};
const SEAT_OVERAGE_IMPORT_ERRORS = {
message: 'There are not enough available seats to invite this many users.',
reason: 'seat_limit_exceeded_error',
};
export const importProjectMembersApiResponse = {
EXPANDED_IMPORT_ERRORS,
NO_COLLAPSE_IMPORT_ERRORS,
SEAT_OVERAGE_IMPORT_ERRORS,
};

View File

@ -3945,6 +3945,7 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq('Import failed')
expect(json_response['reason']).to eq('import_failed_error')
end
context 'when importing of members did not work for some or all members' do
@ -3960,6 +3961,7 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
error_message = { project_bot.username => 'User project bots cannot be added to other groups / projects' }
expect(json_response['message']).to eq(error_message)
expect(json_response['total_members_count']).to eq(3)
expect(json_response['status']).to eq('error')
end
end
end

View File

@ -98,48 +98,6 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_
end
end
context 'with `emails_disabled` attribute' do
context 'when emails_disabled is false' do
let(:extra_params) { { emails_disabled: false } }
it_behaves_like 'creating a group'
it 'sets emails_enabled to true' do
expect(created_group.emails_enabled).to eq(true)
end
end
context 'when emails_disabled is true' do
let(:extra_params) { { emails_disabled: true } }
it_behaves_like 'creating a group'
it 'sets emails_enabled to false' do
expect(created_group.emails_enabled).to eq(false)
end
end
context 'when emails_disabled is nil' do
let(:extra_params) { { emails_disabled: nil } }
it_behaves_like 'creating a group'
it 'sets emails_enabled to default true' do
expect(created_group.emails_enabled).to eq(true)
end
end
context 'when emails_disabled is the string "false"' do
let(:extra_params) { { emails_disabled: "false" } }
it_behaves_like 'creating a group'
it 'sets emails_enabled to false' do
expect(created_group.emails_enabled).to eq(true)
end
end
end
context 'with `allow_mfa_for_subgroups` attribute' do
let(:extra_params) { { allow_mfa_for_subgroups: false } }

View File

@ -390,56 +390,6 @@ RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do
end
end
context 'when updating #emails_disabled' do
context 'when emails_disabled is true' do
let(:service) { described_class.new(internal_group, user, emails_disabled: true) }
it 'updates email_enabled to false' do
internal_group.add_member(user, Gitlab::Access::OWNER)
service.execute
expect(internal_group.emails_enabled).to be(false)
end
end
context 'when emails_disabled is false' do
let(:service) { described_class.new(internal_group, user, emails_disabled: false) }
it 'email_enabled is set to true' do
internal_group.add_member(user, Gitlab::Access::OWNER)
service.execute
expect(internal_group.emails_enabled).to be(true)
end
end
context 'when emails_disabled is nil' do
let(:service) { described_class.new(internal_group, user, emails_disabled: nil) }
it 'email_enabled is set to the default value of true' do
internal_group.add_member(user, Gitlab::Access::OWNER)
service.execute
expect(internal_group.emails_enabled).to be(true)
end
end
context 'when emails_disabled is the string "true"' do
let(:service) { described_class.new(internal_group, user, emails_disabled: "true") }
it 'email_enabled is set to the default value of true' do
internal_group.add_member(user, Gitlab::Access::OWNER)
service.execute
expect(internal_group.emails_enabled).to be(false)
end
end
end
context 'when updating #max_artifacts_size' do
let(:params) { { max_artifacts_size: 10 } }

View File

@ -38,7 +38,7 @@ RSpec.describe Members::ImportProjectTeamService, feature_category: :groups_and_
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Target project does not exist')
expect(result.reason).to eq(:unprocessable_entity)
expect(result.reason).to eq(:argument_error)
end
end
@ -51,7 +51,7 @@ RSpec.describe Members::ImportProjectTeamService, feature_category: :groups_and_
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Source project does not exist')
expect(result.reason).to eq(:unprocessable_entity)
expect(result.reason).to eq(:argument_error)
end
end
@ -64,7 +64,7 @@ RSpec.describe Members::ImportProjectTeamService, feature_category: :groups_and_
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Forbidden')
expect(result.reason).to eq(:unprocessable_entity)
expect(result.reason).to eq(:import_project_team_forbidden_error)
end
end
@ -77,7 +77,7 @@ RSpec.describe Members::ImportProjectTeamService, feature_category: :groups_and_
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Forbidden')
expect(result.reason).to eq(:unprocessable_entity)
expect(result.reason).to eq(:import_project_team_forbidden_error)
end
end
@ -90,7 +90,7 @@ RSpec.describe Members::ImportProjectTeamService, feature_category: :groups_and_
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Forbidden')
expect(result.reason).to eq(:unprocessable_entity)
expect(result.reason).to eq(:import_project_team_forbidden_error)
end
end
@ -107,7 +107,7 @@ RSpec.describe Members::ImportProjectTeamService, feature_category: :groups_and_
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Import failed')
expect(result.reason).to eq(:unprocessable_entity)
expect(result.reason).to eq(:import_failed_error)
end
end