summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/test-on-gdk/main.gitlab-ci.yml145
-rw-r--r--Gemfile.checksum10
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue6
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifest_row.vue44
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifests_list.vue12
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql1
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue2
-rw-r--r--app/models/application_setting.rb3
-rw-r--r--app/models/concerns/enums/package_metadata.rb9
-rw-r--r--db/docs/pm_advisories.yml11
-rw-r--r--db/docs/pm_affected_packages.yml11
-rw-r--r--db/migrate/20230411205121_create_package_metadata_advisory_info.rb39
-rw-r--r--db/migrate/20230501163253_add_remember_me_enabled_to_application_settings.rb7
-rw-r--r--db/schema_migrations/202304112051211
-rw-r--r--db/schema_migrations/202305011632531
-rw-r--r--db/structure.sql78
-rw-r--r--doc/administration/package_information/defaults.md3
-rw-r--r--doc/development/testing_guide/end_to_end/execution_context_selection.md12
-rw-r--r--doc/user/application_security/sast/index.md1
-rw-r--r--doc/user/application_security/secret_detection/index.md35
-rw-r--r--doc/user/compliance/index.md10
-rw-r--r--doc/user/compliance/license_approval_policies.md6
-rw-r--r--locale/gitlab.pot8
-rwxr-xr-xqa/gdk/launch2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb7
-rw-r--r--qa/qa/specs/helpers/context_selector.rb68
-rw-r--r--qa/spec/specs/helpers/context_selector_spec.rb14
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/app_spec.js1
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js14
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/components/manifest_row_spec.js51
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/mock_data.js4
-rw-r--r--spec/models/application_setting_spec.rb3
34 files changed, 565 insertions, 58 deletions
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index 6f06746c49f..dae96808a61 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -128,6 +128,8 @@ e2e:test-on-gdk:
# In MRs we assume the last scheduled master pipeline built the image already.
- job: build-qa-on-gdk-master-image
optional: true
+ variables:
+ QA_RUN_TYPE: e2e-test-on-gdk # Setting it here so that all the child pipeline reporting jobs inherit this variable
allow_failure: true
trigger:
strategy: depend
diff --git a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml
index d51e255fb95..10eca154b31 100644
--- a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml
+++ b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml
@@ -3,6 +3,27 @@ default:
include:
- local: .gitlab/ci/package-and-test/rules.gitlab-ci.yml
+ - local: .gitlab/ci/package-and-test/variables.gitlab-ci.yml
+ - project: 'gitlab-org/quality/pipeline-common'
+ ref: 3.1.3
+ file:
+ - /ci/base.gitlab-ci.yml
+ - /ci/allure-report.yml
+ - /ci/knapsack-report.yml
+
+stages:
+ - test
+ - report
+ - notify
+
+.qa-install:
+ variables:
+ BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES: "true"
+ BUNDLE_SILENCE_ROOT_WARNING: "true"
+ RUN_WITH_BUNDLE: "true" # instructs pipeline to install and run gitlab-qa gem via bundler
+ QA_PATH: qa # sets the optional path for bundler to run from
+ extends:
+ - .gitlab-qa-install
dont-interrupt-me:
extends: .rules:dont-interrupt
@@ -11,6 +32,25 @@ dont-interrupt-me:
script:
- echo "This jobs makes sure this pipeline won't be interrupted! See https://docs.gitlab.com/ee/ci/yaml/#interruptible."
+download-knapsack-report:
+ extends:
+ - .gitlab-qa-image
+ - .rules:download-knapsack
+ stage: .pre
+ variables:
+ KNAPSACK_DIR: ${CI_PROJECT_DIR}/qa/knapsack
+ GIT_STRATEGY: none
+ script:
+ # when using qa-image, code runs in /home/gitlab/qa folder
+ - bundle exec rake "knapsack:download[test]"
+ - mkdir -p "$KNAPSACK_DIR" && cp knapsack/*.json "${KNAPSACK_DIR}/"
+ - echo "$PROCESS_TEST_RESULTS"
+ allow_failure: true
+ artifacts:
+ paths:
+ - qa/knapsack/*.json
+ expire_in: 1 day
+
.run-tests:
stage: test
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}
@@ -25,15 +65,15 @@ dont-interrupt-me:
DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375
QA_GDK_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-qa-gdk:master"
- QA_GENERATE_ALLURE_REPORT: "false"
+ QA_GENERATE_ALLURE_REPORT: "true"
QA_CAN_TEST_PRAEFECT: "false"
QA_INTERCEPT_REQUESTS: "false"
- QA_RUN_TYPE: e2e-test-on-gdk
TEST_LICENSE_MODE: $QA_TEST_LICENSE_MODE
EE_LICENSE: $QA_EE_LICENSE
GITHUB_ACCESS_TOKEN: $QA_GITHUB_ACCESS_TOKEN
GITLAB_QA_ADMIN_ACCESS_TOKEN: $QA_ADMIN_ACCESS_TOKEN
QA_KNAPSACK_REPORTS: qa-smoke,ee-instance-parallel
+ RSPEC_REPORT_OPTS: "--format QA::Support::JsonFormatter --out tmp/rspec-${CI_JOB_ID}.json --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml --format html --out tmp/rspec-${CI_JOB_ID}.htm --color --format documentation"
timeout: 2 hours
artifacts:
when: always
@@ -41,6 +81,8 @@ dont-interrupt-me:
- test_output
- logs
expire_in: 7 days
+ reports:
+ junit: test_output/**/rspec-*.xml
script:
- echo -e "\e[0Ksection_start:`date +%s`:pull_image\r\e[0KPull GDK QA image"
- docker pull ${QA_GDK_IMAGE}
@@ -57,8 +99,8 @@ dont-interrupt-me:
--volume $CI_PROJECT_DIR/test_output:/home/gdk/gdk/gitlab/qa/tmp:z \
--volume $CI_PROJECT_DIR/logs/gdk:/home/gdk/gdk/log \
--volume $CI_PROJECT_DIR/logs/gitlab:/home/gdk/gdk/gitlab/log \
- ${QA_GDK_IMAGE} "${CI_COMMIT_SHA}" "$TEST_GDK_TAGS --tag ~requires_praefect" || true
- - echo -e "\e[0Ksection_end:`date +%s`:launch_gdk_and_tests\r\e[0K"
+ ${QA_GDK_IMAGE} "${CI_COMMIT_SHA}" "$RSPEC_REPORT_OPTS $TEST_GDK_TAGS --tag ~requires_praefect"
+ # The above image's launch script takes two arguments only - first one is the commit sha and the second one Rspec Args
allow_failure: true
test-on-gdk-smoke:
@@ -79,3 +121,98 @@ test-on-gdk-full:
QA_KNAPSACK_REPORT_NAME: ee-instance-parallel
rules:
- when: manual
+
+# ==========================================
+# Post test stage
+# ==========================================
+e2e-test-report:
+ extends:
+ - .generate-allure-report-base
+ - .rules:report:allure-report
+ stage: report
+ variables:
+ ALLURE_JOB_NAME: e2e-test-on-gdk
+ ALLURE_RESULTS_GLOB: test_output/allure-results
+ GITLAB_AUTH_TOKEN: $PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE
+ ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
+ ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
+
+upload-knapsack-report:
+ extends:
+ - .generate-knapsack-report-base
+ - .qa-install
+ - .ruby-qa-image
+ - .rules:report:process-results
+ variables:
+ QA_KNAPSACK_REPORT_FILE_PATTERN: $CI_PROJECT_DIR/test_output/knapsack/*/*.json
+ stage: report
+ when: always
+
+export-test-metrics:
+ extends:
+ - .qa-install
+ - .ruby-qa-image
+ - .rules:report:process-results
+ stage: report
+ when: always
+ script:
+ - pwd
+ - bundle exec rake "ci:export_test_metrics[$CI_PROJECT_DIR/test_output/test-metrics-*.json]"
+
+relate-test-failures:
+ extends:
+ - .qa-install
+ - .ruby-qa-image
+ - .rules:report:process-results
+ stage: report
+ variables:
+ QA_FAILURES_REPORTING_PROJECT: gitlab-org/gitlab
+ QA_FAILURES_MAX_DIFF_RATIO: "0.15"
+ GITLAB_QA_ACCESS_TOKEN: $QA_GITLAB_CI_TOKEN
+ when: on_failure
+ script:
+ - |
+ bundle exec gitlab-qa-report \
+ --relate-failure-issue "$CI_PROJECT_DIR/test_output/rspec-*.json" \
+ --project "$QA_FAILURES_REPORTING_PROJECT" \
+ --max-diff-ratio "$QA_FAILURES_MAX_DIFF_RATIO"
+
+generate-test-session:
+ extends:
+ - .qa-install
+ - .ruby-qa-image
+ - .rules:report:process-results
+ stage: report
+ variables:
+ QA_TESTCASE_SESSIONS_PROJECT: gitlab-org/quality/testcase-sessions
+ GITLAB_QA_ACCESS_TOKEN: $QA_TEST_SESSION_TOKEN
+ GITLAB_CI_API_TOKEN: $QA_GITLAB_CI_TOKEN
+ when: always
+ script:
+ - |
+ bundle exec gitlab-qa-report \
+ --generate-test-session "$CI_PROJECT_DIR/test_output/rspec-*.json" \
+ --project "$QA_TESTCASE_SESSIONS_PROJECT"
+ artifacts:
+ when: always
+ expire_in: 1d
+ paths:
+ - qa/REPORT_ISSUE_URL
+
+notify-slack:
+ extends:
+ - .notify-slack-qa
+ - .qa-install
+ - .ruby-qa-image
+ - .rules:report:process-results
+ stage: notify
+ variables:
+ ALLURE_JOB_NAME: e2e-test-on-gdk
+ SLACK_ICON_EMOJI: ci_failing
+ STATUS_SYM: ☠️
+ STATUS: failed
+ TYPE: "(e2e-test-on-gdk) "
+ when: on_failure
+ script:
+ - bundle exec gitlab-qa-report --prepare-stage-reports "$CI_PROJECT_DIR/test_output/rspec-*.xml" # generate summary
+ - !reference [.notify-slack-qa, script]
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 5471e4e7ea7..29417b7a380 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -450,11 +450,11 @@
{"name":"premailer","version":"1.16.0","platform":"ruby","checksum":"03e4402c448e6bae13fb5f6301a8bde4f3508e1bff90ae7c0972c7be94694786"},
{"name":"premailer-rails","version":"1.10.3","platform":"ruby","checksum":"7cdcb97027866f7a81c490c6d15ada7f39666b5f6375f0821b7e97e0483b112f"},
{"name":"proc_to_ast","version":"0.1.0","platform":"ruby","checksum":"92a73fa66e2250a83f8589f818b0751bcf227c68f85916202df7af85082f8691"},
-{"name":"prometheus-client-mmap","version":"0.23.0","platform":"aarch64-linux","checksum":"1f17600960e3d779ccf40da9e0603d8d754eceff6addb2f5b8482b10e93c980a"},
-{"name":"prometheus-client-mmap","version":"0.23.0","platform":"arm64-darwin","checksum":"8b1872583814e9d8bbf81032c67412fcec7a8130a2d1e7e4a5784cd3c7300a89"},
-{"name":"prometheus-client-mmap","version":"0.23.0","platform":"ruby","checksum":"e90b353fc583e0a317a3aeae02439a96e86d2193d4a3a31b733cafc6e4d6280b"},
-{"name":"prometheus-client-mmap","version":"0.23.0","platform":"x86_64-darwin","checksum":"d73d3a2b1f1d0f3e1f4b9b8b06c4471f1cf951a7e6b38b83f2f3eb4816500cac"},
-{"name":"prometheus-client-mmap","version":"0.23.0","platform":"x86_64-linux","checksum":"35dc742f4b3a718bda62c80cd618c0ec42accabf58f8330d92e39f67be58a0bf"},
+{"name":"prometheus-client-mmap","version":"0.23.1","platform":"aarch64-linux","checksum":"4091121090d1d44747b3d09f2dbd5fdd61e274d557b8ed98b06c65cdd006d174"},
+{"name":"prometheus-client-mmap","version":"0.23.1","platform":"arm64-darwin","checksum":"fa54f230631852392b38cba1ad396c0472cb9f088eef563d0c381b19b1333855"},
+{"name":"prometheus-client-mmap","version":"0.23.1","platform":"ruby","checksum":"48545f23217a5e85ca79fa8c2563711e319debdae46ddbd6348ff37f48029c40"},
+{"name":"prometheus-client-mmap","version":"0.23.1","platform":"x86_64-darwin","checksum":"99b56f4017f0a1a062914da253c613b9957bfabf5b38af5012e3d8515ed49555"},
+{"name":"prometheus-client-mmap","version":"0.23.1","platform":"x86_64-linux","checksum":"624da747dbb97e0d88be1f2ba5ae5253941fc85dea875845f5b4c7a2c95ee032"},
{"name":"pry","version":"0.14.2","platform":"java","checksum":"fd780670977ba04ff7ee32dabd4d02fe4bf02e977afe8809832d5dca1412862e"},
{"name":"pry","version":"0.14.2","platform":"ruby","checksum":"c4fe54efedaca1d351280b45b8849af363184696fcac1c72e0415f9bdac4334d"},
{"name":"pry-byebug","version":"3.10.1","platform":"ruby","checksum":"c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8"},
diff --git a/Gemfile.lock b/Gemfile.lock
index e2054d98a13..e4f49206276 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1154,7 +1154,7 @@ GEM
coderay
parser
unparser
- prometheus-client-mmap (0.23.0)
+ prometheus-client-mmap (0.23.1)
rb_sys (~> 0.9)
pry (0.14.2)
coderay (~> 1.1)
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
index d32e90f3adb..48e5bb8f0b2 100644
--- a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
@@ -114,8 +114,11 @@ export default {
showDeleteDropdown() {
return this.group.dependencyProxyManifests?.nodes.length > 0 && this.canClearCache;
},
+ dependencyProxyImagePrefix() {
+ return this.group.dependencyProxyImagePrefix;
+ },
showDependencyProxyImagePrefix() {
- return this.group.dependencyProxyImagePrefix?.length > 0;
+ return this.dependencyProxyImagePrefix?.length > 0;
},
},
methods: {
@@ -208,6 +211,7 @@ export default {
<manifests-list
v-if="manifests && manifests.length"
+ :dependency-proxy-image-prefix="dependencyProxyImagePrefix"
:loading="$apollo.queries.group.loading"
:manifests="manifests"
:pagination="pageInfo"
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifest_row.vue b/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifest_row.vue
index 1bbd0c32dc4..254fd578cf1 100644
--- a/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifest_row.vue
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifest_row.vue
@@ -3,11 +3,16 @@ import { GlIcon, GlSprintf } from '@gitlab/ui';
import { MANIFEST_PENDING_DESTRUCTION_STATUS } from '~/packages_and_registries/dependency_proxy/constants';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { s__ } from '~/locale';
+const SHORT_DIGEST_START_INDEX = 7;
+const SHORT_DIGEST_END_INDEX = 14;
+
export default {
name: 'ManifestRow',
components: {
+ ClipboardButton,
GlIcon,
GlSprintf,
ListItem,
@@ -18,13 +23,25 @@ export default {
type: Object,
required: true,
},
+ dependencyProxyImagePrefix: {
+ type: String,
+ default: '',
+ required: false,
+ },
},
computed: {
name() {
- return this.manifest?.imageName.split(':')[0];
+ if (this.containsDigestInImageName) {
+ return this.manifest?.imageName.split(':')[0];
+ }
+ return this.manifest?.imageName;
},
- version() {
- return this.manifest?.imageName.split(':')[1];
+ imageCopyText() {
+ const name = this.manifest?.imageName.replace(':sha256:', '@sha256:') ?? '';
+ return `${this.dependencyProxyImagePrefix}/${name}`;
+ },
+ containsDigestInImageName() {
+ return this.manifest?.imageName.includes(':sha256:');
},
isErrorStatus() {
return this.manifest?.status === MANIFEST_PENDING_DESTRUCTION_STATUS;
@@ -32,9 +49,16 @@ export default {
disabledRowStyle() {
return this.isErrorStatus ? 'gl-font-weight-normal gl-text-gray-500' : '';
},
+ shortDigest() {
+ // digest is in the format `sha256:995efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089`
+ // for short digest, remove sha256: from the string, and show only the first 7 char
+ return this.manifest.digest?.substring(SHORT_DIGEST_START_INDEX, SHORT_DIGEST_END_INDEX);
+ },
},
i18n: {
cachedAgoMessage: s__('DependencyProxy|Cached %{time}'),
+ copyImagePathTitle: s__('DependencyProxy|Copy image path'),
+ digestLabel: s__('DependencyProxy|Digest: %{shortDigest}'),
scheduledForDeletion: s__('DependencyProxy|Scheduled for deletion'),
},
};
@@ -44,9 +68,21 @@ export default {
<list-item :disabled="isErrorStatus">
<template #left-primary>
<span :class="disabledRowStyle">{{ name }}</span>
+ <clipboard-button
+ class="gl-ml-2"
+ :text="imageCopyText"
+ :title="$options.i18n.copyImagePathTitle"
+ category="tertiary"
+ />
</template>
<template #left-secondary>
- {{ version }}
+ <span data-testid="manifest-row-short-digest">
+ <gl-sprintf :message="$options.i18n.digestLabel">
+ <template #shortDigest>
+ {{ shortDigest }}
+ </template>
+ </gl-sprintf>
+ </span>
<span v-if="isErrorStatus" class="gl-ml-4" data-testid="status"
><gl-icon name="clock" /> {{ $options.i18n.scheduledForDeletion }}</span
>
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifests_list.vue b/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifests_list.vue
index 0d9b8330fe3..9870841f1ff 100644
--- a/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifests_list.vue
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/components/manifests_list.vue
@@ -25,6 +25,11 @@ export default {
required: false,
default: () => false,
},
+ dependencyProxyImagePrefix: {
+ type: String,
+ default: '',
+ required: false,
+ },
},
i18n: {
listTitle: s__('DependencyProxy|Image list'),
@@ -45,7 +50,12 @@ export default {
<div
class="gl-border-t-1 gl-border-gray-100 gl-border-t-solid gl-display-flex gl-flex-direction-column"
>
- <manifest-row v-for="(manifest, index) in manifests" :key="index" :manifest="manifest" />
+ <manifest-row
+ v-for="(manifest, index) in manifests"
+ :key="index"
+ :dependency-proxy-image-prefix="dependencyProxyImagePrefix"
+ :manifest="manifest"
+ />
</div>
<div class="gl-display-flex gl-justify-content-center">
<gl-keyset-pagination
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql b/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql
index c1597625964..db0e596ba64 100644
--- a/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql
@@ -19,6 +19,7 @@ query getDependencyProxyDetails(
nodes {
id
createdAt
+ digest
imageName
status
}
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index 1d8c5b30e13..55146610d5f 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -137,7 +137,7 @@ export default {
</div>
<slot name="actions"></slot>
<span v-if="hasPill || isPinnable" class="gl-flex-grow-1 gl-text-right gl-mr-3">
- <gl-badge v-if="hasPill" size="sm" variant="info">
+ <gl-badge v-if="hasPill" size="sm" variant="neutral">
{{ pillData }}
</gl-badge>
<gl-button
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 8b51322f1dc..a537e52ccf6 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -449,6 +449,9 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
validates :silent_mode_enabled,
inclusion: { in: [true, false], message: N_('must be a boolean value') }
+ validates :remember_me_enabled,
+ inclusion: { in: [true, false], message: N_('must be a boolean value') }
+
Gitlab::SSHPublicKey.supported_types.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
end
diff --git a/app/models/concerns/enums/package_metadata.rb b/app/models/concerns/enums/package_metadata.rb
index a866e2b995a..69108ae9cc4 100644
--- a/app/models/concerns/enums/package_metadata.rb
+++ b/app/models/concerns/enums/package_metadata.rb
@@ -17,6 +17,11 @@ module Enums
cbl_mariner: 12
}.with_indifferent_access.freeze
+ ADVISORY_SOURCES = {
+ glad: 1, # gitlab advisory db
+ trivy: 2
+ }.with_indifferent_access.freeze
+
def self.purl_types
PURL_TYPES
end
@@ -24,5 +29,9 @@ module Enums
def self.purl_types_numerical
purl_types.invert
end
+
+ def self.advisory_sources
+ ADVISORY_SOURCES
+ end
end
end
diff --git a/db/docs/pm_advisories.yml b/db/docs/pm_advisories.yml
new file mode 100644
index 00000000000..010f029978e
--- /dev/null
+++ b/db/docs/pm_advisories.yml
@@ -0,0 +1,11 @@
+---
+table_name: pm_advisories
+classes:
+- PackageMetadata::Advisory
+feature_categories:
+- software_composition_analysis
+- container_scanning
+description: Stores security advisories.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117326
+milestone: '16.0'
+gitlab_schema: gitlab_pm
diff --git a/db/docs/pm_affected_packages.yml b/db/docs/pm_affected_packages.yml
new file mode 100644
index 00000000000..ad389c72b59
--- /dev/null
+++ b/db/docs/pm_affected_packages.yml
@@ -0,0 +1,11 @@
+---
+table_name: pm_affected_packages
+classes:
+- PackageMetadata::AffectedPackage
+feature_categories:
+- software_composition_analysis
+- container_scanning
+description: Stores info for packages affected by an advisory.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117326
+milestone: '16.0'
+gitlab_schema: gitlab_pm
diff --git a/db/migrate/20230411205121_create_package_metadata_advisory_info.rb b/db/migrate/20230411205121_create_package_metadata_advisory_info.rb
new file mode 100644
index 00000000000..adfc8e868f7
--- /dev/null
+++ b/db/migrate/20230411205121_create_package_metadata_advisory_info.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+class CreatePackageMetadataAdvisoryInfo < Gitlab::Database::Migration[2.1]
+ def change
+ create_table :pm_advisories do |t|
+ t.text :advisory_xid, limit: 36, null: false
+ t.date :published_date, null: false
+ t.timestamps_with_timezone null: false
+ t.integer :source_xid, limit: 2, null: false
+
+ t.text :title, limit: 256
+ t.text :description, limit: 8192
+ t.text :cvss_v2, limit: 128
+ t.text :cvss_v3, limit: 128
+ t.text :urls, array: true, default: []
+ t.jsonb :identifiers, null: false
+
+ t.index [:advisory_xid, :source_xid], unique: true
+ t.check_constraint 'CARDINALITY(urls) <= 10'
+ end
+
+ create_table :pm_affected_packages do |t|
+ t.references :pm_advisory, index: true, foreign_key: { on_delete: :cascade }, null: false
+ t.timestamps_with_timezone null: false
+ t.integer :purl_type, limit: 2, null: false
+
+ t.text :package_name, limit: 256, null: false
+ t.text :distro_version, limit: 256, null: true
+ t.text :solution, limit: 2048, null: true
+ t.text :affected_range, limit: 512, null: false
+ t.text :fixed_versions, array: true, default: []
+ t.jsonb :overridden_advisory_fields, null: false, default: {}
+
+ t.index [:pm_advisory_id, :purl_type, :package_name, :distro_version], unique: true,
+ name: 'i_affected_packages_unique_for_upsert'
+ t.check_constraint 'CARDINALITY(fixed_versions) <= 10'
+ end
+ end
+end
diff --git a/db/migrate/20230501163253_add_remember_me_enabled_to_application_settings.rb b/db/migrate/20230501163253_add_remember_me_enabled_to_application_settings.rb
new file mode 100644
index 00000000000..40c4ccd9f26
--- /dev/null
+++ b/db/migrate/20230501163253_add_remember_me_enabled_to_application_settings.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddRememberMeEnabledToApplicationSettings < Gitlab::Database::Migration[2.1]
+ def change
+ add_column :application_settings, :remember_me_enabled, :boolean, default: true, null: false
+ end
+end
diff --git a/db/schema_migrations/20230411205121 b/db/schema_migrations/20230411205121
new file mode 100644
index 00000000000..b90bda11c27
--- /dev/null
+++ b/db/schema_migrations/20230411205121
@@ -0,0 +1 @@
+cb1766c3c3b6604353dfcb774b8b4f3fe65dac10d15312785855153769ac6fe0 \ No newline at end of file
diff --git a/db/schema_migrations/20230501163253 b/db/schema_migrations/20230501163253
new file mode 100644
index 00000000000..e0f178a65a7
--- /dev/null
+++ b/db/schema_migrations/20230501163253
@@ -0,0 +1 @@
+e13f88c8de95d10e1150b07e6d112aaa9221e0a866fce3f92883cec9ee026acd \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 47569c39a8d..8516c54c821 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -11844,6 +11844,7 @@ CREATE TABLE application_settings (
encrypted_tofa_client_library_fetch_access_token_method_iv bytea,
encrypted_tofa_access_token_expires_in bytea,
encrypted_tofa_access_token_expires_in_iv bytea,
+ remember_me_enabled boolean DEFAULT true NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
@@ -20224,6 +20225,64 @@ CREATE SEQUENCE plans_id_seq
ALTER SEQUENCE plans_id_seq OWNED BY plans.id;
+CREATE TABLE pm_advisories (
+ id bigint NOT NULL,
+ advisory_xid text NOT NULL,
+ published_date date NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ source_xid smallint NOT NULL,
+ title text,
+ description text,
+ cvss_v2 text,
+ cvss_v3 text,
+ urls text[] DEFAULT '{}'::text[],
+ identifiers jsonb NOT NULL,
+ CONSTRAINT check_152def3868 CHECK ((char_length(cvss_v2) <= 128)),
+ CONSTRAINT check_19cbd06439 CHECK ((char_length(advisory_xid) <= 36)),
+ CONSTRAINT check_bed97fa77a CHECK ((char_length(cvss_v3) <= 128)),
+ CONSTRAINT check_e4bfd3ffbf CHECK ((char_length(title) <= 256)),
+ CONSTRAINT check_fee880f7aa CHECK ((char_length(description) <= 8192)),
+ CONSTRAINT chk_rails_e73af9de76 CHECK ((cardinality(urls) <= 10))
+);
+
+CREATE SEQUENCE pm_advisories_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE pm_advisories_id_seq OWNED BY pm_advisories.id;
+
+CREATE TABLE pm_affected_packages (
+ id bigint NOT NULL,
+ pm_advisory_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ purl_type smallint NOT NULL,
+ package_name text NOT NULL,
+ distro_version text,
+ solution text,
+ affected_range text NOT NULL,
+ fixed_versions text[] DEFAULT '{}'::text[],
+ overridden_advisory_fields jsonb DEFAULT '{}'::jsonb NOT NULL,
+ CONSTRAINT check_5dd528a2be CHECK ((char_length(package_name) <= 256)),
+ CONSTRAINT check_80dea16c7b CHECK ((char_length(affected_range) <= 512)),
+ CONSTRAINT check_d1d4646298 CHECK ((char_length(solution) <= 2048)),
+ CONSTRAINT check_ec4c8efb5e CHECK ((char_length(distro_version) <= 256)),
+ CONSTRAINT chk_rails_a0f80d74e0 CHECK ((cardinality(fixed_versions) <= 10))
+);
+
+CREATE SEQUENCE pm_affected_packages_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE pm_affected_packages_id_seq OWNED BY pm_affected_packages.id;
+
CREATE TABLE pm_checkpoints (
sequence integer NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -25483,6 +25542,10 @@ ALTER TABLE ONLY plan_limits ALTER COLUMN id SET DEFAULT nextval('plan_limits_id
ALTER TABLE ONLY plans ALTER COLUMN id SET DEFAULT nextval('plans_id_seq'::regclass);
+ALTER TABLE ONLY pm_advisories ALTER COLUMN id SET DEFAULT nextval('pm_advisories_id_seq'::regclass);
+
+ALTER TABLE ONLY pm_affected_packages ALTER COLUMN id SET DEFAULT nextval('pm_affected_packages_id_seq'::regclass);
+
ALTER TABLE ONLY pm_licenses ALTER COLUMN id SET DEFAULT nextval('pm_licenses_id_seq'::regclass);
ALTER TABLE ONLY pm_package_version_licenses ALTER COLUMN id SET DEFAULT nextval('pm_package_version_licenses_id_seq'::regclass);
@@ -27777,6 +27840,12 @@ ALTER TABLE ONLY plan_limits
ALTER TABLE ONLY plans
ADD CONSTRAINT plans_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY pm_advisories
+ ADD CONSTRAINT pm_advisories_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY pm_affected_packages
+ ADD CONSTRAINT pm_affected_packages_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY pm_checkpoints
ADD CONSTRAINT pm_checkpoints_pkey PRIMARY KEY (purl_type);
@@ -29387,6 +29456,8 @@ CREATE UNIQUE INDEX finding_link_url_idx ON vulnerability_finding_links USING bt
CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id);
+CREATE UNIQUE INDEX i_affected_packages_unique_for_upsert ON pm_affected_packages USING btree (pm_advisory_id, purl_type, package_name, distro_version);
+
CREATE INDEX i_batched_background_migration_job_transition_logs_on_job_id ON ONLY batched_background_migration_job_transition_logs USING btree (batched_background_migration_job_id);
CREATE UNIQUE INDEX i_bulk_import_export_batches_id_batch_number ON bulk_import_export_batches USING btree (export_id, batch_number);
@@ -31863,6 +31934,10 @@ CREATE UNIQUE INDEX index_plan_limits_on_plan_id ON plan_limits USING btree (pla
CREATE UNIQUE INDEX index_plans_on_name ON plans USING btree (name);
+CREATE UNIQUE INDEX index_pm_advisories_on_advisory_xid_and_source_xid ON pm_advisories USING btree (advisory_xid, source_xid);
+
+CREATE INDEX index_pm_affected_packages_on_pm_advisory_id ON pm_affected_packages USING btree (pm_advisory_id);
+
CREATE INDEX index_pm_package_version_licenses_on_pm_license_id ON pm_package_version_licenses USING btree (pm_license_id);
CREATE INDEX index_pm_package_version_licenses_on_pm_package_version_id ON pm_package_version_licenses USING btree (pm_package_version_id);
@@ -35735,6 +35810,9 @@ ALTER TABLE ONLY gpg_signatures
ALTER TABLE ONLY project_authorizations
ADD CONSTRAINT fk_rails_11e7aa3ed9 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY pm_affected_packages
+ ADD CONSTRAINT fk_rails_1279c1b9a1 FOREIGN KEY (pm_advisory_id) REFERENCES pm_advisories(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY description_versions
ADD CONSTRAINT fk_rails_12b144011c FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
diff --git a/doc/administration/package_information/defaults.md b/doc/administration/package_information/defaults.md
index 932342a6a92..68958102d4e 100644
--- a/doc/administration/package_information/defaults.md
+++ b/doc/administration/package_information/defaults.md
@@ -50,7 +50,8 @@ by default:
| Consul | No | Port | X | 8300, 8301(UDP), 8500, 8600[^Consul-notes] |
| Patroni | No | Port | X | 8008 |
| GitLab KAS | Yes | Port | X | 8150 |
-| Gitaly | No | Port | X | 8075 |
+| Gitaly | No | Port | X | 8075 or 9999 (TLS) |
+| Praefect | No | Port | X | 2305 or 3305 (TLS) |
Legend:
diff --git a/doc/development/testing_guide/end_to_end/execution_context_selection.md b/doc/development/testing_guide/end_to_end/execution_context_selection.md
index 23b8ab7287b..4d83884f4d0 100644
--- a/doc/development/testing_guide/end_to_end/execution_context_selection.md
+++ b/doc/development/testing_guide/end_to_end/execution_context_selection.md
@@ -34,7 +34,8 @@ Matches use:
- Regex for environments.
- String matching for pipelines.
-- Regex or string matching for jobs.
+- Regex or string matching for jobs
+- Lambda or truthy/falsey value for generic condition
| Test execution context | Key | Matches |
| ---------------- | --- | --------------- |
@@ -47,6 +48,7 @@ Matches use:
| The `nightly` and `canary` pipelines | `only: { pipeline: [:nightly, :canary] }` | ["nightly"](https://gitlab.com/gitlab-org/quality/nightly) and ["canary"](https://gitlab.com/gitlab-org/quality/canary) |
| The `ee:instance` job | `only: { job: 'ee:instance' }` | The `ee:instance` job in any pipeline |
| Any `quarantine` job | `only: { job: '.*quarantine' }` | Any job ending in `quarantine` in any pipeline |
+| Any run where condition evaluates to a truthy value | `only: { condition: -> { ENV['TEST_ENV'] == 'true' } }` | Any run where `TEST_ENV` is set to true
```ruby
RSpec.describe 'Area' do
@@ -62,6 +64,8 @@ RSpec.describe 'Area' do
it 'runs only in nightly pipeline', only: { pipeline: :nightly } do; end
it 'runs in nightly and canary pipelines', only: { pipeline: [:nightly, :canary] } do; end
+
+ it 'runs in specific environment matching condition', only: { condition: -> { ENV['TEST_ENV'] == 'true' } } do; end
end
```
@@ -73,7 +77,8 @@ Matches use:
- Regex for environments.
- String matching for pipelines.
-- Regex or string matching for jobs.
+- Regex or string matching for jobs
+- Lambda or truthy/falsey value for generic condition
| Test execution context | Key | Matches |
| ---------------- | --- | --------------- |
@@ -86,6 +91,7 @@ Matches use:
| The `nightly` and `canary` pipelines | `except: { pipeline: [:nightly, :canary] }` | ["nightly"](https://gitlab.com/gitlab-org/quality/nightly) and ["canary"](https://gitlab.com/gitlab-org/quality/canary) |
| The `ee:instance` job | `except: { job: 'ee:instance' }` | The `ee:instance` job in any pipeline |
| Any `quarantine` job | `except: { job: '.*quarantine' }` | Any job ending in `quarantine` in any pipeline |
+| Any run except where condition evaluates to a truthy value | `except: { condition: -> { ENV['TEST_ENV'] == 'true' } }` | Any run where `TEST_ENV` is not set to true
```ruby
RSpec.describe 'Area' do
@@ -96,6 +102,8 @@ RSpec.describe 'Area' do
it 'runs in any execution context except the nightly pipeline', except: { pipeline: :nightly } do; end
it 'runs in any execution context except the ee:instance job', except: { job: 'ee:instance' } do; end
+
+ it 'runs in specific environment not matching condition', except: { condition: -> { ENV['TEST_ENV'] == 'true' } } do; end
end
```
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 90a7d5e72ae..d3edb7dd932 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -615,6 +615,7 @@ flags are added to the scanner's CLI options.
| Analyzer | CLI option | Description |
|------------------------------------------------------------------------------|--------------------|------------------------------------------------------------------------------|
| [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) | `--max-memory` | Sets the maximum system memory to use when running a rule on a single file. Measured in MB. |
+| [Flawfinder](https://gitlab.com/gitlab-org/security-products/analyzers/flawfinder) | `--neverignore` | Never ignore security issues, even if they have an “ignore” directive in a comment. Adding this option is likely to result in the analyzer detecting additional vulnerability findings which cannot be automatically resolved. |
#### Custom CI/CD variables
diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md
index 78de8df5cb9..c5e10d7a2df 100644
--- a/doc/user/application_security/secret_detection/index.md
+++ b/doc/user/application_security/secret_detection/index.md
@@ -460,6 +460,41 @@ For more information on the syntax of passthroughs, see the
[passthroughs section on the SAST customize rulesets](../sast/customize_rulesets.md#the-analyzerpassthrough-section)
page.
+#### Extending the default configuration
+
+You can extend the default configuration with additional changes by using [Gitleaks `extend` support](https://github.com/gitleaks/gitleaks#configuration).
+
+In the following `file` passthrough example, the string `glpat-1234567890abcdefghij` is ignored by Secret Detection. That GitLab personal access token (PAT) is used in test cases. Detection of it would be a false positive.
+
+The `secret-detection-ruleset.toml` file defines that the configuration in `extended-gitleaks-config.toml` file is to be included. The `extended-gitleaks-config.toml` file defines the custom Gitleaks configuration. The `allowlist` stanza defines a regular expression that matches the secret that is to be ignored ("allowed").
+
+```toml
+# .gitlab/secret-detection-ruleset.toml
+[secrets]
+ description = 'secrets custom rules configuration'
+
+ [[secrets.passthrough]]
+ type = "file"
+ target = "gitleaks.toml"
+ value = "extended-gitleaks-config.toml"
+```
+
+```toml
+# extended-gitleaks-config.toml
+title = "extension of gitlab's default gitleaks config"
+
+[extend]
+# Extends default packaged path
+path = "/gitleaks.toml"
+
+[allowlist]
+ description = "allow list of test tokens to ignore in detection"
+ regexTarget = "match"
+ regexes = [
+ '''glpat-1234567890abcdefghij''',
+ ]
+```
+
## Running Secret Detection in an offline environment **(PREMIUM SELF)**
An offline environment has limited, restricted, or intermittent access to external resources through
diff --git a/doc/user/compliance/index.md b/doc/user/compliance/index.md
index 7981ab44bf9..ad36684d987 100644
--- a/doc/user/compliance/index.md
+++ b/doc/user/compliance/index.md
@@ -7,6 +7,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Compliance **(ULTIMATE)**
-The compliance tools provided by GitLab help you keep an eye on various aspects of your project. For more information
-on GitLab compliance features for projects, groups, and instances, see
+The compliance tools provided by GitLab help you keep an eye on various aspects of your project, including:
+
+- [Compliance report](compliance_report/index.md).
+- [License approval policies](license_approval_policies.md).
+- [License list](license_list.md).
+- [License scanning of CycloneDX files](license_scanning_of_cyclonedx_files/index.md).
+
+For more information on other GitLab compliance features for projects, groups, and instances, see
[Compliance features](../../administration/compliance.md).
diff --git a/doc/user/compliance/license_approval_policies.md b/doc/user/compliance/license_approval_policies.md
index 4e10d01b18e..860c2008021 100644
--- a/doc/user/compliance/license_approval_policies.md
+++ b/doc/user/compliance/license_approval_policies.md
@@ -5,15 +5,15 @@ group: Security Policies
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
---
-# License Approval Policies **(ULTIMATE)**
+# License approval policies **(ULTIMATE)**
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8092) in GitLab 15.9 [with a flag](../../administration/feature_flags.md) named `license_scanning_policies`.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/397644) in GitLab 15.11. Feature flag `license_scanning_policies` removed.
-License Approval Policies allow you to specify multiple types of criteria that define when approval is required before a merge request can be merged in.
+License approval policies allow you to specify multiple types of criteria that define when approval is required before a merge request can be merged in.
NOTE:
-License Approval Policies are applicable only to [protected](../project/protected_branches.md) target branches.
+License approval policies are applicable only to [protected](../project/protected_branches.md) target branches.
The following video provides an overview of these policies.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 76cd90943ae..1ee59dde1f0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -14574,6 +14574,9 @@ msgstr ""
msgid "DependencyProxy|Contains %{count} blobs of images (%{size})"
msgstr ""
+msgid "DependencyProxy|Copy image path"
+msgstr ""
+
msgid "DependencyProxy|Copy prefix"
msgstr ""
@@ -14583,6 +14586,9 @@ msgstr ""
msgid "DependencyProxy|Dependency Proxy image prefix"
msgstr ""
+msgid "DependencyProxy|Digest: %{shortDigest}"
+msgstr ""
+
msgid "DependencyProxy|Enable Dependency Proxy"
msgstr ""
@@ -21044,7 +21050,7 @@ msgstr ""
msgid "GroupSAML|SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr ""
-msgid "GroupSAML|Some todos may be hidden because your SAML session has expired. Click to reauthenticate with the following groups to view hidden todos:"
+msgid "GroupSAML|Some to-do items may be hidden because your SAML session has expired. Select the group’s path to reauthenticate and view the hidden to-do items."
msgstr ""
msgid "GroupSAML|The SCIM token is now hidden. To see the value of the token again, you need to %{linkStart}reset it%{linkEnd}."
diff --git a/qa/gdk/launch b/qa/gdk/launch
index 8ad2ce7e5ad..5d123906f04 100755
--- a/qa/gdk/launch
+++ b/qa/gdk/launch
@@ -37,4 +37,4 @@ bundle install --jobs=$(nproc) --retry=3 --quiet
# Run the tests
bundle exec rake "knapsack:download[test]"
-bundle exec bin/qa Test::Instance::All http://gdk.test:3000 -- $RSPEC_ARGS || true
+bundle exec bin/qa Test::Instance::All http://gdk.test:3000 -- $RSPEC_ARGS
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
index cf9282c1149..54fb2aca990 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
@@ -43,7 +43,12 @@ module QA
it(
'allows enforcing 2FA via UI and logging in with 2FA',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931',
+ quarantine: {
+ type: :bug,
+ only: { condition: -> { QA::Runtime::Env.super_sidebar_enabled? } },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/409336'
+ }
) do
enforce_two_factor_authentication_on_group(group)
diff --git a/qa/qa/specs/helpers/context_selector.rb b/qa/qa/specs/helpers/context_selector.rb
index 59ab7c9722e..2527895dc4d 100644
--- a/qa/qa/specs/helpers/context_selector.rb
+++ b/qa/qa/specs/helpers/context_selector.rb
@@ -10,7 +10,11 @@ module QA
def except?(*options)
return false if Runtime::Env.ci_job_name.blank? && options.any? { |o| o.is_a?(Hash) && o[:job].present? }
- return false if Runtime::Env.ci_project_name.blank? && options.any? { |o| o.is_a?(Hash) && o[:pipeline].present? }
+
+ return false if Runtime::Env.ci_project_name.blank? && options.any? do |o|
+ o.is_a?(Hash) && o[:pipeline].present?
+ end
+
return false if Runtime::Scenario.attributes[:gitlab_address].blank?
context_matches?(*options)
@@ -34,24 +38,13 @@ module QA
opts.merge!(option)
if option[:pipeline].present?
- return true if Runtime::Env.ci_project_name.blank?
-
- return pipeline_matches?(option[:pipeline])
-
+ return evaluate_pipeline_context(option[:pipeline])
elsif option[:job].present?
- return true if Runtime::Env.ci_job_name.blank?
-
- return job_matches?(option[:job])
-
+ return evaluate_job_context(option[:job])
+ elsif !option[:condition].nil?
+ return evaluate_generic_condition(option[:condition])
elsif option[:subdomain].present?
- opts[:subdomain] = case option[:subdomain]
- when Array
- "(#{option[:subdomain].join("|")})\\."
- when Regexp
- option[:subdomain]
- else
- "(#{option[:subdomain]})\\."
- end
+ opts[:subdomain] = evaluate_subdomain_context(option[:subdomain])
end
end
@@ -60,6 +53,43 @@ module QA
alias_method :dot_com?, :context_matches?
+ private
+
+ def evaluate_pipeline_context(pipeline)
+ return true if Runtime::Env.ci_project_name.blank?
+
+ pipeline_matches?(pipeline)
+ end
+
+ def evaluate_job_context(job)
+ return true if Runtime::Env.ci_job_name.blank?
+
+ job_matches?(job)
+ end
+
+ def evaluate_generic_condition(condition)
+ return condition.call if condition.respond_to?(:call)
+
+ condition
+ end
+
+ def evaluate_subdomain_context(option)
+ case option
+ when Array
+ "(#{option.join('|')})\\."
+ when Regexp
+ option
+ else
+ "(#{option})\\."
+ end
+ end
+
+ def pipeline_matches?(pipeline_to_run_in)
+ Array(pipeline_to_run_in).any? do |pipeline|
+ pipeline.to_s.casecmp?(pipeline_from_project_name(Runtime::Env.ci_project_name))
+ end
+ end
+
def job_matches?(job_patterns)
Array(job_patterns).any? do |job|
pattern = job.is_a?(Regexp) ? job : Regexp.new(job)
@@ -68,10 +98,6 @@ module QA
end
end
- def pipeline_matches?(pipeline_to_run_in)
- Array(pipeline_to_run_in).any? { |pipeline| pipeline.to_s.casecmp?(pipeline_from_project_name(Runtime::Env.ci_project_name)) }
- end
-
def pipeline_from_project_name(project_name)
project_name.to_s.start_with?('gitlab-qa') ? Runtime::Env.default_branch : project_name
end
diff --git a/qa/spec/specs/helpers/context_selector_spec.rb b/qa/spec/specs/helpers/context_selector_spec.rb
index 9e46933542e..3c09f938684 100644
--- a/qa/spec/specs/helpers/context_selector_spec.rb
+++ b/qa/spec/specs/helpers/context_selector_spec.rb
@@ -132,6 +132,20 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
end
end
+ context 'with generic condition context matcher' do
+ it 'matches truthy lambda condition result' do
+ expect(described_class.context_matches?(condition: -> { true })).to be_truthy
+ end
+
+ it 'matches truthy condition result' do
+ expect(described_class.context_matches?(condition: true)).to be_truthy
+ end
+
+ it 'skips falsey condition result' do
+ expect(described_class.context_matches?(condition: false)).to be_falsey
+ end
+ end
+
it 'returns false for mismatching' do
QA::Runtime::Scenario.define(:gitlab_address, "https://staging.gitlab.com")
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
index 2e7195aa59b..0e383e551d0 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
@@ -165,6 +165,7 @@ describe('DependencyProxyApp', () => {
it('shows list', () => {
expect(findManifestList().props()).toMatchObject({
+ dependencyProxyImagePrefix: proxyData().dependencyProxyImagePrefix,
manifests: proxyManifests(),
pagination: pagination(),
});
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
index 0d8af42bae3..4149f728cd8 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_list_spec.js
@@ -3,6 +3,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ManifestRow from '~/packages_and_registries/dependency_proxy/components/manifest_row.vue';
import Component from '~/packages_and_registries/dependency_proxy/components/manifests_list.vue';
import {
+ proxyData,
proxyManifests,
pagination,
} from 'jest/packages_and_registries/dependency_proxy/mock_data';
@@ -11,6 +12,7 @@ describe('Manifests List', () => {
let wrapper;
const defaultProps = {
+ dependencyProxyImagePrefix: proxyData().dependencyProxyImagePrefix,
manifests: proxyManifests(),
pagination: pagination(),
loading: false,
@@ -42,9 +44,15 @@ describe('Manifests List', () => {
it('binds a manifest to each row', () => {
createComponent();
- expect(findRows().at(0).props()).toMatchObject({
- manifest: defaultProps.manifests[0],
- });
+ expect(findRows().at(0).props('manifest')).toBe(defaultProps.manifests[0]);
+ });
+
+ it('binds a dependencyProxyImagePrefix to each row', () => {
+ createComponent();
+
+ expect(findRows().at(0).props('dependencyProxyImagePrefix')).toBe(
+ proxyData().dependencyProxyImagePrefix,
+ );
});
describe('loading', () => {
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_row_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_row_spec.js
index ace5ce3a58d..5f47a1b8098 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_row_spec.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/components/manifest_row_spec.js
@@ -1,15 +1,17 @@
import { GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Component from '~/packages_and_registries/dependency_proxy/components/manifest_row.vue';
import { MANIFEST_PENDING_DESTRUCTION_STATUS } from '~/packages_and_registries/dependency_proxy/constants';
-import { proxyManifests } from 'jest/packages_and_registries/dependency_proxy/mock_data';
+import { proxyData, proxyManifests } from 'jest/packages_and_registries/dependency_proxy/mock_data';
describe('Manifest Row', () => {
let wrapper;
const defaultProps = {
+ dependencyProxyImagePrefix: proxyData().dependencyProxyImagePrefix,
manifest: proxyManifests()[0],
};
@@ -24,8 +26,10 @@ describe('Manifest Row', () => {
});
};
+ const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
const findListItem = () => wrapper.findComponent(ListItem);
const findCachedMessages = () => wrapper.findByTestId('cached-message');
+ const findDigest = () => wrapper.findByTestId('manifest-row-short-digest');
const findTimeAgoTooltip = () => wrapper.findComponent(TimeagoTooltip);
const findStatus = () => wrapper.findByTestId('status');
@@ -38,12 +42,18 @@ describe('Manifest Row', () => {
expect(findListItem().exists()).toBe(true);
});
- it('displays the name', () => {
- expect(wrapper.text()).toContain('alpine');
+ it('displays the name with tag & digest', () => {
+ expect(wrapper.text()).toContain('alpine:latest');
+ expect(findDigest().text()).toMatchInterpolatedText('Digest: 995efde');
});
- it('displays the version', () => {
- expect(wrapper.text()).toContain('latest');
+ it('displays the name & digest for manifests that contain digest in image name', () => {
+ createComponent({
+ ...defaultProps,
+ manifest: proxyManifests()[1],
+ });
+ expect(wrapper.text()).toContain('alpine');
+ expect(findDigest().text()).toMatchInterpolatedText('Digest: e95efde');
});
it('displays the cached time', () => {
@@ -82,4 +92,35 @@ describe('Manifest Row', () => {
expect(findStatus().text()).toBe('Scheduled for deletion');
});
});
+
+ describe('clipboard button', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('exists', () => {
+ expect(findClipboardButton().exists()).toBe(true);
+ });
+
+ it('passes the correct title prop', () => {
+ expect(findClipboardButton().attributes('title')).toBe(Component.i18n.copyImagePathTitle);
+ });
+
+ it('has the correct copy text when image name contains tag name', () => {
+ expect(findClipboardButton().attributes('text')).toBe(
+ 'gdk.test:3000/private-group/dependency_proxy/containers/alpine:latest',
+ );
+ });
+
+ it('has the correct copy text when image name contains digest', () => {
+ createComponent({
+ ...defaultProps,
+ manifest: proxyManifests()[1],
+ });
+
+ expect(findClipboardButton().attributes('text')).toBe(
+ 'gdk.test:3000/private-group/dependency_proxy/containers/alpine@sha256:e95efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089',
+ );
+ });
+ });
});
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/mock_data.js b/spec/frontend/packages_and_registries/dependency_proxy/mock_data.js
index 37c8eb669ba..4d0be0a0c09 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/mock_data.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/mock_data.js
@@ -11,13 +11,15 @@ export const proxyManifests = () => [
{
id: 'proxy-1',
createdAt: '2021-09-22T09:45:28Z',
+ digest: 'sha256:995efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089',
imageName: 'alpine:latest',
status: 'DEFAULT',
},
{
id: 'proxy-2',
createdAt: '2021-09-21T09:45:28Z',
- imageName: 'alpine:stable',
+ digest: 'sha256:e95efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089',
+ imageName: 'alpine:sha256:e95efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089',
status: 'DEFAULT',
},
];
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 7b5c3c82b7f..288eedffe1c 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -284,6 +284,9 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
it { is_expected.not_to allow_value(10.5).for(:ci_max_includes) }
it { is_expected.not_to allow_value(-1).for(:ci_max_includes) }
+ it { is_expected.to allow_value([true, false]).for(:remember_me_enabled) }
+ it { is_expected.not_to allow_value(nil).for(:remember_me_enabled) }
+
context 'when deactivate_dormant_users is enabled' do
before do
stub_application_setting(deactivate_dormant_users: true)