Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f4ad4d1900
commit
5842aa3556
|
|
@ -134,7 +134,7 @@ rubocop:
|
|||
unset CI_SLACK_WEBHOOK_URL
|
||||
run_timed_command "fail_on_warnings bundle exec rake rubocop:check:graceful"
|
||||
else
|
||||
cat "${RSPEC_CHANGED_FILES_PATH}" | ruby -e 'print $stdin.read.split(" ").select { |f| File.exist?(f) }.join(" ")' > "$RUBOCOP_TARGET_FILES"
|
||||
select_existing_files < "${RSPEC_CHANGED_FILES_PATH}" > "${RUBOCOP_TARGET_FILES}"
|
||||
# Skip running RuboCop if there's no target files
|
||||
if [ -s "${RUBOCOP_TARGET_FILES}" ]; then
|
||||
run_timed_command "fail_on_warnings bundle exec rubocop --parallel --force-exclusion $(cat ${RUBOCOP_TARGET_FILES})"
|
||||
|
|
|
|||
|
|
@ -1,26 +1,19 @@
|
|||
---
|
||||
Naming/InclusiveLanguage:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/application_settings/appearances_controller.rb'
|
||||
- 'app/controllers/concerns/requires_whitelisted_monitoring_client.rb'
|
||||
- 'app/controllers/health_check_controller.rb'
|
||||
- 'app/controllers/health_controller.rb'
|
||||
- 'app/controllers/metrics_controller.rb'
|
||||
- 'app/controllers/concerns/requires_allowlisted_monitoring_client.rb'
|
||||
- 'app/helpers/application_settings_helper.rb'
|
||||
- 'app/helpers/blob_helper.rb'
|
||||
- 'app/helpers/markup_helper.rb'
|
||||
- 'app/models/application_setting.rb'
|
||||
- 'app/models/application_setting_implementation.rb'
|
||||
- 'app/models/concerns/cache_markdown_field.rb'
|
||||
- 'app/services/application_settings/update_service.rb'
|
||||
- 'app/services/projects/download_service.rb'
|
||||
- 'app/uploaders/avatar_uploader.rb'
|
||||
- 'app/uploaders/content_type_whitelist.rb'
|
||||
- 'app/uploaders/design_management/design_v432x230_uploader.rb'
|
||||
- 'app/uploaders/favicon_uploader.rb'
|
||||
- 'app/uploaders/gitlab_uploader.rb'
|
||||
- 'app/uploaders/import_export_uploader.rb'
|
||||
- 'app/validators/cron_validator.rb'
|
||||
- 'app/validators/qualified_domain_array_validator.rb'
|
||||
- 'config/initializers/1_settings.rb'
|
||||
- 'config/initializers/doorkeeper.rb'
|
||||
|
|
@ -28,14 +21,12 @@ Naming/InclusiveLanguage:
|
|||
- 'ee/app/controllers/projects/push_rules_controller.rb'
|
||||
- 'ee/lib/arkose/verify_response.rb'
|
||||
- 'ee/lib/system_check/geo/http_connection_check.rb'
|
||||
- 'ee/spec/models/dora/lead_time_for_changes_metric_spec.rb'
|
||||
- 'lib/api/entities/application_setting.rb'
|
||||
- 'lib/api/settings.rb'
|
||||
- 'lib/banzai/filter/asset_proxy_filter.rb'
|
||||
- 'lib/gitlab/asset_proxy.rb'
|
||||
- 'lib/gitlab/auth/ip_rate_limiter.rb'
|
||||
- 'lib/gitlab/ci/config/external/file/base.rb'
|
||||
- 'lib/gitlab/git/hook_env.rb'
|
||||
- 'lib/gitlab/github_import/markdown/attachment.rb'
|
||||
- 'lib/gitlab/markdown_cache/active_record/extension.rb'
|
||||
- 'lib/gitlab/markdown_cache/field_data.rb'
|
||||
|
|
@ -44,9 +35,6 @@ Naming/InclusiveLanguage:
|
|||
- 'lib/gitlab/sanitizers/svg.rb'
|
||||
- 'lib/gitlab/sanitizers/svg/whitelist.rb'
|
||||
- 'lib/system_check/app/git_user_default_ssh_config_check.rb'
|
||||
- 'rubocop/cop/avoid_return_from_blocks.rb'
|
||||
- 'rubocop/cop/graphql/id_type.rb'
|
||||
- 'spec/controllers/concerns/issuable_collections_spec.rb'
|
||||
- 'spec/controllers/health_check_controller_spec.rb'
|
||||
- 'spec/controllers/metrics_controller_spec.rb'
|
||||
- 'spec/helpers/markup_helper_spec.rb'
|
||||
|
|
@ -61,17 +49,7 @@ Naming/InclusiveLanguage:
|
|||
- 'spec/models/application_setting_spec.rb'
|
||||
- 'spec/requests/api/settings_spec.rb'
|
||||
- 'spec/requests/health_controller_spec.rb'
|
||||
- 'spec/rubocop/cop/avoid_return_from_blocks_spec.rb'
|
||||
- 'spec/rubocop/cop/graphql/id_type_spec.rb'
|
||||
- 'spec/services/application_settings/update_service_spec.rb'
|
||||
- 'spec/services/design_management/generate_image_versions_service_spec.rb'
|
||||
- 'spec/services/projects/download_service_spec.rb'
|
||||
- 'spec/support/import_export/export_file_helper.rb'
|
||||
- 'spec/support/shared_contexts/upload_type_check_shared_context.rb'
|
||||
- 'spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/models/application_setting_shared_examples.rb'
|
||||
- 'spec/uploaders/avatar_uploader_spec.rb'
|
||||
- 'spec/uploaders/content_type_whitelist_spec.rb'
|
||||
- 'spec/uploaders/design_management/design_v432x230_uploader_spec.rb'
|
||||
- 'spec/uploaders/favicon_uploader_spec.rb'
|
||||
- 'spec/validators/cron_validator_spec.rb'
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<content-editor-provider :content-editor="contentEditor">
|
||||
<div>
|
||||
<div class="md-area gl-overflow-hidden">
|
||||
<editor-state-observer
|
||||
@docUpdate="notifyChange"
|
||||
@focus="focus"
|
||||
|
|
@ -238,7 +238,6 @@ export default {
|
|||
<div
|
||||
data-testid="content-editor"
|
||||
data-qa-selector="content_editor_container"
|
||||
class="md-area gl-border-none! gl-shadow-none!"
|
||||
:class="{ 'is-focused': focused }"
|
||||
>
|
||||
<formatting-toolbar
|
||||
|
|
@ -263,7 +262,7 @@ export default {
|
|||
<reference-bubble-menu />
|
||||
</div>
|
||||
<div
|
||||
class="gl-display-flex gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base gl-px-2 gl-mx-2 gl-mb-2 gl-bg-gray-10 gl-text-secondary"
|
||||
class="gl-display-flex gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base gl-px-2 gl-border-t gl-border-gray-100 gl-text-secondary"
|
||||
>
|
||||
<editor-mode-switcher size="small" value="richText" @input="handleEditorModeChanged" />
|
||||
<gl-button
|
||||
|
|
@ -274,6 +273,7 @@ export default {
|
|||
category="tertiary"
|
||||
size="small"
|
||||
title="Markdown is supported"
|
||||
class="gl-px-3!"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -72,125 +72,123 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="gl-mx-2 gl-mt-2">
|
||||
<div
|
||||
class="gl-w-full gl-display-flex gl-align-items-center gl-flex-wrap gl-bg-gray-50 gl-px-2 gl-rounded-base gl-justify-content-space-between"
|
||||
data-testid="formatting-toolbar"
|
||||
>
|
||||
<div class="gl-py-2 gl-display-flex gl-flex-wrap">
|
||||
<toolbar-text-style-dropdown
|
||||
data-testid="text-styles"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="codeSuggestionsEnabled"
|
||||
data-testid="code-suggestion"
|
||||
content-type="codeSuggestion"
|
||||
icon-name="doc-code"
|
||||
editor-command="insertCodeSuggestion"
|
||||
:label="__('Insert suggestion')"
|
||||
:show-active-state="false"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="bold"
|
||||
content-type="bold"
|
||||
icon-name="bold"
|
||||
editor-command="toggleBold"
|
||||
:label="i18n.bold"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="italic"
|
||||
content-type="italic"
|
||||
icon-name="italic"
|
||||
editor-command="toggleItalic"
|
||||
:label="i18n.italic"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="strike"
|
||||
content-type="strike"
|
||||
icon-name="strikethrough"
|
||||
editor-command="toggleStrike"
|
||||
:label="i18n.strike"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="blockquote"
|
||||
content-type="blockquote"
|
||||
icon-name="quote"
|
||||
editor-command="toggleBlockquote"
|
||||
:label="i18n.quote"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="code"
|
||||
content-type="code"
|
||||
icon-name="code"
|
||||
editor-command="toggleCode"
|
||||
:label="i18n.code"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="link"
|
||||
content-type="link"
|
||||
icon-name="link"
|
||||
editor-command="editLink"
|
||||
:label="i18n.link"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="bullet-list"
|
||||
content-type="bulletList"
|
||||
icon-name="list-bulleted"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="toggleBulletList"
|
||||
:label="i18n.bulletList"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="ordered-list"
|
||||
content-type="orderedList"
|
||||
icon-name="list-numbered"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="toggleOrderedList"
|
||||
:label="i18n.numberedList"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="task-list"
|
||||
content-type="taskList"
|
||||
icon-name="list-task"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="toggleTaskList"
|
||||
:label="i18n.taskList"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-table-button data-testid="table" @execute="trackToolbarControlExecution" />
|
||||
<toolbar-attachment-button
|
||||
v-if="!hideAttachmentButton"
|
||||
data-testid="attachment"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<!-- TODO Add icon and trigger functionality from here -->
|
||||
<toolbar-button
|
||||
v-if="supportsQuickActions"
|
||||
data-testid="quick-actions"
|
||||
content-type="quickAction"
|
||||
icon-name="quick-actions"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="insertQuickAction"
|
||||
:label="__('Add a quick action')"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<comment-templates-dropdown
|
||||
v-if="newCommentTemplatePath"
|
||||
:new-comment-template-path="newCommentTemplatePath"
|
||||
@select="insertSavedReply"
|
||||
/>
|
||||
<toolbar-more-dropdown data-testid="more" @execute="trackToolbarControlExecution" />
|
||||
</div>
|
||||
<div
|
||||
class="gl-w-full gl-display-flex gl-align-items-center gl-flex-wrap gl-border-b gl-border-gray-100 gl-px-3 gl-rounded-top-base gl-justify-content-space-between"
|
||||
data-testid="formatting-toolbar"
|
||||
>
|
||||
<div class="gl-py-3 gl-display-flex gl-flex-wrap">
|
||||
<toolbar-text-style-dropdown
|
||||
data-testid="text-styles"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
v-if="codeSuggestionsEnabled"
|
||||
data-testid="code-suggestion"
|
||||
content-type="codeSuggestion"
|
||||
icon-name="doc-code"
|
||||
editor-command="insertCodeSuggestion"
|
||||
:label="__('Insert suggestion')"
|
||||
:show-active-state="false"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="bold"
|
||||
content-type="bold"
|
||||
icon-name="bold"
|
||||
editor-command="toggleBold"
|
||||
:label="i18n.bold"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="italic"
|
||||
content-type="italic"
|
||||
icon-name="italic"
|
||||
editor-command="toggleItalic"
|
||||
:label="i18n.italic"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="strike"
|
||||
content-type="strike"
|
||||
icon-name="strikethrough"
|
||||
editor-command="toggleStrike"
|
||||
:label="i18n.strike"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="blockquote"
|
||||
content-type="blockquote"
|
||||
icon-name="quote"
|
||||
editor-command="toggleBlockquote"
|
||||
:label="i18n.quote"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="code"
|
||||
content-type="code"
|
||||
icon-name="code"
|
||||
editor-command="toggleCode"
|
||||
:label="i18n.code"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="link"
|
||||
content-type="link"
|
||||
icon-name="link"
|
||||
editor-command="editLink"
|
||||
:label="i18n.link"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="bullet-list"
|
||||
content-type="bulletList"
|
||||
icon-name="list-bulleted"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="toggleBulletList"
|
||||
:label="i18n.bulletList"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="ordered-list"
|
||||
content-type="orderedList"
|
||||
icon-name="list-numbered"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="toggleOrderedList"
|
||||
:label="i18n.numberedList"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-button
|
||||
data-testid="task-list"
|
||||
content-type="taskList"
|
||||
icon-name="list-task"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="toggleTaskList"
|
||||
:label="i18n.taskList"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-table-button data-testid="table" @execute="trackToolbarControlExecution" />
|
||||
<toolbar-attachment-button
|
||||
v-if="!hideAttachmentButton"
|
||||
data-testid="attachment"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<!-- TODO Add icon and trigger functionality from here -->
|
||||
<toolbar-button
|
||||
v-if="supportsQuickActions"
|
||||
data-testid="quick-actions"
|
||||
content-type="quickAction"
|
||||
icon-name="quick-actions"
|
||||
class="gl-display-none gl-sm-display-inline"
|
||||
editor-command="insertQuickAction"
|
||||
:label="__('Add a quick action')"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<comment-templates-dropdown
|
||||
v-if="newCommentTemplatePath"
|
||||
:new-comment-template-path="newCommentTemplatePath"
|
||||
@select="insertSavedReply"
|
||||
/>
|
||||
<toolbar-more-dropdown data-testid="more" @execute="trackToolbarControlExecution" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -74,11 +74,6 @@ import trackInputRulesAndShortcuts from './track_input_rules_and_shortcuts';
|
|||
const createTiptapEditor = ({ extensions = [], ...options } = {}) =>
|
||||
new Editor({
|
||||
extensions: [...extensions],
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'gl-shadow-none!',
|
||||
},
|
||||
},
|
||||
...options,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -76,10 +76,10 @@ export default {
|
|||
<h3 id="related-merge-requests" class="gl-new-card-title">
|
||||
{{ __('Related merge requests') }}
|
||||
</h3>
|
||||
<div class="gl-display-inline-flex gl-align-items-center gl-m-0">
|
||||
<div class="gl-new-card-count">
|
||||
<template v-if="totalCount">
|
||||
<gl-icon name="merge-request" class="gl-ml-3 gl-mr-2 gl-text-gray-500" />
|
||||
<span data-testid="count" class="gl-text-gray-500">{{ totalCount }}</span>
|
||||
<gl-icon name="merge-request" class="gl-mr-2" />
|
||||
<span data-testid="count">{{ totalCount }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ export default {
|
|||
<template #textarea>
|
||||
<textarea
|
||||
v-model="timelineText"
|
||||
class="note-textarea js-gfm-input js-autosize markdown-area"
|
||||
class="note-textarea note-textarea-rounded-bottom js-gfm-input js-autosize markdown-area gl-bordered"
|
||||
data-testid="input-note"
|
||||
dir="auto"
|
||||
data-supports-quick-actions="false"
|
||||
|
|
|
|||
|
|
@ -66,9 +66,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="comment-warning-wrapper gl-border-solid gl-border-1 gl-rounded-lg gl-border-gray-100 gl-bg-white gl-overflow-hidden"
|
||||
>
|
||||
<div class="comment-warning-wrapper">
|
||||
<div
|
||||
v-if="withAlertContainer"
|
||||
class="error-alert"
|
||||
|
|
@ -76,7 +74,7 @@ export default {
|
|||
></div>
|
||||
<noteable-warning
|
||||
v-if="hasWarning"
|
||||
class="gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100 gl-rounded-base gl-rounded-bottom-left-none gl-rounded-bottom-right-none"
|
||||
class="gl-pt-4 gl-pb-5 gl-mb-n3 gl-rounded-lg gl-rounded-bottom-left-none gl-rounded-bottom-right-none"
|
||||
:is-locked="isLocked"
|
||||
:is-confidential="isConfidential"
|
||||
:noteable-type="noteableType"
|
||||
|
|
@ -84,10 +82,20 @@ export default {
|
|||
:confidential-noteable-docs-path="noteableData.confidential_issues_docs_path"
|
||||
/>
|
||||
<slot></slot>
|
||||
<attachments-warning v-if="showAttachmentWarning" />
|
||||
<attachments-warning
|
||||
v-if="showAttachmentWarning"
|
||||
:class="{
|
||||
'gl-py-3': !showEmailParticipantsWarning,
|
||||
'gl-pt-4 gl-pb-3 gl-mt-n3': showEmailParticipantsWarning,
|
||||
}"
|
||||
/>
|
||||
<email-participants-warning
|
||||
v-if="showEmailParticipantsWarning"
|
||||
class="gl-border-t-1 gl-border-t-solid gl-border-t-gray-100 gl-rounded-base gl-rounded-top-left-none! gl-rounded-top-right-none!"
|
||||
class="gl-border-t-1 gl-rounded-lg gl-rounded-top-left-none! gl-rounded-top-right-none!"
|
||||
:class="{
|
||||
'gl-pt-4 gl-pb-3 gl-mt-n3': !showAttachmentWarning,
|
||||
'gl-py-3 gl-mt-1': showAttachmentWarning,
|
||||
}"
|
||||
:emails="emailParticipants"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -336,7 +336,7 @@ export default {
|
|||
<li
|
||||
v-else-if="canShowReplyActions && showReplies"
|
||||
data-testid="reply-wrapper"
|
||||
class="discussion-reply-holder gl-border-t-0! clearfix"
|
||||
class="discussion-reply-holder gl-border-t-0! gl-pb-5! clearfix"
|
||||
:class="discussionHolderClass"
|
||||
>
|
||||
<discussion-actions
|
||||
|
|
|
|||
|
|
@ -200,13 +200,9 @@ export default {
|
|||
/>
|
||||
<slot name="header-text">{{ headerText }}</slot>
|
||||
</h3>
|
||||
<div
|
||||
class="gl-new-card-count js-related-issues-header-issue-count gl-display-inline-flex gl-mx-3 gl-text-gray-500"
|
||||
>
|
||||
<span class="gl-display-inline-flex gl-align-items-center">
|
||||
<gl-icon :name="issuableTypeIcon" class="gl-mr-2 gl-text-gray-500" />
|
||||
{{ badgeLabel }}
|
||||
</span>
|
||||
<div class="gl-new-card-count js-related-issues-header-issue-count">
|
||||
<gl-icon :name="issuableTypeIcon" class="gl-mr-2" />
|
||||
{{ badgeLabel }}
|
||||
</div>
|
||||
</div>
|
||||
<slot name="header-actions"></slot>
|
||||
|
|
@ -220,7 +216,7 @@ export default {
|
|||
>
|
||||
<slot name="add-button-text">{{ __('Add') }}</slot>
|
||||
</gl-button>
|
||||
<div class="gl-pl-3 gl-ml-3 gl-border-l-1 gl-border-l-solid gl-border-l-gray-100">
|
||||
<div class="gl-new-card-toggle">
|
||||
<gl-button
|
||||
category="tertiary"
|
||||
size="small"
|
||||
|
|
@ -282,7 +278,7 @@ export default {
|
|||
@saveReorder="$emit('saveReorder', $event)"
|
||||
/>
|
||||
</template>
|
||||
<div v-if="!shouldShowTokenBody && !isFormVisible" data-testid="related-items-empty">
|
||||
<div v-if="!shouldShowTokenBody && !isFormVisible">
|
||||
<p class="gl-new-card-empty">
|
||||
{{ emptyStateMessage }}
|
||||
<gl-link
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ export default {
|
|||
:default-toggle-text="$options.i18n.toggleText"
|
||||
:fetch-items="fetchGroups"
|
||||
:fetch-initial-selection-text="fetchGroupName"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template #error>
|
||||
<gl-alert v-if="errorMessage" class="gl-mb-3" variant="danger" @dismiss="dismissError">{{
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ export default {
|
|||
:fetch-initial-selection-text="fetchProjectName"
|
||||
:block="block"
|
||||
clearable
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template v-if="hasHtmlLabel" #label>
|
||||
<span v-safe-html="label"></span>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default {
|
|||
<template>
|
||||
<div class="content-editor-switcher gl-display-inline-flex gl-align-items-center">
|
||||
<gl-button
|
||||
class="btn btn-default btn-sm gl-button btn-default-tertiary"
|
||||
class="btn btn-default btn-sm gl-button btn-default-tertiary gl-font-sm! gl-text-secondary! gl-px-4!"
|
||||
data-qa-selector="editing_mode_switcher"
|
||||
@click="$emit('input')"
|
||||
>{{ text }}</gl-button
|
||||
|
|
|
|||
|
|
@ -355,10 +355,7 @@ export default {
|
|||
<template>
|
||||
<div
|
||||
ref="gl-form"
|
||||
:class="{
|
||||
'gl-border-none! gl-shadow-none!': removeBorder,
|
||||
}"
|
||||
class="js-vue-markdown-field md-area position-relative gfm-form"
|
||||
class="js-vue-markdown-field md-area position-relative gfm-form gl-overflow-hidden"
|
||||
:data-uploads-path="uploadsPath"
|
||||
>
|
||||
<markdown-header
|
||||
|
|
|
|||
|
|
@ -257,11 +257,11 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="md-header gl-bg-gray-50 gl-px-2 gl-rounded-base gl-mx-2 gl-mt-2">
|
||||
<div class="md-header gl-border-b gl-border-gray-100 gl-px-3">
|
||||
<div class="gl-display-flex gl-align-items-center gl-flex-wrap">
|
||||
<div
|
||||
data-testid="md-header-toolbar"
|
||||
class="md-header-toolbar gl-display-flex gl-py-2 gl-flex-wrap gl-row-gap-3"
|
||||
class="md-header-toolbar gl-display-flex gl-py-3 gl-flex-wrap gl-row-gap-3"
|
||||
>
|
||||
<gl-button
|
||||
v-if="enablePreview"
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="md-area gl-px-0! gl-overflow-hidden">
|
||||
<div class="gl-px-0!">
|
||||
<local-storage-sync
|
||||
:value="editingMode"
|
||||
as-string
|
||||
|
|
|
|||
|
|
@ -50,11 +50,11 @@ export default {
|
|||
<template>
|
||||
<div
|
||||
v-if="showCommentToolBar"
|
||||
class="comment-toolbar gl-display-flex gl-flex-direction-row gl-mx-2 gl-mb-2 gl-px-2 gl-rounded-bottom-left-base gl-rounded-bottom-right-base"
|
||||
class="comment-toolbar gl-display-flex gl-flex-direction-row gl-px-2 gl-rounded-bottom-left-base gl-rounded-bottom-right-base"
|
||||
:class="
|
||||
showContentEditorSwitcher
|
||||
? 'gl-bg-gray-10 gl-justify-content-space-between'
|
||||
: 'gl-justify-content-end'
|
||||
? 'gl-justify-content-space-between gl-align-items-center gl-border-t gl-border-gray-100'
|
||||
: 'gl-justify-content-end gl-my-2'
|
||||
"
|
||||
>
|
||||
<editor-mode-switcher
|
||||
|
|
@ -63,21 +63,8 @@ export default {
|
|||
value="markdown"
|
||||
@input="handleEditorModeChanged"
|
||||
/>
|
||||
<div>
|
||||
<div class="toolbar-text gl-font-sm">
|
||||
<template v-if="markdownDocsPath">
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
icon="markdown-mark"
|
||||
:href="markdownDocsPath"
|
||||
target="_blank"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
title="Markdown is supported"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<span v-if="canAttachFile" class="uploading-container gl-font-sm gl-line-height-32">
|
||||
<div class="gl-display-flex">
|
||||
<div v-if="canAttachFile" class="uploading-container gl-font-sm gl-line-height-32 gl-mr-3">
|
||||
<span class="uploading-progress-container hide">
|
||||
<gl-icon name="paperclip" />
|
||||
<span class="attaching-file-message"></span>
|
||||
|
|
@ -125,7 +112,18 @@ export default {
|
|||
>
|
||||
{{ __('Cancel') }}
|
||||
</gl-button>
|
||||
</span>
|
||||
</div>
|
||||
<gl-button
|
||||
v-if="markdownDocsPath"
|
||||
v-gl-tooltip
|
||||
icon="markdown-mark"
|
||||
:href="markdownDocsPath"
|
||||
target="_blank"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
title="Markdown is supported"
|
||||
class="gl-px-3!"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -205,10 +205,7 @@ export default {
|
|||
>
|
||||
<template #header>{{ $options.i18n.title }}</template>
|
||||
<template #header-suffix>
|
||||
<span
|
||||
class="gl-display-inline-flex gl-align-items-center gl-line-height-24 gl-ml-3 gl-font-weight-bold gl-text-gray-500"
|
||||
data-testid="children-count"
|
||||
>
|
||||
<span class="gl-new-card-count" data-testid="children-count">
|
||||
<gl-icon :name="$options.WIDGET_TYPE_TASK_ICON" class="gl-mr-2" />
|
||||
{{ childrenCountLabel }}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,16 @@
|
|||
max-height: 55vh;
|
||||
position: static;
|
||||
overflow-y: auto;
|
||||
transition: box-shadow ease-in-out 0.15s;
|
||||
|
||||
::selection {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include gl-focus($inset: true);
|
||||
}
|
||||
|
||||
&:not(.ProseMirror-hideselection) .content-editor-selection,
|
||||
a.ProseMirror-selectednode,
|
||||
span.ProseMirror-selectednode {
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@
|
|||
}
|
||||
|
||||
.md-preview-holder {
|
||||
min-height: 176px;
|
||||
min-height: 173px;
|
||||
padding: 10px 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
|
@ -106,6 +106,7 @@
|
|||
box-shadow: none;
|
||||
width: 100%;
|
||||
resize: none !important;
|
||||
transition: box-shadow $gl-transition-duration-medium ease;
|
||||
}
|
||||
|
||||
.md-suggestion-diff {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
@include gl-rounded-base;
|
||||
|
||||
&-header {
|
||||
@include gl-pl-5;
|
||||
@include gl-pr-4;
|
||||
@include gl-px-5;
|
||||
@include gl-py-4;
|
||||
@include gl-display-flex;
|
||||
@include gl-justify-content-space-between;
|
||||
|
|
@ -42,6 +41,8 @@
|
|||
@include gl-font-base;
|
||||
@include gl-font-weight-bold;
|
||||
@include gl-text-gray-500;
|
||||
@include gl-display-inline-flex;
|
||||
@include gl-align-items-center;
|
||||
}
|
||||
|
||||
&-description {
|
||||
|
|
@ -53,6 +54,7 @@
|
|||
&-toggle {
|
||||
@include gl-pl-3;
|
||||
@include gl-ml-3;
|
||||
@include gl-mr-n2;
|
||||
@include gl-border-l-1;
|
||||
@include gl-border-l-solid;
|
||||
@include gl-border-l-gray-100;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
.common-note-form {
|
||||
.md-area {
|
||||
border: 1px solid $border-color;
|
||||
border: 1px solid $gray-400;
|
||||
border-radius: $border-radius-large;
|
||||
transition: border-color ease-in-out 0.15s,
|
||||
box-shadow ease-in-out 0.15s;
|
||||
|
|
@ -65,19 +65,34 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Disable inner focus
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
@include gl-shadow-md;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $gray-500;
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border: 1px solid $gray-900;
|
||||
}
|
||||
|
||||
// Add focus
|
||||
textarea:focus {
|
||||
@include gl-shadow-none;
|
||||
@include gl-focus($inset: true);
|
||||
}
|
||||
|
||||
.note-textarea-rounded-bottom {
|
||||
border-bottom-left-radius: calc(#{$border-radius-large} - 1px);
|
||||
border-bottom-right-radius: calc(#{$border-radius-large} - 1px);
|
||||
}
|
||||
|
||||
// Disable inner focus on fullscreen
|
||||
.zen-backdrop.fullscreen textarea:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-warning-wrapper:focus-within {
|
||||
@include gl-focus;
|
||||
}
|
||||
}
|
||||
|
||||
.md-area:focus-within {
|
||||
@include gl-focus;
|
||||
}
|
||||
|
||||
.md-header {
|
||||
|
|
@ -217,6 +232,7 @@ table {
|
|||
|
||||
.md-area {
|
||||
background-color: $white;
|
||||
@include gl-rounded-base;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -309,13 +325,19 @@ table {
|
|||
resize: none;
|
||||
padding: $gl-padding-8 $gl-padding-12;
|
||||
line-height: 1;
|
||||
border: 1px solid $border-color;
|
||||
border: 1px solid $gray-200;
|
||||
background-color: $white;
|
||||
overflow: hidden;
|
||||
transition: border-color ease-in-out 0.15s,
|
||||
box-shadow ease-in-out 0.15s;
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
margin-bottom: $gl-padding-8;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $gray-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -436,9 +458,4 @@ table {
|
|||
.comment-warning-wrapper {
|
||||
transition: border-color ease-in-out 0.15s,
|
||||
box-shadow ease-in-out 0.15s;
|
||||
|
||||
.md-area {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class Admin::ApplicationSettings::AppearancesController < Admin::ApplicationCont
|
|||
@appearance = Appearance.current || Appearance.new
|
||||
end
|
||||
|
||||
# Only allow a trusted parameter "white list" through.
|
||||
# Only allow a trusted parameter "allow list" through.
|
||||
def appearance_params
|
||||
params.require(:appearance).permit(allowed_appearance_params)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,26 +5,23 @@ module Observability
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
content_security_policy_with_context do |p|
|
||||
current_group = if defined?(group)
|
||||
group
|
||||
else
|
||||
defined?(project) ? project&.group : nil
|
||||
end
|
||||
|
||||
next if p.directives.blank? || !Feature.enabled?(:observability_group_tab, current_group)
|
||||
content_security_policy do |p|
|
||||
next if p.directives.blank?
|
||||
|
||||
default_frame_src = p.directives['frame-src'] || p.directives['default-src']
|
||||
|
||||
# When ObservabilityUI is not authenticated, it needs to be able
|
||||
# to redirect to the GL sign-in page, hence '/users/sign_in' and '/oauth/authorize'
|
||||
# When Gitlab Observability Backend is not authenticated, it needs to be able
|
||||
# to redirect to the GitLab sign-in page, hence '/users/sign_in' and '/oauth/authorize'
|
||||
frame_src_values = Array.wrap(default_frame_src) | [
|
||||
Gitlab::Observability.observability_url,
|
||||
Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/users/sign_in'),
|
||||
Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/oauth/authorize')
|
||||
]
|
||||
|
||||
p.frame_src(*frame_src_values)
|
||||
|
||||
default_connect_src = p.directives['connect-src'] || p.directives['default-src']
|
||||
connect_src_values =
|
||||
Array.wrap(default_connect_src) | [Gitlab::Observability.observability_url]
|
||||
p.connect_src(*connect_src_values)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RequiresWhitelistedMonitoringClient
|
||||
module RequiresAllowlistedMonitoringClient
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action :validate_ip_whitelisted_or_valid_token!
|
||||
before_action :validate_ip_allowlisted_or_valid_token!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_ip_whitelisted_or_valid_token!
|
||||
render_404 unless client_ip_whitelisted? || valid_token?
|
||||
def validate_ip_allowlisted_or_valid_token!
|
||||
render_404 unless client_ip_allowlisted? || valid_token?
|
||||
end
|
||||
|
||||
def client_ip_whitelisted?
|
||||
def client_ip_allowlisted?
|
||||
# Always allow developers to access http://localhost:3000/-/metrics for
|
||||
# debugging purposes
|
||||
return true if Rails.env.development? && request.local?
|
||||
|
||||
ip_whitelist.any? { |e| e.include?(Gitlab::RequestContext.instance.client_ip) }
|
||||
ip_allowlist.any? { |e| e.include?(Gitlab::RequestContext.instance.client_ip) }
|
||||
end
|
||||
|
||||
def ip_whitelist
|
||||
@ip_whitelist ||= Settings.monitoring.ip_whitelist.map { |ip| IPAddr.new(ip) }
|
||||
def ip_allowlist
|
||||
@ip_allowlist ||= Settings.monitoring.ip_whitelist.map { |ip| IPAddr.new(ip) }
|
||||
end
|
||||
|
||||
def valid_token?
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HealthCheckController < HealthCheck::HealthCheckController
|
||||
include RequiresWhitelistedMonitoringClient
|
||||
include RequiresAllowlistedMonitoringClient
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# rubocop:disable Rails/ApplicationController
|
||||
class HealthController < ActionController::Base
|
||||
protect_from_forgery with: :exception, prepend: true
|
||||
include RequiresWhitelistedMonitoringClient
|
||||
include RequiresAllowlistedMonitoringClient
|
||||
|
||||
CHECKS = [
|
||||
Gitlab::HealthChecks::MasterCheck
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# rubocop:disable Rails/ApplicationController
|
||||
class MetricsController < ActionController::Base
|
||||
include RequiresWhitelistedMonitoringClient
|
||||
include RequiresAllowlistedMonitoringClient
|
||||
|
||||
protect_from_forgery with: :exception, prepend: true
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Projects
|
||||
class TracingController < Projects::ApplicationController
|
||||
include ::Observability::ContentSecurityPolicy
|
||||
|
||||
feature_category :tracing
|
||||
|
||||
before_action :check_tracing_enabled
|
||||
|
||||
def index
|
||||
# TODO frontend changes coming separately https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125014
|
||||
render html: helpers.tag.strong('Tracing')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_tracing_enabled
|
||||
render_404 unless Gitlab::Observability.tracing_enabled?(project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -118,8 +118,8 @@ module BlobHelper
|
|||
"#{blob_raw_path.rpartition('/').first}/"
|
||||
end
|
||||
|
||||
# SVGs can contain malicious JavaScript; only include whitelisted
|
||||
# elements and attributes. Note that this whitelist is by no means complete
|
||||
# SVGs can contain malicious JavaScript; only include allowlisted
|
||||
# elements and attributes. Note that this allowlist is by no means complete
|
||||
# and may omit some elements.
|
||||
def sanitize_svg_data(data)
|
||||
Gitlab::Sanitizers::SVG.clean(data)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Projects
|
||||
class DownloadService < BaseService
|
||||
WHITELIST = [
|
||||
ALLOWLIST = [
|
||||
/^[^.]+\.fogbugz.com$/
|
||||
].freeze
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ module Projects
|
|||
|
||||
def valid_domain?(url)
|
||||
host = URI.parse(url).host
|
||||
WHITELIST.any? { |entry| entry === host }
|
||||
ALLOWLIST.any? { |entry| entry === host }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CronValidator < ActiveModel::EachValidator
|
||||
ATTRIBUTE_WHITELIST = %i[cron freeze_start freeze_end].freeze
|
||||
ATTRIBUTE_ALLOWLIST = %i[cron freeze_start freeze_end].freeze
|
||||
|
||||
NonWhitelistedAttributeError = Class.new(StandardError)
|
||||
NonAllowlistedAttributeError = Class.new(StandardError)
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
if ATTRIBUTE_WHITELIST.include?(attribute)
|
||||
if ATTRIBUTE_ALLOWLIST.include?(attribute)
|
||||
cron_parser = Gitlab::Ci::CronParser.new(record.public_send(attribute), record.cron_timezone) # rubocop:disable GitlabSecurity/PublicSend
|
||||
record.errors.add(attribute, " is invalid syntax") unless cron_parser.cron_valid?
|
||||
else
|
||||
raise NonWhitelistedAttributeError, "Non-whitelisted attribute"
|
||||
raise NonAllowlistedAttributeError, "Non-allowlisted attribute"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
= _('Only project members can comment.')
|
||||
|
||||
.md-area.position-relative
|
||||
.md-header.gl-bg-gray-50.gl-px-2.gl-rounded-base.gl-mx-2.gl-mt-2
|
||||
.md-header.gl-px-3.gl-rounded-top-base.gl-border-b.gl-border-gray-100
|
||||
.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-justify-content-space-between
|
||||
.md-header-toolbar.gl-display-flex.gl-py-2.gl-flex-wrap.gl-row-gap-3
|
||||
.md-header-toolbar.gl-display-flex.gl-py-3.gl-flex-wrap.gl-row-gap-3
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, button_options: { class: 'js-md-preview-button', value: 'preview' }) do
|
||||
= _('Preview')
|
||||
= render 'shared/blob/markdown_buttons', supports_quick_actions: supports_quick_actions
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- supports_file_upload = local_assigns.fetch(:supports_file_upload, true)
|
||||
.comment-toolbar.gl-mx-2.gl-mb-2.gl-px-2.gl-display-flex.gl-justify-content-end.gl-rounded-bottom-left-base.gl-rounded-bottom-right-base.clearfix
|
||||
.toolbar-text
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, icon: 'markdown-mark', size: :small, href: help_page_path('user/markdown'), target: '_blank')
|
||||
.comment-toolbar.gl-px-2.gl-display-flex.gl-justify-content-end.gl-rounded-bottom-left-base.gl-rounded-bottom-right-base.clearfix
|
||||
.content-editor-switcher.gl-display-inline-flex.gl-align-items-center
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, icon: 'markdown-mark', size: :small, href: help_page_path('user/markdown'), target: '_blank', button_options: { class: 'gl-px-3!' })
|
||||
- if supports_file_upload
|
||||
%span.uploading-container.gl-line-height-32.gl-font-sm
|
||||
%span.uploading-progress-container.hide
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: observability_tracing
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124966
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2252
|
||||
milestone: '16.2'
|
||||
type: development
|
||||
group: group::observability
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: packages_dependency_proxy_maven
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123491
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/415218
|
||||
milestone: '16.2'
|
||||
type: development
|
||||
group: group::package registry
|
||||
default_enabled: false
|
||||
|
|
@ -404,6 +404,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
end
|
||||
end
|
||||
|
||||
resources :tracing, only: [:index], controller: :tracing
|
||||
|
||||
namespace :design_management do
|
||||
namespace :designs, path: 'designs/:design_id(/:sha)', constraints: -> (params) { params[:sha].nil? || Gitlab::Git.commit_id?(params[:sha]) } do
|
||||
resource :raw_image, only: :show
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ table_name: dependency_proxy_packages_settings
|
|||
classes:
|
||||
- DependencyProxy::Packages::Setting
|
||||
feature_categories:
|
||||
- dependency_proxy
|
||||
- package_registry
|
||||
description: Settings for the dependency proxy for packages.
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120894
|
||||
milestone: '16.1'
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ Here's a list of where we're using this right now, and should try to move away
|
|||
from using `$FORCE_GITLAB_CI`.
|
||||
|
||||
- [JiHu validation pipeline](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/jihu-validation-pipelines.html)
|
||||
- [Gitaly downstream GitLab pipeline](https://gitlab.com/gitlab-org/gitaly/-/issues/4615)
|
||||
|
||||
## Default image
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,11 @@ To view a list of seats being used:
|
|||
1. Select **Settings > Usage Quotas**.
|
||||
1. On the **Seats** tab, view usage information.
|
||||
|
||||
For each user, a list shows groups and projects where the user is a direct member.
|
||||
|
||||
- **Group invite** indicates the user is a member of a [group shared with a group](../../user/group/manage.md#share-a-group-with-another-group).
|
||||
- **Project invite** indicates the user is a member of a [group shared with a project](../../user/project/members/share_project_with_groups.md#share-a-project-with-a-group).
|
||||
|
||||
The data in seat usage listing, **Seats in use**, and **Seats in subscription** are updated live.
|
||||
The counts for **Max seats used** and **Seats owed** are updated once per day.
|
||||
|
||||
|
|
|
|||
|
|
@ -312,6 +312,8 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
|
|||
- Geo: Some project imports do not initialize wiki repositories on project creation. Since the migration of project wikis to SSF, [missing wiki repositories are being incorrectly flagged as failing verification](https://gitlab.com/gitlab-org/gitlab/-/issues/409704). This is not a result of an actual replication/verification failure but an invalid internal state for these missing repositories inside Geo and results in errors in the logs and the verification progress reporting a failed state for these wiki repositories. If you have not imported projects you are not impacted by this issue.
|
||||
- Impacted versions: GitLab versions 15.11.x, 16.0.x, and 16.1.0 - 16.1.2.
|
||||
- Versions containing fix: GitLab 16.1.3 and later.
|
||||
- Starting with 16.0, GitLab self-managed installations now have two database connections by default, instead of one. This change doubles the number of PostgreSQL connections. It makes self-managed versions of GitLab behave similarly to GitLab.com, and is a step toward enabling a separate database for CI features for self-managed versions of GitLab. Before upgrading to 16.0, determine if you need to [increase max connections for PostgreSQL](https://docs.gitlab.com/omnibus/settings/database.html#configuring-multiple-database-connections).
|
||||
- This change applies to installation methods with Linux packages (Omnibus GitLab), GitLab Helm chart, GitLab Operator, GitLab Docker images, and installation from source.
|
||||
|
||||
### 15.11.1
|
||||
|
||||
|
|
|
|||
|
|
@ -114,11 +114,13 @@ Admin Mode times out after six hours, and you cannot change this timeout limit.
|
|||
The following access methods are **not** protected by Admin Mode:
|
||||
|
||||
- Git client access (SSH using public keys or HTTPS using Personal Access Tokens).
|
||||
- API access using a Personal Access Token.
|
||||
|
||||
In other words, administrators who are otherwise limited by Admin Mode can still use
|
||||
Git clients, and access RESTful API endpoints as administrators, without additional
|
||||
authentication steps.
|
||||
Git clients without additional authentication steps.
|
||||
|
||||
To use the GitLab REST- or GraphQL API, administrators must [create a personal access token](../../profile/personal_access_tokens.md#create-a-personal-access-token) with the [`admin_mode` scope](../../profile/personal_access_tokens.md#personal-access-token-scopes).
|
||||
|
||||
If an administrator with a personal access token with the `admin_mode` scope loses their administrator access, that user cannot access the API as an administrator even though they still have the token with the `admin_mode` scope.
|
||||
|
||||
We may address these limitations in the future. For more information see the following epic:
|
||||
[Admin Mode for GitLab Administrators](https://gitlab.com/groups/gitlab-org/-/epics/2158).
|
||||
|
|
|
|||
|
|
@ -635,7 +635,6 @@ The following variables allow configuration of global dependency scanning settin
|
|||
| `DS_IMAGE_SUFFIX` | Suffix added to the image name. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354796) in GitLab 14.10.) Automatically set to `"-fips"` when FIPS mode is enabled. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357922) in GitLab 15.0.) |
|
||||
| `DS_MAX_DEPTH` | Defines how many directory levels deep that the analyzer should search for supported files to scan. A value of `-1` scans all directories regardless of depth. Default: `2`. |
|
||||
| `SECURE_ANALYZERS_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). |
|
||||
| `SECURE_LOG_LEVEL` | Set the minimum logging level. Messages of this logging level or higher are output. From highest to lowest severity, the logging levels are: `fatal`, `error`, `warn`, `info` (default), `debug`. |
|
||||
|
||||
#### Configuring specific analyzers used by dependency scanning
|
||||
|
||||
|
|
@ -1136,12 +1135,10 @@ version number).
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
### Increase log verbosity
|
||||
### Debug-level logging
|
||||
|
||||
When a [job log](../../../ci/jobs/index.md#expand-and-collapse-job-log-sections)
|
||||
doesn't contain enough information about a dependency-scanning failure,
|
||||
[set `SECURE_LOG_LEVEL` to `debug`](#configuring-dependency-scanning)
|
||||
and check the resulting, more verbose log.
|
||||
Debug-level logging can help when troubleshooting. For details, see
|
||||
[debug-level logging](../index.md#debug-level-logging).
|
||||
|
||||
### Working around missing support for certain languages or package managers
|
||||
|
||||
|
|
|
|||
|
|
@ -270,15 +270,10 @@ pipelines tab on merge requests by [setting `artifacts: paths`](../../../ci/yaml
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
### IaC debug logging
|
||||
### Debug-level logging
|
||||
|
||||
To help troubleshoot IaC jobs, you can increase the [Secure scanner log verbosity](../sast/index.md#logging-level)
|
||||
by using a global CI/CD variable set to `debug`:
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
SECURE_LOG_LEVEL: "debug"
|
||||
```
|
||||
Debug-level logging can help when troubleshooting. For details, see
|
||||
[debug-level logging](../index.md#debug-level-logging).
|
||||
|
||||
### IaC Scanning findings show as `No longer detected` unexpectedly
|
||||
|
||||
|
|
|
|||
|
|
@ -536,24 +536,48 @@ Feedback is welcome on our vision for [unifying the user experience for these tw
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
<!-- NOTE: The below subsection(`### Secure job failing with exit code 1`) documentation URL is referred in the [/gitlab-org/security-products/analyzers/command](https://gitlab.com/gitlab-org/security-products/analyzers/command/-/blob/main/command.go#L19) repository. If this section/subsection changes, please ensure to update the corresponding URL in the mentioned repository.
|
||||
-->
|
||||
### Logging level
|
||||
|
||||
### Secure job failing with exit code 1
|
||||
The verbosity of logs output by GitLab analyzers is determined by the `SECURE_LOG_LEVEL` environment
|
||||
variable. Messages of this logging level or higher are output.
|
||||
|
||||
From highest to lowest severity, the logging levels are:
|
||||
|
||||
- `fatal`
|
||||
- `error`
|
||||
- `warn`
|
||||
- `info` (default)
|
||||
- `debug`
|
||||
|
||||
#### Debug-level logging
|
||||
|
||||
WARNING:
|
||||
Debug logging can be a serious security risk. The output may contain the content of
|
||||
environment variables and other secrets available to the job. The output is uploaded
|
||||
to the GitLab server and visible in job logs.
|
||||
to the GitLab server and is visible in job logs.
|
||||
|
||||
If a Secure job is failing and it's unclear why, add `SECURE_LOG_LEVEL: "debug"` as a global CI/CD variable for
|
||||
more verbose output that is helpful for troubleshooting.
|
||||
To enable debug-level logging, add the following to your `.gitlab-ci.yml` file:
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
SECURE_LOG_LEVEL: "debug"
|
||||
```
|
||||
|
||||
This indicates to all GitLab analyzers that they are to output **all** messages. For more details,
|
||||
see [logging level](#logging-level).
|
||||
|
||||
<!-- NOTE: The below subsection(`### Secure job failing with exit code 1`) documentation URL is referred in the [/gitlab-org/security-products/analyzers/command](https://gitlab.com/gitlab-org/security-products/analyzers/command/-/blob/main/command.go#L19) repository. If this section/subsection changes, please ensure to update the corresponding URL in the mentioned repository.
|
||||
-->
|
||||
|
||||
### Secure job failing with exit code 1
|
||||
|
||||
If a Secure job is failing and it's unclear why:
|
||||
|
||||
1. Enable [debug-level logging](#debug-level-logging).
|
||||
1. Run the job.
|
||||
1. Examine the job's output.
|
||||
1. Set the logging level to `info` (default).
|
||||
|
||||
### Outdated security reports
|
||||
|
||||
When a security report generated for a merge request becomes outdated, the merge request shows a
|
||||
|
|
|
|||
|
|
@ -519,21 +519,6 @@ variables:
|
|||
SEARCH_MAX_DEPTH: 10
|
||||
```
|
||||
|
||||
#### Logging level
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1.
|
||||
|
||||
To control the verbosity of logs, set the `SECURE_LOG_LEVEL` environment variable. Messages of this
|
||||
logging level or higher are output.
|
||||
|
||||
From highest to lowest severity, the logging levels are:
|
||||
|
||||
- `fatal`
|
||||
- `error`
|
||||
- `warn`
|
||||
- `info` (default)
|
||||
- `debug`
|
||||
|
||||
#### Custom Certificate Authority
|
||||
|
||||
To trust a custom Certificate Authority, set the `ADDITIONAL_CA_CERT_BUNDLE` variable to the bundle
|
||||
|
|
@ -772,14 +757,10 @@ By default SAST analyzers are supported in GitLab instances hosted on SELinux. A
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
### SAST debug logging
|
||||
### Debug-level logging
|
||||
|
||||
Increase the [Secure scanner log verbosity](#logging-level) to `debug` in a global CI variable to help troubleshoot SAST jobs.
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
SECURE_LOG_LEVEL: "debug"
|
||||
```
|
||||
Debug-level logging can help when troubleshooting. For details, see
|
||||
[debug-level logging](../index.md#debug-level-logging).
|
||||
|
||||
### Pipeline errors related to changes in the GitLab-managed CI/CD template
|
||||
|
||||
|
|
|
|||
|
|
@ -629,21 +629,10 @@ This feature is separate from Secret Detection scanning, which checks your Git r
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
### Set the logging level
|
||||
### Debug-level logging
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1.
|
||||
|
||||
Set the logging level to `debug` when you need diagnostic information in a Secret Detection job log.
|
||||
|
||||
WARNING:
|
||||
Debug logging can be a serious security risk. The output may contain the content of environment
|
||||
variables and other secrets available to the job. The output is uploaded to the GitLab server and
|
||||
visible in job logs.
|
||||
|
||||
1. In the `.gitlab-ci.yml` file, set the `SECURE_LOG_LEVEL` CI/CD variable to `debug`.
|
||||
1. Run the Secret Detection job.
|
||||
1. Analyze the content of the Secret Detection job.
|
||||
1. In the `.gitlab-ci.yml` file, set the `SECURE_LOG_LEVEL` CI/CD variable to `info` (default).
|
||||
Debug-level logging can help when troubleshooting. For details, see
|
||||
[debug-level logging](../index.md#debug-level-logging).
|
||||
|
||||
### Warning: `gl-secret-detection-report.json: no matching files`
|
||||
|
||||
|
|
@ -661,8 +650,8 @@ For example, you could have a pipeline triggered from a merge request containing
|
|||
clone is not deep enough to contain all of the relevant commits. To verify the current value, see
|
||||
[pipeline configuration](../../../ci/pipelines/settings.md#limit-the-number-of-changes-fetched-during-clone).
|
||||
|
||||
To confirm this as the cause of the error, set the [logging level](#set-the-logging-level) to
|
||||
`debug`, then rerun the pipeline. The logs should look similar to the following example. The text
|
||||
To confirm this as the cause of the error, enable [debug-level logging](../index.md#debug-level-logging),
|
||||
then rerun the pipeline. The logs should look similar to the following example. The text
|
||||
"object not found" is a symptom of this error.
|
||||
|
||||
```plaintext
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
stage: Manage
|
||||
group: Foundations
|
||||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
|
||||
type: reference
|
||||
---
|
||||
|
||||
# Command palette **(FREE)**
|
||||
|
||||
> Introduced in GitLab 16.2 [with a flag](../../administration/feature_flags.md) named `command_palette`. Disabled by default.
|
||||
|
||||
You can use command palette to narrow down the scope of your search or to
|
||||
find an object more quickly.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to enable the feature flag named `command_palette`. On GitLab.com, this feature is not available.
|
||||
|
||||
## Open the command palette
|
||||
|
||||
To open the command palette:
|
||||
|
||||
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**).
|
||||
1. Type one of the special characters:
|
||||
|
||||
- <kbd>></kbd> - Use to create a new object or to find a menu item.
|
||||
- <kbd>@</kbd> - Search for user.
|
||||
- <kbd>:</kbd> - Search for project.
|
||||
- <kbd>/</kbd> - Search for project files in the default repository branch.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Helpers
|
||||
module Packages
|
||||
module Maven
|
||||
extend Grape::API::Helpers
|
||||
|
||||
params :path_and_file_name do
|
||||
requires :path,
|
||||
type: String,
|
||||
desc: 'Package path',
|
||||
documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
|
||||
requires :file_name,
|
||||
type: String,
|
||||
desc: 'Package file name',
|
||||
documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -23,14 +23,10 @@ module API
|
|||
|
||||
helpers ::API::Helpers::PackagesHelpers
|
||||
helpers ::API::Helpers::Packages::DependencyProxyHelpers
|
||||
helpers ::API::Helpers::Packages::Maven
|
||||
helpers ::API::Helpers::Packages::Maven::BasicAuthHelpers
|
||||
|
||||
helpers do
|
||||
params :path_and_file_name do
|
||||
requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
|
||||
requires :file_name, type: String, desc: 'Package file name', documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
|
||||
end
|
||||
|
||||
def path_exists?(path)
|
||||
return false if path.blank?
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module Gitlab
|
|||
#
|
||||
# This class is thread-safe via RequestStore.
|
||||
class HookEnv
|
||||
WHITELISTED_VARIABLES = %w[
|
||||
ALLOWLISTED_VARIABLES = %w[
|
||||
GIT_OBJECT_DIRECTORY_RELATIVE
|
||||
GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE
|
||||
].freeze
|
||||
|
|
@ -25,7 +25,7 @@ module Gitlab
|
|||
raise "missing gl_repository" if gl_repository.blank?
|
||||
|
||||
Gitlab::SafeRequestStore[:gitlab_git_env] ||= {}
|
||||
Gitlab::SafeRequestStore[:gitlab_git_env][gl_repository] = whitelist_git_env(env)
|
||||
Gitlab::SafeRequestStore[:gitlab_git_env][gl_repository] = allowlist_git_env(env)
|
||||
end
|
||||
|
||||
def self.all(gl_repository)
|
||||
|
|
@ -46,8 +46,8 @@ module Gitlab
|
|||
env
|
||||
end
|
||||
|
||||
def self.whitelist_git_env(env)
|
||||
env.select { |key, _| WHITELISTED_VARIABLES.include?(key.to_s) }.with_indifferent_access
|
||||
def self.allowlist_git_env(env)
|
||||
env.select { |key, _| ALLOWLISTED_VARIABLES.include?(key.to_s) }.with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,13 @@ module Gitlab
|
|||
'https://observe.gitlab.com'
|
||||
end
|
||||
|
||||
# Returns true if the Observability feature flag is enabled
|
||||
def oauth_url
|
||||
"#{Gitlab::Observability.observability_url}/v1/auth/start"
|
||||
end
|
||||
|
||||
# Returns true if the GitLab Observability UI (GOUI) feature flag is enabled
|
||||
#
|
||||
# @deprecated
|
||||
#
|
||||
def enabled?(group = nil)
|
||||
return Feature.enabled?(:observability_group_tab, group) if group
|
||||
|
|
@ -31,6 +37,11 @@ module Gitlab
|
|||
Feature.enabled?(:observability_group_tab)
|
||||
end
|
||||
|
||||
# Returns true if Tracing UI is enabled
|
||||
def tracing_enabled?(project)
|
||||
Feature.enabled?(:observability_tracing, project)
|
||||
end
|
||||
|
||||
# Returns the embeddable Observability URL of a given URL
|
||||
#
|
||||
# - Validates the URL
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ module Sidebars
|
|||
def configure_menu_items
|
||||
return false unless feature_enabled?
|
||||
|
||||
add_item(tracing_menu_item)
|
||||
add_item(error_tracking_menu_item)
|
||||
add_item(alert_management_menu_item)
|
||||
add_item(incidents_menu_item)
|
||||
|
|
@ -62,6 +63,20 @@ module Sidebars
|
|||
)
|
||||
end
|
||||
|
||||
def tracing_menu_item
|
||||
unless Gitlab::Observability.tracing_enabled?(context.project)
|
||||
return ::Sidebars::NilMenuItem.new(item_id: :tracing)
|
||||
end
|
||||
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('Tracing'),
|
||||
link: project_tracing_index_path(context.project),
|
||||
super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::MonitorMenu,
|
||||
active_routes: { controller: :tracing },
|
||||
item_id: :tracing
|
||||
)
|
||||
end
|
||||
|
||||
def alert_management_menu_item
|
||||
unless can?(context.current_user, :read_alert_management_alert, context.project)
|
||||
return ::Sidebars::NilMenuItem.new(item_id: :alert_management)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ module Sidebars
|
|||
override :configure_menu_items
|
||||
def configure_menu_items
|
||||
[
|
||||
:tracing,
|
||||
:error_tracking,
|
||||
:alert_management,
|
||||
:incidents,
|
||||
|
|
|
|||
|
|
@ -48319,6 +48319,9 @@ msgstr ""
|
|||
msgid "TotalRefCountIndicator|1000+"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tracing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Track groups of issues that share a theme, across projects and milestones"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ module RuboCop
|
|||
class AvoidReturnFromBlocks < RuboCop::Cop::Base
|
||||
MSG = 'Do not return from a block, use next or break instead.'
|
||||
DEF_METHODS = %i[define_method lambda].freeze
|
||||
WHITELISTED_METHODS = %i[each each_filename times loop].freeze
|
||||
ALLOWLISTED_METHODS = %i[each each_filename times loop].freeze
|
||||
|
||||
def on_block(node)
|
||||
block_body = node.body
|
||||
|
|
@ -32,7 +32,7 @@ module RuboCop
|
|||
return unless top_block?(node)
|
||||
|
||||
block_body.each_node(:return) do |return_node|
|
||||
next if parent_blocks(node, return_node).all? { |block_node| whitelisted?(block_node) }
|
||||
next if parent_blocks(node, return_node).all? { |block_node| allowlisted?(block_node) }
|
||||
|
||||
add_offense(return_node)
|
||||
end
|
||||
|
|
@ -69,8 +69,8 @@ module RuboCop
|
|||
(node.type == :block && DEF_METHODS.include?(node.method_name))
|
||||
end
|
||||
|
||||
def whitelisted?(block_node)
|
||||
WHITELISTED_METHODS.include?(block_node.method_name)
|
||||
def allowlisted?(block_node)
|
||||
ALLOWLISTED_METHODS.include?(block_node.method_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module RuboCop
|
|||
class IDType < RuboCop::Cop::Base
|
||||
MSG = 'Do not use GraphQL::Types::ID, use a specific GlobalIDType instead'
|
||||
|
||||
WHITELISTED_ARGUMENTS = %i[iid full_path project_path group_path target_project_path namespace_path].freeze
|
||||
ALLOWLISTED_ARGUMENTS = %i[iid full_path project_path group_path target_project_path namespace_path].freeze
|
||||
|
||||
def_node_search :graphql_id_type?, <<~PATTERN
|
||||
(send nil? :argument (_ #does_not_match?) (const (const (const nil? :GraphQL) :Types) :ID) ...)
|
||||
|
|
@ -21,7 +21,7 @@ module RuboCop
|
|||
private
|
||||
|
||||
def does_not_match?(arg)
|
||||
!WHITELISTED_ARGUMENTS.include?(arg) # rubocop:disable Rails/NegateInclude
|
||||
!ALLOWLISTED_ARGUMENTS.include?(arg) # rubocop:disable Rails/NegateInclude
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ function rspec_rerun_previous_failed_tests() {
|
|||
local test_file_count_threshold=${RSPEC_PREVIOUS_FAILED_TEST_FILE_COUNT_THRESHOLD:-10}
|
||||
local matching_tests_file=${1}
|
||||
local rspec_opts=${2}
|
||||
local test_files="$(cat "${matching_tests_file}")"
|
||||
local test_files="$(select_existing_files < "${matching_tests_file}")"
|
||||
local test_file_count=$(wc -w "${matching_tests_file}" | awk {'print $1'})
|
||||
|
||||
if [[ "${test_file_count}" -gt "${test_file_count_threshold}" ]]; then
|
||||
|
|
|
|||
|
|
@ -201,6 +201,10 @@ function install_junit_merge_gem() {
|
|||
run_timed_command "gem install junit_merge --no-document --version 0.1.2"
|
||||
}
|
||||
|
||||
function select_existing_files() {
|
||||
ruby -e 'print $stdin.read.split(" ").select { |f| File.exist?(f) }.join(" ")'
|
||||
}
|
||||
|
||||
function fail_on_warnings() {
|
||||
local cmd="$*"
|
||||
local warning_file
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ RSpec.describe IssuableCollections do
|
|||
}
|
||||
end
|
||||
|
||||
it 'only allows whitelisted params' do
|
||||
it 'only allows allowlisted params' do
|
||||
is_expected.to include({
|
||||
'assignee_id' => '1',
|
||||
'assignee_username' => 'user1',
|
||||
|
|
@ -123,7 +123,7 @@ RSpec.describe IssuableCollections do
|
|||
}
|
||||
end
|
||||
|
||||
it 'only allows whitelisted params' do
|
||||
it 'only allows allowlisted params' do
|
||||
is_expected.to include({
|
||||
'label_name' => %w[label1 label2],
|
||||
'assignee_username' => %w[user1 user2]
|
||||
|
|
|
|||
|
|
@ -46,14 +46,6 @@ describe('content_editor/services/create_content_editor', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('sets gl-shadow-none! class selector to the tiptapEditor instance', () => {
|
||||
expect(editor.tiptapEditor.options.editorProps).toMatchObject({
|
||||
attributes: {
|
||||
class: 'gl-shadow-none!',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('allows providing external content editor extensions', () => {
|
||||
const labelReference = 'this is a ~group::editor';
|
||||
const { tiptapExtension, serializer } = createTestContentEditorExtension();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { nextTick } from 'vue';
|
||||
import { GlIcon, GlCard } from '@gitlab/ui';
|
||||
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import {
|
||||
issuable1,
|
||||
issuable2,
|
||||
|
|
@ -14,6 +14,7 @@ import {
|
|||
linkedIssueTypesTextMap,
|
||||
PathIdSeparator,
|
||||
} from '~/related_issues/constants';
|
||||
import RelatedIssuesList from '~/related_issues/components/related_issues_list.vue';
|
||||
|
||||
describe('RelatedIssuesBlock', () => {
|
||||
let wrapper;
|
||||
|
|
@ -21,9 +22,10 @@ describe('RelatedIssuesBlock', () => {
|
|||
const findToggleButton = () => wrapper.findByTestId('toggle-links');
|
||||
const findRelatedIssuesBody = () => wrapper.findByTestId('related-issues-body');
|
||||
const findIssueCountBadgeAddButton = () => wrapper.findByTestId('related-issues-plus-button');
|
||||
const findAllRelatedIssuesList = () => wrapper.findAllComponents(RelatedIssuesList);
|
||||
const findRelatedIssuesList = (index) => findAllRelatedIssuesList().at(index);
|
||||
|
||||
const createComponent = ({
|
||||
mountFn = mountExtended,
|
||||
pathIdSeparator = PathIdSeparator.Issue,
|
||||
issuableType = TYPE_ISSUE,
|
||||
canAdmin = false,
|
||||
|
|
@ -35,7 +37,7 @@ describe('RelatedIssuesBlock', () => {
|
|||
autoCompleteEpics = true,
|
||||
slots = '',
|
||||
} = {}) => {
|
||||
wrapper = mountFn(RelatedIssuesBlock, {
|
||||
wrapper = shallowMountExtended(RelatedIssuesBlock, {
|
||||
propsData: {
|
||||
pathIdSeparator,
|
||||
issuableType,
|
||||
|
|
@ -94,10 +96,7 @@ describe('RelatedIssuesBlock', () => {
|
|||
it('displays header text slot data', () => {
|
||||
const headerText = '<div>custom header text</div>';
|
||||
|
||||
createComponent({
|
||||
mountFn: shallowMountExtended,
|
||||
slots: { 'header-text': headerText },
|
||||
});
|
||||
createComponent({ slots: { 'header-text': headerText } });
|
||||
|
||||
expect(wrapper.findByTestId('card-title').html()).toContain(headerText);
|
||||
});
|
||||
|
|
@ -107,10 +106,7 @@ describe('RelatedIssuesBlock', () => {
|
|||
it('displays header actions slot data', () => {
|
||||
const headerActions = '<button data-testid="custom-button">custom button</button>';
|
||||
|
||||
createComponent({
|
||||
mountFn: shallowMountExtended,
|
||||
slots: { 'header-actions': headerActions },
|
||||
});
|
||||
createComponent({ slots: { 'header-actions': headerActions } });
|
||||
|
||||
expect(wrapper.findByTestId('custom-button').html()).toBe(headerActions);
|
||||
});
|
||||
|
|
@ -153,10 +149,6 @@ describe('RelatedIssuesBlock', () => {
|
|||
});
|
||||
|
||||
describe('showCategorizedIssues prop', () => {
|
||||
const issueList = () => wrapper.findAll('.js-related-issues-token-list-item');
|
||||
const categorizedHeadings = () => wrapper.findAll('h4');
|
||||
const headingTextAt = (index) => categorizedHeadings().at(index).text();
|
||||
|
||||
describe('when showCategorizedIssues=true', () => {
|
||||
beforeEach(() =>
|
||||
createComponent({
|
||||
|
|
@ -166,25 +158,25 @@ describe('RelatedIssuesBlock', () => {
|
|||
);
|
||||
|
||||
it('should render issue tokens items', () => {
|
||||
expect(issueList()).toHaveLength(3);
|
||||
expect(findAllRelatedIssuesList()).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('shows "Blocks" heading', () => {
|
||||
const blocks = linkedIssueTypesTextMap[linkedIssueTypesMap.BLOCKS];
|
||||
|
||||
expect(headingTextAt(0)).toBe(blocks);
|
||||
expect(findRelatedIssuesList(0).props('heading')).toBe(
|
||||
linkedIssueTypesTextMap[linkedIssueTypesMap.BLOCKS],
|
||||
);
|
||||
});
|
||||
|
||||
it('shows "Is blocked by" heading', () => {
|
||||
const isBlockedBy = linkedIssueTypesTextMap[linkedIssueTypesMap.IS_BLOCKED_BY];
|
||||
|
||||
expect(headingTextAt(1)).toBe(isBlockedBy);
|
||||
expect(findRelatedIssuesList(1).props('heading')).toBe(
|
||||
linkedIssueTypesTextMap[linkedIssueTypesMap.IS_BLOCKED_BY],
|
||||
);
|
||||
});
|
||||
|
||||
it('shows "Relates to" heading', () => {
|
||||
const relatesTo = linkedIssueTypesTextMap[linkedIssueTypesMap.RELATES_TO];
|
||||
|
||||
expect(headingTextAt(2)).toBe(relatesTo);
|
||||
expect(findRelatedIssuesList(2).props('heading')).toBe(
|
||||
linkedIssueTypesTextMap[linkedIssueTypesMap.RELATES_TO],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -194,8 +186,9 @@ describe('RelatedIssuesBlock', () => {
|
|||
showCategorizedIssues: false,
|
||||
relatedIssues: [issuable1, issuable2, issuable3],
|
||||
});
|
||||
expect(issueList()).toHaveLength(3);
|
||||
expect(categorizedHeadings()).toHaveLength(0);
|
||||
expect(findAllRelatedIssuesList()).toHaveLength(1);
|
||||
expect(findRelatedIssuesList(0).props('relatedIssues')).toHaveLength(3);
|
||||
expect(findRelatedIssuesList(0).props('heading')).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
|
|||
</div>
|
||||
|
||||
<div
|
||||
class="js-vue-markdown-field md-area position-relative gfm-form js-expanded"
|
||||
class="js-vue-markdown-field md-area position-relative gfm-form gl-overflow-hidden js-expanded"
|
||||
data-uploads-path=""
|
||||
>
|
||||
<markdown-header-stub
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ describe('GroupSelect', () => {
|
|||
const findEntitySelect = () => wrapper.findComponent(EntitySelect);
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
const handleInput = jest.fn();
|
||||
|
||||
// Helpers
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
wrapper = shallowMountExtended(GroupSelect, {
|
||||
|
|
@ -52,6 +54,9 @@ describe('GroupSelect', () => {
|
|||
GlAlert,
|
||||
EntitySelect,
|
||||
},
|
||||
listeners: {
|
||||
input: handleInput,
|
||||
},
|
||||
});
|
||||
};
|
||||
const openListbox = () => findListbox().vm.$emit('shown');
|
||||
|
|
@ -132,4 +137,11 @@ describe('GroupSelect', () => {
|
|||
expect(findAlert().exists()).toBe(true);
|
||||
expect(findAlert().text()).toBe(FETCH_GROUPS_ERROR);
|
||||
});
|
||||
|
||||
it('forwards events to the parent scope via `v-on="$listeners"`', () => {
|
||||
createComponent();
|
||||
findEntitySelect().vm.$emit('input');
|
||||
|
||||
expect(handleInput).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ describe('ProjectSelect', () => {
|
|||
const findEntitySelect = () => wrapper.findComponent(EntitySelect);
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
const handleInput = jest.fn();
|
||||
|
||||
// Helpers
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
wrapper = mountExtended(ProjectSelect, {
|
||||
|
|
@ -59,6 +61,9 @@ describe('ProjectSelect', () => {
|
|||
GlAlert,
|
||||
EntitySelect,
|
||||
},
|
||||
listeners: {
|
||||
input: handleInput,
|
||||
},
|
||||
});
|
||||
};
|
||||
const openListbox = () => findListbox().vm.$emit('shown');
|
||||
|
|
@ -255,4 +260,11 @@ describe('ProjectSelect', () => {
|
|||
expect(findAlert().exists()).toBe(true);
|
||||
expect(findAlert().text()).toBe(FETCH_PROJECTS_ERROR);
|
||||
});
|
||||
|
||||
it('forwards events to the parent scope via `v-on="$listeners"`', () => {
|
||||
createComponent();
|
||||
findEntitySelect().vm.$emit('input');
|
||||
|
||||
expect(handleInput).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ RSpec.describe Gitlab::Observability, feature_category: :error_tracking do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.oauth_url' do
|
||||
subject { described_class.oauth_url }
|
||||
|
||||
it { is_expected.to eq("#{described_class.observability_url}/v1/auth/start") }
|
||||
end
|
||||
|
||||
describe '.build_full_url' do
|
||||
let_it_be(:group) { build_stubbed(:group, id: 123) }
|
||||
let(:observability_url) { described_class.observability_url }
|
||||
|
|
@ -148,6 +154,27 @@ RSpec.describe Gitlab::Observability, feature_category: :error_tracking do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.tracing_enabled?' do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
it 'returns true if feature is enabled globally' do
|
||||
expect(described_class.tracing_enabled?(project)).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns true if feature is enabled for the project' do
|
||||
stub_feature_flags(observability_tracing: false)
|
||||
stub_feature_flags(observability_tracing: project)
|
||||
|
||||
expect(described_class.tracing_enabled?(project)).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false if feature is disabled globally' do
|
||||
stub_feature_flags(observability_tracing: false)
|
||||
|
||||
expect(described_class.tracing_enabled?(project)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.allowed_for_action?' do
|
||||
let(:group) { build_stubbed(:group) }
|
||||
let(:user) { build_stubbed(:user) }
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu, feature_category: :naviga
|
|||
|
||||
let(:user) { project.first_owner }
|
||||
let(:show_cluster_hint) { true }
|
||||
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, show_cluster_hint: show_cluster_hint) }
|
||||
let(:context) do
|
||||
Sidebars::Projects::Context.new(current_user: user, container: project, show_cluster_hint: show_cluster_hint)
|
||||
end
|
||||
|
||||
subject { described_class.new(context) }
|
||||
|
||||
|
|
@ -86,5 +88,19 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu, feature_category: :naviga
|
|||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
|
||||
describe 'Tracing' do
|
||||
let(:item_id) { :tracing }
|
||||
|
||||
specify { is_expected.not_to be_nil }
|
||||
|
||||
describe 'when feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(observability_tracing: false)
|
||||
end
|
||||
|
||||
specify { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ RSpec.describe Sidebars::Projects::SuperSidebarMenus::MonitorMenu, feature_categ
|
|||
it 'defines list of NilMenuItem placeholders' do
|
||||
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
|
||||
expect(items.map(&:item_id)).to eq([
|
||||
:tracing,
|
||||
:error_tracking,
|
||||
:alert_management,
|
||||
:incidents,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ RSpec.describe Groups::ObservabilityController, feature_category: :tracing do
|
|||
end
|
||||
|
||||
it_behaves_like 'observability csp policy' do
|
||||
before_all do
|
||||
group.add_developer(user)
|
||||
end
|
||||
|
||||
let(:tested_path) { path }
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
|
|||
describe 'GET #new' do
|
||||
include_context 'group project issue'
|
||||
|
||||
before do
|
||||
group.add_developer(user)
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
it_behaves_like "observability csp policy", described_class do
|
||||
let(:tested_path) do
|
||||
new_project_issue_path(project)
|
||||
|
|
@ -26,11 +31,13 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
|
|||
|
||||
describe 'GET #show' do
|
||||
before do
|
||||
group.add_developer(user)
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
it_behaves_like "observability csp policy", described_class do
|
||||
include_context 'group project issue'
|
||||
|
||||
let(:tested_path) do
|
||||
project_issue_path(project, issue)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,8 +6,13 @@ RSpec.describe 'merge requests creations', feature_category: :code_review_workfl
|
|||
describe 'GET /:namespace/:project/merge_requests/new' do
|
||||
include ProjectForksHelper
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user) { project.first_owner }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, :repository, group: group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before_all do
|
||||
group.add_developer(user)
|
||||
end
|
||||
|
||||
before do
|
||||
login_as(user)
|
||||
|
|
@ -26,16 +31,13 @@ RSpec.describe 'merge requests creations', feature_category: :code_review_workfl
|
|||
end
|
||||
|
||||
it_behaves_like "observability csp policy", Projects::MergeRequests::CreationsController do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
let(:tested_path) do
|
||||
project_new_merge_request_path(project, merge_request: {
|
||||
title: 'Some feature',
|
||||
source_branch: 'fix',
|
||||
target_branch: 'feature',
|
||||
target_project: project,
|
||||
source_project: project
|
||||
source_branch: 'fix',
|
||||
target_branch: 'feature',
|
||||
target_project: project,
|
||||
source_project: project
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :source_code
|
|||
|
||||
context 'when logged in' do
|
||||
before do
|
||||
group.add_developer(user)
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::TracingController, feature_category: :tracing do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let(:path) { nil }
|
||||
let(:observability_tracing_ff) { true }
|
||||
|
||||
subject do
|
||||
get path
|
||||
response
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
stub_feature_flags(observability_tracing: observability_tracing_ff)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
let(:path) { project_tracing_index_path(project) }
|
||||
|
||||
it_behaves_like 'observability csp policy' do
|
||||
before_all do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
let(:tested_path) { path }
|
||||
end
|
||||
|
||||
context 'when user does not have permissions' do
|
||||
it 'returns 404' do
|
||||
expect(subject).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has permissions' do
|
||||
before_all do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns 200' do
|
||||
expect(subject).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'when feature is disabled' do
|
||||
let(:observability_tracing_ff) { false }
|
||||
|
||||
it 'returns 404' do
|
||||
expect(subject).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -41,10 +41,10 @@ RSpec.describe RuboCop::Cop::AvoidReturnFromBlocks do
|
|||
RUBY
|
||||
end
|
||||
|
||||
shared_examples 'examples with whitelisted method' do |whitelisted_method|
|
||||
it "doesn't flag violation for return inside #{whitelisted_method}" do
|
||||
shared_examples 'examples with allowlisted method' do |allowlisted_method|
|
||||
it "doesn't flag violation for return inside #{allowlisted_method}" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
items.#{whitelisted_method} do |item|
|
||||
items.#{allowlisted_method} do |item|
|
||||
do_something
|
||||
return if something_else
|
||||
end
|
||||
|
|
@ -52,8 +52,8 @@ RSpec.describe RuboCop::Cop::AvoidReturnFromBlocks do
|
|||
end
|
||||
end
|
||||
|
||||
%i[each each_filename times loop].each do |whitelisted_method|
|
||||
it_behaves_like 'examples with whitelisted method', whitelisted_method
|
||||
%i[each each_filename times loop].each do |allowlisted_method|
|
||||
it_behaves_like 'examples with allowlisted method', allowlisted_method
|
||||
end
|
||||
|
||||
shared_examples 'examples with def methods' do |def_method|
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ RSpec.describe RuboCop::Cop::Graphql::IDType do
|
|||
TYPE
|
||||
end
|
||||
|
||||
context 'whitelisted arguments' do
|
||||
RuboCop::Cop::Graphql::IDType::WHITELISTED_ARGUMENTS.each do |arg|
|
||||
context 'allowlisted arguments' do
|
||||
RuboCop::Cop::Graphql::IDType::ALLOWLISTED_ARGUMENTS.each do |arg|
|
||||
it "does not add an offense for calls to #argument with #{arg} as argument name" do
|
||||
expect_no_offenses(<<~TYPE.strip)
|
||||
argument #{arg}, GraphQL::Types::ID, some: other, params: do_not_matter
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ RSpec.describe DesignManagement::GenerateImageVersionsService, feature_category:
|
|||
.from(nil).to(CarrierWave::SanitizedFile)
|
||||
end
|
||||
|
||||
it 'skips generating image versions if the mime type is not whitelisted' do
|
||||
it 'skips generating image versions if the mime type is not allowlisted' do
|
||||
stub_const('DesignManagement::DesignV432x230Uploader::MIME_TYPE_ALLOWLIST', [])
|
||||
|
||||
described_class.new(version).execute
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ RSpec.describe Projects::DownloadService, feature_category: :groups_and_projects
|
|||
@project = create(:project, creator_id: @user.id, namespace: @user.namespace)
|
||||
end
|
||||
|
||||
context 'for a URL that is not on whitelist' do
|
||||
context 'for a URL that is not on allowlist' do
|
||||
before do
|
||||
url = 'https://code.jquery.com/jquery-2.1.4.min.js'
|
||||
@link_to_file = download_file(@project, url)
|
||||
|
|
@ -18,7 +18,7 @@ RSpec.describe Projects::DownloadService, feature_category: :groups_and_projects
|
|||
it { expect(@link_to_file).to eq(nil) }
|
||||
end
|
||||
|
||||
context 'for URLs that are on the whitelist' do
|
||||
context 'for URLs that are on the allowlist' do
|
||||
before do
|
||||
# `ssrf_filter` resolves the hostname. See https://github.com/carrierwaveuploader/carrierwave/commit/91714adda998bc9e8decf5b1f5d260d808761304
|
||||
stub_request(:get, %r{http://[\d.]+/rails_sample.jpg}).to_return(body: File.read(Rails.root + 'spec/fixtures/rails_sample.jpg'))
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ module ExportFileHelper
|
|||
end
|
||||
|
||||
# Returns the offended ObjectWithParent object if a sensitive word is found inside a hash,
|
||||
# excluding the whitelisted safe hashes.
|
||||
# excluding the allowlisted safe hashes.
|
||||
def find_sensitive_attributes(sensitive_word, project_hash)
|
||||
loop do
|
||||
object_with_parent = deep_find_with_parent(sensitive_word, project_hash)
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ RSpec.shared_context 'project navbar structure' do
|
|||
{
|
||||
nav_item: _('Monitor'),
|
||||
nav_sub_items: [
|
||||
_('Tracing'),
|
||||
_('Error Tracking'),
|
||||
_('Alerts'),
|
||||
_('Incidents')
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ RSpec.shared_examples 'default allowlist' do
|
|||
expect(filter(act).to_html).to eq exp
|
||||
end
|
||||
|
||||
it 'allows whitelisted HTML tags from the user' do
|
||||
it 'allows allowlisted HTML tags from the user' do
|
||||
exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>"
|
||||
expect(filter(act).to_html).to eq exp
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,23 +5,28 @@
|
|||
# It requires the following variables declared in the context including this example:
|
||||
#
|
||||
# - `tested_path`: the path under test
|
||||
# - `user`: the test user
|
||||
# - `group`: the test group
|
||||
#
|
||||
# e.g.
|
||||
#
|
||||
# ```
|
||||
# let_it_be(:group) { create(:group) }
|
||||
# let_it_be(:user) { create(:user) }
|
||||
# it_behaves_like "observability csp policy" do
|
||||
# let(:tested_path) { ....the path under test }
|
||||
# end
|
||||
# ```
|
||||
#
|
||||
# Note that the context's user is expected to be logged-in and the
|
||||
# related resources (group, project, etc) are supposed to be provided with proper
|
||||
# permissions already, e.g.
|
||||
#
|
||||
# before do
|
||||
# login_as(user)
|
||||
# group.add_developer(user)
|
||||
# end
|
||||
#
|
||||
# It optionally supports specifying the controller class handling the tested path as a parameter, e.g.
|
||||
#
|
||||
# ```
|
||||
# it_behaves_like "observability csp policy", Groups::ObservabilityController
|
||||
# it_behaves_like "observability csp policy", Projects::TracingController
|
||||
# ```
|
||||
# (If not specified it will default to `described_class`)
|
||||
#
|
||||
|
|
@ -41,9 +46,6 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe
|
|||
|
||||
before do
|
||||
setup_csp_for_controller(controller_class, csp, any_time: true)
|
||||
group.add_developer(user)
|
||||
login_as(user)
|
||||
stub_feature_flags(observability_group_tab: true)
|
||||
end
|
||||
|
||||
subject do
|
||||
|
|
@ -59,93 +61,127 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe
|
|||
end
|
||||
end
|
||||
|
||||
context 'when observability is disabled' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src 'https://something.test'
|
||||
describe 'frame-src' do
|
||||
context 'when frame-src exists in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src 'https://something.test'
|
||||
end
|
||||
end
|
||||
|
||||
it 'appends the proper url to frame-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(observability_group_tab: false)
|
||||
context 'when signin url is already present in the policy' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src signin_url
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not append signin again' do
|
||||
expect(subject).to include(
|
||||
"frame-src #{signin_url} #{observability_url} #{oauth_url};")
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not add observability urls to the csp header' do
|
||||
expect(subject).to include("frame-src https://something.test")
|
||||
expect(subject).not_to include("#{observability_url} #{signin_url} #{oauth_url}")
|
||||
context 'when oauth url is already present in the policy' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src oauth_url
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not append oauth again' do
|
||||
expect(subject).to include(
|
||||
"frame-src #{oauth_url} #{observability_url} #{signin_url};")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when default-src exists in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.default_src 'https://something.test'
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not change default-src' do
|
||||
expect(subject).to include(
|
||||
"default-src https://something.test;")
|
||||
end
|
||||
|
||||
it 'appends the proper url to frame-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when frame-src and default-src exist in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.default_src 'https://something_default.test'
|
||||
p.frame_src 'https://something.test'
|
||||
end
|
||||
end
|
||||
|
||||
it 'appends to frame-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
|
||||
expect(subject).to include(
|
||||
"default-src https://something_default.test")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when frame-src exists in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src 'https://something.test'
|
||||
describe 'connect-src' do
|
||||
context 'when connect-src exists in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.connect_src 'https://something.test'
|
||||
end
|
||||
end
|
||||
|
||||
it 'appends the proper url to connect-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"connect-src https://something.test localhost #{observability_url}")
|
||||
end
|
||||
end
|
||||
|
||||
it 'appends the proper url to frame-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
|
||||
end
|
||||
end
|
||||
context 'when default-src exists in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.default_src 'https://something.test'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signin is already present in the policy' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src signin_url
|
||||
it 'does not change default-src' do
|
||||
expect(subject).to include(
|
||||
"default-src https://something.test;")
|
||||
end
|
||||
|
||||
it 'appends the proper url to connect-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"connect-src https://something.test localhost #{observability_url}")
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not append signin again' do
|
||||
expect(subject).to include(
|
||||
"frame-src #{signin_url} #{observability_url} #{oauth_url};")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when oauth is already present in the policy' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src oauth_url
|
||||
context 'when connect-src and default-src exist in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.default_src 'https://something_default.test'
|
||||
p.connect_src 'https://something.test'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not append oauth again' do
|
||||
expect(subject).to include(
|
||||
"frame-src #{oauth_url} #{observability_url} #{signin_url};")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when default-src exists in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.default_src 'https://something.test'
|
||||
it 'appends to connect-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"connect-src https://something.test localhost #{observability_url}")
|
||||
expect(subject).to include(
|
||||
"default-src https://something_default.test")
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not change default-src' do
|
||||
expect(subject).to include(
|
||||
"default-src https://something.test;")
|
||||
end
|
||||
|
||||
it 'appends the proper url to frame-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when frame-src and default-src exist in the CSP config' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.default_src 'https://something_default.test'
|
||||
p.frame_src 'https://something.test'
|
||||
end
|
||||
end
|
||||
|
||||
it 'appends to frame-src CSP directives' do
|
||||
expect(subject).to include(
|
||||
"frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}")
|
||||
expect(subject).to include(
|
||||
"default-src https://something_default.test")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ RSpec.describe AvatarUploader do
|
|||
end
|
||||
end
|
||||
|
||||
context 'accept whitelist file content type' do
|
||||
context 'accept allowlist file content type' do
|
||||
# We need to feed through a valid path, but we force the parsed mime type
|
||||
# in a stub below so we can set any path.
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'video_sample.mp4') }
|
||||
|
|
@ -61,13 +61,13 @@ RSpec.describe AvatarUploader do
|
|||
end
|
||||
end
|
||||
|
||||
context 'upload non-whitelisted file content type' do
|
||||
context 'upload denylisted file content type' do
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'sanitized.svg') }
|
||||
|
||||
it_behaves_like 'denied carrierwave upload'
|
||||
end
|
||||
|
||||
context 'upload misnamed non-whitelisted file content type' do
|
||||
context 'upload misnamed denylisted file content type' do
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'not_a_png.png') }
|
||||
|
||||
it_behaves_like 'denied carrierwave upload'
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ RSpec.describe DesignManagement::DesignV432x230Uploader do
|
|||
)
|
||||
end
|
||||
|
||||
context 'accept whitelist file content type' do
|
||||
context 'accept allowlisted file content type' do
|
||||
# We need to feed through a valid path, but we force the parsed mime type
|
||||
# in a stub below so we can set any path.
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'dk.png') }
|
||||
|
|
@ -72,13 +72,13 @@ RSpec.describe DesignManagement::DesignV432x230Uploader do
|
|||
end
|
||||
end
|
||||
|
||||
context 'upload non-whitelisted file content type' do
|
||||
context 'upload denylisted file content type' do
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'logo_sample.svg') }
|
||||
|
||||
it_behaves_like 'denied carrierwave upload'
|
||||
end
|
||||
|
||||
context 'upload misnamed non-whitelisted file content type' do
|
||||
context 'upload misnamed denylisted file content type' do
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'not_a_png.png') }
|
||||
|
||||
it_behaves_like 'denied carrierwave upload'
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RSpec.describe FaviconUploader do
|
|||
let_it_be(:model) { build_stubbed(:user) }
|
||||
let_it_be(:uploader) { described_class.new(model, :favicon) }
|
||||
|
||||
context 'accept whitelist file content type' do
|
||||
context 'accept allowlist file content type' do
|
||||
include_context 'ignore extension allowlist check'
|
||||
|
||||
# We need to feed through a valid path, but we force the parsed mime type
|
||||
|
|
@ -22,7 +22,7 @@ RSpec.describe FaviconUploader do
|
|||
end
|
||||
end
|
||||
|
||||
context 'upload non-whitelisted file content type' do
|
||||
context 'upload denylisted file content type' do
|
||||
include_context 'ignore extension allowlist check'
|
||||
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'sanitized.svg') }
|
||||
|
|
@ -30,7 +30,7 @@ RSpec.describe FaviconUploader do
|
|||
it_behaves_like 'denied carrierwave upload'
|
||||
end
|
||||
|
||||
context 'upload misnamed non-whitelisted file content type' do
|
||||
context 'upload misnamed denylisted file content type' do
|
||||
include_context 'ignore extension allowlist check'
|
||||
|
||||
let_it_be(:path) { File.join('spec', 'fixtures', 'not_a_png.png') }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ RSpec.describe CronValidator do
|
|||
expect(subject.valid?).to be_falsy
|
||||
end
|
||||
|
||||
context 'cron field is not whitelisted' do
|
||||
context 'cron field is not allowlisted' do
|
||||
subject do
|
||||
Class.new do
|
||||
include ActiveModel::Model
|
||||
|
|
@ -43,7 +43,7 @@ RSpec.describe CronValidator do
|
|||
it 'raises an error' do
|
||||
subject.cron_partytime = '0 23 * * 5'
|
||||
|
||||
expect { subject.valid? }.to raise_error(StandardError, "Non-whitelisted attribute")
|
||||
expect { subject.valid? }.to raise_error(StandardError, "Non-allowlisted attribute")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue