diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index b9a2eb3ebc6..2605b3ec8b6 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -144,18 +144,20 @@ review-stop: .allure-report-base: image: - name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.0.6 + name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.3.1 entrypoint: [""] stage: post-qa variables: GIT_STRATEGY: none STORAGE_CREDENTIALS: $QA_ALLURE_REPORT_GCS_CREDENTIALS + GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN script: - | allure-report-publisher upload gcs \ --results-glob="qa/gitlab-qa-run-*/**/allure-results/*" \ --bucket="gitlab-qa-allure-reports" \ --prefix="$ALLURE_REPORT_PATH_PREFIX/$CI_COMMIT_REF_SLUG" \ + --update-pr="comment" \ --copy-latest \ --color @@ -231,18 +233,20 @@ parallel-spec-reports: allure-report-qa-smoke: extends: - .allure-report-base - - .review:rules:review-qa-smoke + - .review:rules:review-qa-smoke-report needs: ["review-qa-smoke"] variables: ALLURE_REPORT_PATH_PREFIX: gitlab-review-smoke + ALLURE_JOB_NAME: review-qa-smoke allure-report-qa-all: extends: - .allure-report-base - - .review:rules:review-qa-all + - .review:rules:review-qa-all-report needs: ["review-qa-all"] variables: ALLURE_REPORT_PATH_PREFIX: gitlab-review-all + ALLURE_JOB_NAME: review-qa-all danger-review: extends: diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 4d083541b43..fbc010f90b5 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1130,6 +1130,22 @@ changes: *code-qa-patterns allow_failure: true +.review:rules:review-qa-smoke-report: + rules: + - <<: *if-not-ee + when: never + - <<: *if-dot-com-gitlab-org-merge-request + changes: *ci-review-patterns + when: always + - <<: *if-dot-com-gitlab-org-merge-request + changes: *frontend-patterns + allow_failure: true + when: always + - <<: *if-dot-com-gitlab-org-merge-request + changes: *code-qa-patterns + allow_failure: true + when: always + .review:rules:review-qa-all: rules: - <<: *if-not-ee @@ -1142,6 +1158,19 @@ changes: *qa-patterns allow_failure: true +.review:rules:review-qa-all-report: + rules: + - <<: *if-not-ee + when: never + - <<: *if-dot-com-gitlab-org-merge-request + changes: *code-patterns + when: manual + allow_failure: true + - <<: *if-dot-com-gitlab-org-merge-request + changes: *qa-patterns + allow_failure: true + when: always + .review:rules:review-cleanup: rules: - <<: *if-not-ee diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js index 3cdcac4c0b4..142dade914b 100644 --- a/app/assets/javascripts/security_configuration/components/constants.js +++ b/app/assets/javascripts/security_configuration/components/constants.js @@ -18,18 +18,27 @@ import { * Translations & helpPagePaths for Static Security Configuration Page */ export const SAST_NAME = __('Static Application Security Testing (SAST)'); +export const SAST_SHORT_NAME = s__('ciReport|SAST'); export const SAST_DESCRIPTION = __('Analyze your source code for known vulnerabilities.'); export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index'); +export const SAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/sast/index', { + anchor: 'configuration', +}); export const DAST_NAME = __('Dynamic Application Security Testing (DAST)'); +export const DAST_SHORT_NAME = s__('ciReport|DAST'); export const DAST_DESCRIPTION = __('Analyze a review version of your web application.'); export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index'); +export const DAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/dast/index', { + anchor: 'enable-dast', +}); export const DAST_PROFILES_NAME = __('DAST Scans'); export const DAST_PROFILES_DESCRIPTION = __( 'Saved scan settings and target site settings which are reusable.', ); export const DAST_PROFILES_HELP_PATH = helpPagePath('user/application_security/dast/index'); +export const DAST_PROFILES_CONFIG_TEXT = s__('SecurityConfiguration|Manage scans'); export const SECRET_DETECTION_NAME = __('Secret Detection'); export const SECRET_DETECTION_DESCRIPTION = __( @@ -38,6 +47,10 @@ export const SECRET_DETECTION_DESCRIPTION = __( export const SECRET_DETECTION_HELP_PATH = helpPagePath( 'user/application_security/secret_detection/index', ); +export const SECRET_DETECTION_CONFIG_HELP_PATH = helpPagePath( + 'user/application_security/secret_detection/index', + { anchor: 'configuration' }, +); export const DEPENDENCY_SCANNING_NAME = __('Dependency Scanning'); export const DEPENDENCY_SCANNING_DESCRIPTION = __( @@ -46,6 +59,10 @@ export const DEPENDENCY_SCANNING_DESCRIPTION = __( export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath( 'user/application_security/dependency_scanning/index', ); +export const DEPENDENCY_SCANNING_CONFIG_HELP_PATH = helpPagePath( + 'user/application_security/dependency_scanning/index', + { anchor: 'configuration' }, +); export const CONTAINER_SCANNING_NAME = __('Container Scanning'); export const CONTAINER_SCANNING_DESCRIPTION = __( @@ -54,6 +71,10 @@ export const CONTAINER_SCANNING_DESCRIPTION = __( export const CONTAINER_SCANNING_HELP_PATH = helpPagePath( 'user/application_security/container_scanning/index', ); +export const CONTAINER_SCANNING_CONFIG_HELP_PATH = helpPagePath( + 'user/application_security/container_scanning/index', + { anchor: 'configuration' }, +); export const COVERAGE_FUZZING_NAME = __('Coverage Fuzzing'); export const COVERAGE_FUZZING_DESCRIPTION = __( @@ -136,6 +157,83 @@ export const scanners = [ }, ]; +export const securityFeatures = [ + { + name: SAST_NAME, + shortName: SAST_SHORT_NAME, + description: SAST_DESCRIPTION, + helpPath: SAST_HELP_PATH, + configurationHelpPath: SAST_CONFIG_HELP_PATH, + type: REPORT_TYPE_SAST, + // This field is currently hardcoded because SAST is always available. + // It will eventually come from the Backend, the progress is tracked in + // https://gitlab.com/gitlab-org/gitlab/-/issues/331622 + available: true, + + // This field is currently hardcoded because SAST can always be enabled via MR + // It will eventually come from the Backend, the progress is tracked in + // https://gitlab.com/gitlab-org/gitlab/-/issues/331621 + canEnableByMergeRequest: true, + }, + { + name: DAST_NAME, + shortName: DAST_SHORT_NAME, + description: DAST_DESCRIPTION, + helpPath: DAST_HELP_PATH, + configurationHelpPath: DAST_CONFIG_HELP_PATH, + type: REPORT_TYPE_DAST, + secondary: { + type: REPORT_TYPE_DAST_PROFILES, + name: DAST_PROFILES_NAME, + description: DAST_PROFILES_DESCRIPTION, + configurationText: DAST_PROFILES_CONFIG_TEXT, + }, + }, + { + name: DEPENDENCY_SCANNING_NAME, + description: DEPENDENCY_SCANNING_DESCRIPTION, + helpPath: DEPENDENCY_SCANNING_HELP_PATH, + configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH, + type: REPORT_TYPE_DEPENDENCY_SCANNING, + }, + { + name: CONTAINER_SCANNING_NAME, + description: CONTAINER_SCANNING_DESCRIPTION, + helpPath: CONTAINER_SCANNING_HELP_PATH, + configurationHelpPath: CONTAINER_SCANNING_CONFIG_HELP_PATH, + type: REPORT_TYPE_CONTAINER_SCANNING, + }, + { + name: SECRET_DETECTION_NAME, + description: SECRET_DETECTION_DESCRIPTION, + helpPath: SECRET_DETECTION_HELP_PATH, + configurationHelpPath: SECRET_DETECTION_CONFIG_HELP_PATH, + type: REPORT_TYPE_SECRET_DETECTION, + available: true, + }, + { + name: API_FUZZING_NAME, + description: API_FUZZING_DESCRIPTION, + helpPath: API_FUZZING_HELP_PATH, + type: REPORT_TYPE_API_FUZZING, + }, + { + name: COVERAGE_FUZZING_NAME, + description: COVERAGE_FUZZING_DESCRIPTION, + helpPath: COVERAGE_FUZZING_HELP_PATH, + type: REPORT_TYPE_COVERAGE_FUZZING, + }, +]; + +export const complianceFeatures = [ + { + name: LICENSE_COMPLIANCE_NAME, + description: LICENSE_COMPLIANCE_DESCRIPTION, + helpPath: LICENSE_COMPLIANCE_HELP_PATH, + type: REPORT_TYPE_LICENSE_COMPLIANCE, + }, +]; + export const featureToMutationMap = { [REPORT_TYPE_SAST]: { mutationId: 'configureSast', diff --git a/app/assets/javascripts/security_configuration/components/redesigned_app.vue b/app/assets/javascripts/security_configuration/components/redesigned_app.vue new file mode 100644 index 00000000000..9f3f7f2f703 --- /dev/null +++ b/app/assets/javascripts/security_configuration/components/redesigned_app.vue @@ -0,0 +1,76 @@ + + + diff --git a/app/assets/javascripts/security_configuration/index.js b/app/assets/javascripts/security_configuration/index.js index 1134a1ffb44..a74bc5163d0 100644 --- a/app/assets/javascripts/security_configuration/index.js +++ b/app/assets/javascripts/security_configuration/index.js @@ -2,6 +2,9 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; import SecurityConfigurationApp from './components/app.vue'; +import { securityFeatures, complianceFeatures } from './components/constants'; +import RedesignedSecurityConfigurationApp from './components/redesigned_app.vue'; +import { augmentFeatures } from './utils'; export const initStaticSecurityConfiguration = (el) => { if (!el) { @@ -14,8 +17,32 @@ export const initStaticSecurityConfiguration = (el) => { defaultClient: createDefaultClient(), }); - const { projectPath, upgradePath } = el.dataset; + const { projectPath, upgradePath, features, latestPipelinePath } = el.dataset; + if (gon.features.securityConfigurationRedesign) { + const { augmentedSecurityFeatures } = augmentFeatures( + securityFeatures, + complianceFeatures, + features ? JSON.parse(features) : [], + ); + + return new Vue({ + el, + apolloProvider, + provide: { + projectPath, + upgradePath, + }, + render(createElement) { + return createElement(RedesignedSecurityConfigurationApp, { + props: { + augmentedSecurityFeatures, + latestPipelinePath, + }, + }); + }, + }); + } return new Vue({ el, apolloProvider, diff --git a/app/assets/javascripts/security_configuration/utils.js b/app/assets/javascripts/security_configuration/utils.js new file mode 100644 index 00000000000..071ebff4f21 --- /dev/null +++ b/app/assets/javascripts/security_configuration/utils.js @@ -0,0 +1,24 @@ +export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => { + const featuresByType = features.reduce((acc, feature) => { + acc[feature.type] = feature; + return acc; + }, {}); + + const augmentFeature = (feature) => { + const augmented = { + ...feature, + ...featuresByType[feature.type], + }; + + if (augmented.secondary) { + augmented.secondary = { ...augmented.secondary, ...featuresByType[feature.secondary.type] }; + } + + return augmented; + }; + + return { + augmentedSecurityFeatures: securityFeatures.map((feature) => augmentFeature(feature)), + augmentedComplianceFeatures: complianceFeatures.map((feature) => augmentFeature(feature)), + }; +}; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 7b99286d2ee..95804e11b4a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -435,9 +435,7 @@ export default {