Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8ef0366928
commit
8c438dd7a6
|
@ -144,18 +144,20 @@ review-stop:
|
||||||
|
|
||||||
.allure-report-base:
|
.allure-report-base:
|
||||||
image:
|
image:
|
||||||
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.0.6
|
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.3.1
|
||||||
entrypoint: [""]
|
entrypoint: [""]
|
||||||
stage: post-qa
|
stage: post-qa
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none
|
GIT_STRATEGY: none
|
||||||
STORAGE_CREDENTIALS: $QA_ALLURE_REPORT_GCS_CREDENTIALS
|
STORAGE_CREDENTIALS: $QA_ALLURE_REPORT_GCS_CREDENTIALS
|
||||||
|
GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
allure-report-publisher upload gcs \
|
allure-report-publisher upload gcs \
|
||||||
--results-glob="qa/gitlab-qa-run-*/**/allure-results/*" \
|
--results-glob="qa/gitlab-qa-run-*/**/allure-results/*" \
|
||||||
--bucket="gitlab-qa-allure-reports" \
|
--bucket="gitlab-qa-allure-reports" \
|
||||||
--prefix="$ALLURE_REPORT_PATH_PREFIX/$CI_COMMIT_REF_SLUG" \
|
--prefix="$ALLURE_REPORT_PATH_PREFIX/$CI_COMMIT_REF_SLUG" \
|
||||||
|
--update-pr="comment" \
|
||||||
--copy-latest \
|
--copy-latest \
|
||||||
--color
|
--color
|
||||||
|
|
||||||
|
@ -231,18 +233,20 @@ parallel-spec-reports:
|
||||||
allure-report-qa-smoke:
|
allure-report-qa-smoke:
|
||||||
extends:
|
extends:
|
||||||
- .allure-report-base
|
- .allure-report-base
|
||||||
- .review:rules:review-qa-smoke
|
- .review:rules:review-qa-smoke-report
|
||||||
needs: ["review-qa-smoke"]
|
needs: ["review-qa-smoke"]
|
||||||
variables:
|
variables:
|
||||||
ALLURE_REPORT_PATH_PREFIX: gitlab-review-smoke
|
ALLURE_REPORT_PATH_PREFIX: gitlab-review-smoke
|
||||||
|
ALLURE_JOB_NAME: review-qa-smoke
|
||||||
|
|
||||||
allure-report-qa-all:
|
allure-report-qa-all:
|
||||||
extends:
|
extends:
|
||||||
- .allure-report-base
|
- .allure-report-base
|
||||||
- .review:rules:review-qa-all
|
- .review:rules:review-qa-all-report
|
||||||
needs: ["review-qa-all"]
|
needs: ["review-qa-all"]
|
||||||
variables:
|
variables:
|
||||||
ALLURE_REPORT_PATH_PREFIX: gitlab-review-all
|
ALLURE_REPORT_PATH_PREFIX: gitlab-review-all
|
||||||
|
ALLURE_JOB_NAME: review-qa-all
|
||||||
|
|
||||||
danger-review:
|
danger-review:
|
||||||
extends:
|
extends:
|
||||||
|
|
|
@ -1130,6 +1130,22 @@
|
||||||
changes: *code-qa-patterns
|
changes: *code-qa-patterns
|
||||||
allow_failure: true
|
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:
|
.review:rules:review-qa-all:
|
||||||
rules:
|
rules:
|
||||||
- <<: *if-not-ee
|
- <<: *if-not-ee
|
||||||
|
@ -1142,6 +1158,19 @@
|
||||||
changes: *qa-patterns
|
changes: *qa-patterns
|
||||||
allow_failure: true
|
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:
|
.review:rules:review-cleanup:
|
||||||
rules:
|
rules:
|
||||||
- <<: *if-not-ee
|
- <<: *if-not-ee
|
||||||
|
|
|
@ -18,18 +18,27 @@ import {
|
||||||
* Translations & helpPagePaths for Static Security Configuration Page
|
* Translations & helpPagePaths for Static Security Configuration Page
|
||||||
*/
|
*/
|
||||||
export const SAST_NAME = __('Static Application Security Testing (SAST)');
|
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_DESCRIPTION = __('Analyze your source code for known vulnerabilities.');
|
||||||
export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index');
|
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_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_DESCRIPTION = __('Analyze a review version of your web application.');
|
||||||
export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index');
|
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_NAME = __('DAST Scans');
|
||||||
export const DAST_PROFILES_DESCRIPTION = __(
|
export const DAST_PROFILES_DESCRIPTION = __(
|
||||||
'Saved scan settings and target site settings which are reusable.',
|
'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_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_NAME = __('Secret Detection');
|
||||||
export const SECRET_DETECTION_DESCRIPTION = __(
|
export const SECRET_DETECTION_DESCRIPTION = __(
|
||||||
|
@ -38,6 +47,10 @@ export const SECRET_DETECTION_DESCRIPTION = __(
|
||||||
export const SECRET_DETECTION_HELP_PATH = helpPagePath(
|
export const SECRET_DETECTION_HELP_PATH = helpPagePath(
|
||||||
'user/application_security/secret_detection/index',
|
'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_NAME = __('Dependency Scanning');
|
||||||
export const DEPENDENCY_SCANNING_DESCRIPTION = __(
|
export const DEPENDENCY_SCANNING_DESCRIPTION = __(
|
||||||
|
@ -46,6 +59,10 @@ export const DEPENDENCY_SCANNING_DESCRIPTION = __(
|
||||||
export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath(
|
export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath(
|
||||||
'user/application_security/dependency_scanning/index',
|
'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_NAME = __('Container Scanning');
|
||||||
export const CONTAINER_SCANNING_DESCRIPTION = __(
|
export const CONTAINER_SCANNING_DESCRIPTION = __(
|
||||||
|
@ -54,6 +71,10 @@ export const CONTAINER_SCANNING_DESCRIPTION = __(
|
||||||
export const CONTAINER_SCANNING_HELP_PATH = helpPagePath(
|
export const CONTAINER_SCANNING_HELP_PATH = helpPagePath(
|
||||||
'user/application_security/container_scanning/index',
|
'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_NAME = __('Coverage Fuzzing');
|
||||||
export const COVERAGE_FUZZING_DESCRIPTION = __(
|
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 = {
|
export const featureToMutationMap = {
|
||||||
[REPORT_TYPE_SAST]: {
|
[REPORT_TYPE_SAST]: {
|
||||||
mutationId: 'configureSast',
|
mutationId: 'configureSast',
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
<script>
|
||||||
|
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
|
||||||
|
import { __, s__ } from '~/locale';
|
||||||
|
import FeatureCard from './feature_card.vue';
|
||||||
|
|
||||||
|
export const i18n = {
|
||||||
|
compliance: s__('SecurityConfiguration|Compliance'),
|
||||||
|
securityTesting: s__('SecurityConfiguration|Security testing'),
|
||||||
|
securityTestingDescription: s__(
|
||||||
|
`SecurityConfiguration|The status of the tools only applies to the
|
||||||
|
default branch and is based on the %{linkStart}latest pipeline%{linkEnd}.
|
||||||
|
Once you've enabled a scan for the default branch, any subsequent feature
|
||||||
|
branch you create will include the scan.`,
|
||||||
|
),
|
||||||
|
securityConfiguration: __('Security Configuration'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
i18n,
|
||||||
|
components: {
|
||||||
|
GlTab,
|
||||||
|
GlLink,
|
||||||
|
GlTabs,
|
||||||
|
GlSprintf,
|
||||||
|
FeatureCard,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
augmentedSecurityFeatures: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
latestPipelinePath: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1 class="gl-font-size-h1">{{ $options.i18n.securityConfiguration }}</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<gl-tabs content-class="gl-pt-6">
|
||||||
|
<gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<h2 class="gl-font-size-h2 gl-mt-0">{{ $options.i18n.securityTesting }}</h2>
|
||||||
|
<p
|
||||||
|
v-if="latestPipelinePath"
|
||||||
|
data-testid="latest-pipeline-info"
|
||||||
|
class="gl-line-height-20"
|
||||||
|
>
|
||||||
|
<gl-sprintf :message="$options.i18n.securityTestingDescription">
|
||||||
|
<template #link="{ content }">
|
||||||
|
<gl-link :href="latestPipelinePath">{{ content }}</gl-link>
|
||||||
|
</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-7">
|
||||||
|
<feature-card
|
||||||
|
v-for="feature in augmentedSecurityFeatures"
|
||||||
|
:key="feature.type"
|
||||||
|
:feature="feature"
|
||||||
|
class="gl-mb-6"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</gl-tab>
|
||||||
|
</gl-tabs>
|
||||||
|
</article>
|
||||||
|
</template>
|
|
@ -2,6 +2,9 @@ import Vue from 'vue';
|
||||||
import VueApollo from 'vue-apollo';
|
import VueApollo from 'vue-apollo';
|
||||||
import createDefaultClient from '~/lib/graphql';
|
import createDefaultClient from '~/lib/graphql';
|
||||||
import SecurityConfigurationApp from './components/app.vue';
|
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) => {
|
export const initStaticSecurityConfiguration = (el) => {
|
||||||
if (!el) {
|
if (!el) {
|
||||||
|
@ -14,8 +17,32 @@ export const initStaticSecurityConfiguration = (el) => {
|
||||||
defaultClient: createDefaultClient(),
|
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({
|
return new Vue({
|
||||||
el,
|
el,
|
||||||
apolloProvider,
|
apolloProvider,
|
||||||
|
|
|
@ -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)),
|
||||||
|
};
|
||||||
|
};
|
|
@ -435,9 +435,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div v-if="isLoaded" class="mr-state-widget gl-mt-3">
|
<div v-if="isLoaded" class="mr-state-widget gl-mt-3">
|
||||||
<header
|
<header class="gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100">
|
||||||
class="gl-overflow-hidden gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100"
|
|
||||||
>
|
|
||||||
<mr-widget-alert-message v-if="shouldRenderCollaborationStatus" type="info">
|
<mr-widget-alert-message v-if="shouldRenderCollaborationStatus" type="info">
|
||||||
{{ s__('mrWidget|Members who can merge are allowed to add commits.') }}
|
{{ s__('mrWidget|Members who can merge are allowed to add commits.') }}
|
||||||
</mr-widget-alert-message>
|
</mr-widget-alert-message>
|
||||||
|
|
|
@ -7,6 +7,10 @@ module Projects
|
||||||
|
|
||||||
feature_category :static_application_security_testing
|
feature_category :static_application_security_testing
|
||||||
|
|
||||||
|
before_action only: [:show] do
|
||||||
|
push_frontend_feature_flag(:security_configuration_redesign, project, default_enabled: :yaml)
|
||||||
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render_403 unless can?(current_user, :read_security_configuration, project)
|
render_403 unless can?(current_user, :read_security_configuration, project)
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ module Security
|
||||||
attr_reader :pipeline
|
attr_reader :pipeline
|
||||||
|
|
||||||
def self.allowed_job_types
|
def self.allowed_job_types
|
||||||
# Example return: [:sast, :dast, :dependency_scanning, :container_scanning, :license_management, :coverage_fuzzing]
|
# Example return: [:sast, :dast, :dependency_scanning, :container_scanning, :license_scanning, :coverage_fuzzing]
|
||||||
raise NotImplementedError, 'allowed_job_types must be overwritten to return an array of job types'
|
raise NotImplementedError, 'allowed_job_types must be overwritten to return an array of job types'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
= link_to _('Forgot your password?'), new_password_path(:user)
|
= link_to _('Forgot your password?'), new_password_path(:user)
|
||||||
%div
|
%div
|
||||||
- if captcha_enabled? || captcha_on_login_required?
|
- if captcha_enabled? || captcha_on_login_required?
|
||||||
= recaptcha_tags
|
= recaptcha_tags nonce: content_security_policy_nonce
|
||||||
|
|
||||||
.submit-container.move-submit-down
|
.submit-container.move-submit-down
|
||||||
= f.submit _('Sign in'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'sign_in_button' }
|
= f.submit _('Sign in'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'sign_in_button' }
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
.devise-errors
|
.devise-errors
|
||||||
= render 'devise/shared/error_messages', resource: resource
|
= render 'devise/shared/error_messages', resource: resource
|
||||||
- if Gitlab::CurrentSettings.invisible_captcha_enabled
|
- if Gitlab::CurrentSettings.invisible_captcha_enabled
|
||||||
= invisible_captcha
|
= invisible_captcha nonce: true
|
||||||
.name.form-row
|
.name.form-row
|
||||||
.col.form-group
|
.col.form-group
|
||||||
= f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold'
|
= f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold'
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
%p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
|
%p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
|
||||||
%div
|
%div
|
||||||
- if show_recaptcha_sign_up?
|
- if show_recaptcha_sign_up?
|
||||||
= recaptcha_tags
|
= recaptcha_tags nonce: content_security_policy_nonce
|
||||||
.submit-container
|
.submit-container
|
||||||
= f.submit button_text, class: 'btn gl-button btn-confirm', data: { qa_selector: 'new_user_register_button' }
|
= f.submit button_text, class: 'btn gl-button btn-confirm', data: { qa_selector: 'new_user_register_button' }
|
||||||
= render 'devise/shared/terms_of_service_notice', button_text: button_text
|
= render 'devise/shared/terms_of_service_notice', button_text: button_text
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
- if captcha_required?
|
- if captcha_required?
|
||||||
.row.recaptcha
|
.row.recaptcha
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
= recaptcha_tags
|
= recaptcha_tags nonce: content_security_policy_nonce
|
||||||
.row
|
.row
|
||||||
.col-sm-12
|
.col-sm-12
|
||||||
= f.submit _('Create group'), class: "btn gl-button btn-confirm"
|
= f.submit _('Create group'), class: "btn gl-button btn-confirm"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
= hidden_field(resource_name, field, value: value)
|
= hidden_field(resource_name, field, value: value)
|
||||||
= hidden_field_tag(:spam_log_id, spammable.spam_log.id)
|
= hidden_field_tag(:spam_log_id, spammable.spam_log.id)
|
||||||
-# The reCAPTCHA response value will be returned in the 'g-recaptcha-response' field
|
-# The reCAPTCHA response value will be returned in the 'g-recaptcha-response' field
|
||||||
= recaptcha_tags script: script, callback: 'recaptchaDialogCallback' unless Rails.env.test?
|
= recaptcha_tags script: script, callback: 'recaptchaDialogCallback', nonce: content_security_policy_nonce unless Rails.env.test?
|
||||||
-# Fake the 'g-recaptcha-response' field in the test environment, so that the feature spec
|
-# Fake the 'g-recaptcha-response' field in the test environment, so that the feature spec
|
||||||
-# can get to the (mocked) SpamVerdictService check.
|
-# can get to the (mocked) SpamVerdictService check.
|
||||||
= hidden_field_tag('g-recaptcha-response', 'abc123') if Rails.env.test?
|
= hidden_field_tag('g-recaptcha-response', 'abc123') if Rails.env.test?
|
||||||
|
|
|
@ -40,7 +40,9 @@ module AuthorizedProjectUpdate
|
||||||
private
|
private
|
||||||
|
|
||||||
def use_primary_database
|
def use_primary_database
|
||||||
# no-op in CE, overriden in EE
|
if ::Gitlab::Database::LoadBalancing.enable?
|
||||||
|
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_authorizations_needs_refresh?(user)
|
def project_authorizations_needs_refresh?(user)
|
||||||
|
@ -54,5 +56,3 @@ module AuthorizedProjectUpdate
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker.prepend_mod_with('AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker')
|
|
||||||
|
|
|
@ -15,10 +15,8 @@
|
||||||
- audit_reports
|
- audit_reports
|
||||||
- authentication_and_authorization
|
- authentication_and_authorization
|
||||||
- auto_devops
|
- auto_devops
|
||||||
- auto_portfolio_mgmt
|
|
||||||
- backup_restore
|
- backup_restore
|
||||||
- boards
|
- boards
|
||||||
- browser_performance
|
|
||||||
- chatops
|
- chatops
|
||||||
- cloud_native_installation
|
- cloud_native_installation
|
||||||
- cluster_cost_management
|
- cluster_cost_management
|
||||||
|
@ -78,7 +76,6 @@
|
||||||
- license
|
- license
|
||||||
- license_compliance
|
- license_compliance
|
||||||
- live_preview
|
- live_preview
|
||||||
- load_testing
|
|
||||||
- logging
|
- logging
|
||||||
- memory
|
- memory
|
||||||
- merge_trains
|
- merge_trains
|
||||||
|
@ -87,9 +84,11 @@
|
||||||
- mobile_signing_deployment
|
- mobile_signing_deployment
|
||||||
- navigation
|
- navigation
|
||||||
- omnibus_package
|
- omnibus_package
|
||||||
|
- on_call_schedule_management
|
||||||
- onboarding
|
- onboarding
|
||||||
- package_registry
|
- package_registry
|
||||||
- pages
|
- pages
|
||||||
|
- performance_testing
|
||||||
- pipeline_authoring
|
- pipeline_authoring
|
||||||
- planning_analytics
|
- planning_analytics
|
||||||
- privacy_control_center
|
- privacy_control_center
|
||||||
|
@ -111,6 +110,7 @@
|
||||||
- self_monitoring
|
- self_monitoring
|
||||||
- serverless
|
- serverless
|
||||||
- service_desk
|
- service_desk
|
||||||
|
- sharding
|
||||||
- snippets
|
- snippets
|
||||||
- source_code_management
|
- source_code_management
|
||||||
- static_application_security_testing
|
- static_application_security_testing
|
||||||
|
@ -130,3 +130,4 @@
|
||||||
- web_firewall
|
- web_firewall
|
||||||
- web_ide
|
- web_ide
|
||||||
- wiki
|
- wiki
|
||||||
|
- workflow_automation
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: security_configuration_redesign
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62285
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331614
|
||||||
|
milestone: '14.0'
|
||||||
|
type: development
|
||||||
|
group: group::static analysis
|
||||||
|
default_enabled: false
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ScheduleUpdateJiraTrackerDataDeploymentTypeBasedOnUrl < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
MIGRATION = 'UpdateJiraTrackerDataDeploymentTypeBasedOnUrl'
|
||||||
|
DELAY_INTERVAL = 2.minutes.to_i
|
||||||
|
BATCH_SIZE = 2_500
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
say "Scheduling #{MIGRATION} jobs"
|
||||||
|
queue_background_migration_jobs_by_range_at_intervals(
|
||||||
|
define_batchable_model('jira_tracker_data'),
|
||||||
|
MIGRATION,
|
||||||
|
DELAY_INTERVAL,
|
||||||
|
batch_size: BATCH_SIZE
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# no-op
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveSegmentSelectionsTable < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
drop_table :analytics_devops_adoption_segment_selections
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
create_table :analytics_devops_adoption_segment_selections do |t|
|
||||||
|
t.references :segment, index: { name: 'index_on_segment_selections_segment_id' }, null: false, foreign_key: { to_table: :analytics_devops_adoption_segments, on_delete: :cascade }
|
||||||
|
t.bigint :group_id
|
||||||
|
t.bigint :project_id
|
||||||
|
t.index [:group_id, :segment_id], unique: true, name: 'index_on_segment_selections_group_id_segment_id'
|
||||||
|
t.index [:project_id, :segment_id], unique: true, name: 'index_on_segment_selections_project_id_segment_id'
|
||||||
|
|
||||||
|
t.timestamps_with_timezone
|
||||||
|
end
|
||||||
|
add_concurrent_foreign_key(:analytics_devops_adoption_segment_selections, :projects, column: :project_id, on_delete: :cascade)
|
||||||
|
add_concurrent_foreign_key(:analytics_devops_adoption_segment_selections, :namespaces, column: :group_id, on_delete: :cascade)
|
||||||
|
add_check_constraint :analytics_devops_adoption_segment_selections, '(project_id != NULL AND group_id IS NULL) OR (group_id != NULL AND project_id IS NULL)', 'segment_selection_project_id_or_group_id_required'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
0f6019cc094481cafbf0c9bd42f53ae09034ea87e3f360b02f9ec03192caab9d
|
|
@ -0,0 +1 @@
|
||||||
|
ee76ee2e2515c06b09fca23a77bdfb9532fa5d80fc3d5aba44a80d123b74cfa9
|
|
@ -9110,25 +9110,6 @@ CREATE SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq
|
||||||
|
|
||||||
ALTER SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq OWNED BY analytics_cycle_analytics_project_value_streams.id;
|
ALTER SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq OWNED BY analytics_cycle_analytics_project_value_streams.id;
|
||||||
|
|
||||||
CREATE TABLE analytics_devops_adoption_segment_selections (
|
|
||||||
id bigint NOT NULL,
|
|
||||||
segment_id bigint NOT NULL,
|
|
||||||
group_id bigint,
|
|
||||||
project_id bigint,
|
|
||||||
created_at timestamp with time zone NOT NULL,
|
|
||||||
updated_at timestamp with time zone NOT NULL,
|
|
||||||
CONSTRAINT segment_selection_project_id_or_group_id_required CHECK ((((project_id <> NULL::bigint) AND (group_id IS NULL)) OR ((group_id <> NULL::bigint) AND (project_id IS NULL))))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE SEQUENCE analytics_devops_adoption_segment_selections_id_seq
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
ALTER SEQUENCE analytics_devops_adoption_segment_selections_id_seq OWNED BY analytics_devops_adoption_segment_selections.id;
|
|
||||||
|
|
||||||
CREATE TABLE analytics_devops_adoption_segments (
|
CREATE TABLE analytics_devops_adoption_segments (
|
||||||
id bigint NOT NULL,
|
id bigint NOT NULL,
|
||||||
last_recorded_at timestamp with time zone,
|
last_recorded_at timestamp with time zone,
|
||||||
|
@ -19520,8 +19501,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_project_stages ALTER COLUMN id SET DE
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_value_streams_id_seq'::regclass);
|
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_value_streams_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segment_selections_id_seq'::regclass);
|
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_segments ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segments_id_seq'::regclass);
|
ALTER TABLE ONLY analytics_devops_adoption_segments ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segments_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_snapshots ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_snapshots_id_seq'::regclass);
|
ALTER TABLE ONLY analytics_devops_adoption_snapshots ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_snapshots_id_seq'::regclass);
|
||||||
|
@ -20611,9 +20590,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_project_stages
|
||||||
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams
|
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams
|
||||||
ADD CONSTRAINT analytics_cycle_analytics_project_value_streams_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT analytics_cycle_analytics_project_value_streams_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
|
||||||
ADD CONSTRAINT analytics_devops_adoption_segment_selections_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_segments
|
ALTER TABLE ONLY analytics_devops_adoption_segments
|
||||||
ADD CONSTRAINT analytics_devops_adoption_segments_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT analytics_devops_adoption_segments_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
@ -23899,12 +23875,6 @@ CREATE INDEX index_on_projects_lower_path ON projects USING btree (lower((path):
|
||||||
|
|
||||||
CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text));
|
CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text));
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_on_segment_selections_group_id_segment_id ON analytics_devops_adoption_segment_selections USING btree (group_id, segment_id);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_on_segment_selections_project_id_segment_id ON analytics_devops_adoption_segment_selections USING btree (project_id, segment_id);
|
|
||||||
|
|
||||||
CREATE INDEX index_on_segment_selections_segment_id ON analytics_devops_adoption_segment_selections USING btree (segment_id);
|
|
||||||
|
|
||||||
CREATE INDEX index_on_snapshots_segment_id_end_time ON analytics_devops_adoption_snapshots USING btree (segment_id, end_time);
|
CREATE INDEX index_on_snapshots_segment_id_end_time ON analytics_devops_adoption_snapshots USING btree (segment_id, end_time);
|
||||||
|
|
||||||
CREATE INDEX index_on_snapshots_segment_id_recorded_at ON analytics_devops_adoption_snapshots USING btree (segment_id, recorded_at);
|
CREATE INDEX index_on_snapshots_segment_id_recorded_at ON analytics_devops_adoption_snapshots USING btree (segment_id, recorded_at);
|
||||||
|
@ -25955,9 +25925,6 @@ ALTER TABLE ONLY project_group_links
|
||||||
ALTER TABLE ONLY epics
|
ALTER TABLE ONLY epics
|
||||||
ADD CONSTRAINT fk_dccd3f98fc FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL;
|
ADD CONSTRAINT fk_dccd3f98fc FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
|
||||||
ADD CONSTRAINT fk_ded7fe0344 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY issues
|
ALTER TABLE ONLY issues
|
||||||
ADD CONSTRAINT fk_df75a7c8b8 FOREIGN KEY (promoted_to_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
|
ADD CONSTRAINT fk_df75a7c8b8 FOREIGN KEY (promoted_to_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
@ -26027,9 +25994,6 @@ ALTER TABLE ONLY vulnerability_external_issue_links
|
||||||
ALTER TABLE ONLY epics
|
ALTER TABLE ONLY epics
|
||||||
ADD CONSTRAINT fk_f081aa4489 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_f081aa4489 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
|
||||||
ADD CONSTRAINT fk_f1472b95f3 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY boards
|
ALTER TABLE ONLY boards
|
||||||
ADD CONSTRAINT fk_f15266b5f9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_f15266b5f9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
@ -26120,9 +26084,6 @@ ALTER TABLE ONLY ip_restrictions
|
||||||
ALTER TABLE ONLY terraform_state_versions
|
ALTER TABLE ONLY terraform_state_versions
|
||||||
ADD CONSTRAINT fk_rails_04f176e239 FOREIGN KEY (terraform_state_id) REFERENCES terraform_states(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_04f176e239 FOREIGN KEY (terraform_state_id) REFERENCES terraform_states(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
|
||||||
ADD CONSTRAINT fk_rails_053f00a9da FOREIGN KEY (segment_id) REFERENCES analytics_devops_adoption_segments(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY ci_build_report_results
|
ALTER TABLE ONLY ci_build_report_results
|
||||||
ADD CONSTRAINT fk_rails_056d298d48 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_056d298d48 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,8 @@ GitLab has several features based on receiving incoming emails:
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
It is **not** recommended to use an email address that receives any
|
We recommend using an email address that receives **only** messages that are intended for
|
||||||
messages not intended for the GitLab instance. Any incoming emails not intended
|
the GitLab instance. Any incoming emails not intended for GitLab receive a reject notice.
|
||||||
for GitLab receive a reject notice.
|
|
||||||
|
|
||||||
Handling incoming emails requires an [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol)-enabled
|
Handling incoming emails requires an [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol)-enabled
|
||||||
email account. GitLab requires one of the following three strategies:
|
email account. GitLab requires one of the following three strategies:
|
||||||
|
@ -131,6 +130,9 @@ list.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo gitlab-ctl reconfigure
|
sudo gitlab-ctl reconfigure
|
||||||
|
|
||||||
|
# Needed when enabling or disabling for the first time but not for password changes.
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab-foss/-/issues/23560#note_61966788
|
||||||
sudo gitlab-ctl restart
|
sudo gitlab-ctl restart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ The following are the requirements for providing your own Redis instance:
|
||||||
[requirements page](../../install/requirements.md).
|
[requirements page](../../install/requirements.md).
|
||||||
- Standalone Redis or Redis high availability with Sentinel are supported. Redis
|
- Standalone Redis or Redis high availability with Sentinel are supported. Redis
|
||||||
Cluster is not supported.
|
Cluster is not supported.
|
||||||
- Managed Redis from cloud providers such as AWS ElastiCache will work. If these
|
- Managed Redis from cloud providers such as AWS ElastiCache works fine. If these
|
||||||
services support high availability, be sure it is **not** the Redis Cluster type.
|
services support high availability, be sure it is **not** the Redis Cluster type.
|
||||||
|
|
||||||
Note the Redis node's IP address or hostname, port, and password (if required).
|
Note the Redis node's IP address or hostname, port, and password (if required).
|
||||||
|
@ -53,7 +53,7 @@ Note the Redis node's IP address or hostname, port, and password (if required).
|
||||||
This is the documentation for configuring a scalable Redis setup when
|
This is the documentation for configuring a scalable Redis setup when
|
||||||
you have installed Redis all by yourself and not using the bundled one that
|
you have installed Redis all by yourself and not using the bundled one that
|
||||||
comes with the Omnibus packages, although using the Omnibus GitLab packages is
|
comes with the Omnibus packages, although using the Omnibus GitLab packages is
|
||||||
highly recommend as we optimize them specifically for GitLab, and we will take
|
highly recommend as we optimize them specifically for GitLab, and we take
|
||||||
care of upgrading Redis to the latest supported version.
|
care of upgrading Redis to the latest supported version.
|
||||||
|
|
||||||
Note also that you may elect to override all references to
|
Note also that you may elect to override all references to
|
||||||
|
@ -76,7 +76,7 @@ requirements:
|
||||||
(e.g., one from an internal network).
|
(e.g., one from an internal network).
|
||||||
- Since Redis 3.2, you must define a password to receive external connections
|
- Since Redis 3.2, you must define a password to receive external connections
|
||||||
(`requirepass`).
|
(`requirepass`).
|
||||||
- If you are using Redis with Sentinel, you will also need to define the same
|
- If you are using Redis with Sentinel, you also need to define the same
|
||||||
password for the replica password definition (`masterauth`) in the same instance.
|
password for the replica password definition (`masterauth`) in the same instance.
|
||||||
|
|
||||||
In addition, read the prerequisites as described in the
|
In addition, read the prerequisites as described in the
|
||||||
|
@ -176,7 +176,7 @@ primary with IP `10.0.0.1` (some settings might overlap with the primary):
|
||||||
sentinel monitor gitlab-redis 10.0.0.1 6379 2
|
sentinel monitor gitlab-redis 10.0.0.1 6379 2
|
||||||
|
|
||||||
## Define with `sentinel down-after-milliseconds` the time in `ms`
|
## Define with `sentinel down-after-milliseconds` the time in `ms`
|
||||||
## that an unresponsive server will be considered down.
|
## that an unresponsive server is considered down.
|
||||||
sentinel down-after-milliseconds gitlab-redis 10000
|
sentinel down-after-milliseconds gitlab-redis 10000
|
||||||
|
|
||||||
## Define a value for `sentinel failover_timeout` in `ms`. This has multiple
|
## Define a value for `sentinel failover_timeout` in `ms`. This has multiple
|
||||||
|
@ -197,7 +197,7 @@ primary with IP `10.0.0.1` (some settings might overlap with the primary):
|
||||||
##
|
##
|
||||||
## * The maximum time a failover in progress waits for all the replicas to be
|
## * The maximum time a failover in progress waits for all the replicas to be
|
||||||
## reconfigured as replicas of the new primary. However even after this time
|
## reconfigured as replicas of the new primary. However even after this time
|
||||||
## the replicas will be reconfigured by the Sentinels anyway, but not with
|
## the replicas are reconfigured by the Sentinels anyway, but not with
|
||||||
## the exact parallel-syncs progression as specified.
|
## the exact parallel-syncs progression as specified.
|
||||||
sentinel failover_timeout 30000
|
sentinel failover_timeout 30000
|
||||||
```
|
```
|
||||||
|
@ -249,7 +249,7 @@ In a real world usage, you would also set up firewall rules to prevent
|
||||||
unauthorized access from other machines, and block traffic from the
|
unauthorized access from other machines, and block traffic from the
|
||||||
outside ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)).
|
outside ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)).
|
||||||
|
|
||||||
For this example, **Sentinel 1** will be configured in the same machine as the
|
For this example, **Sentinel 1** is configured in the same machine as the
|
||||||
**Redis Primary**, **Sentinel 2** and **Sentinel 3** in the same machines as the
|
**Redis Primary**, **Sentinel 2** and **Sentinel 3** in the same machines as the
|
||||||
**Replica 1** and **Replica 2** respectively.
|
**Replica 1** and **Replica 2** respectively.
|
||||||
|
|
||||||
|
@ -261,11 +261,11 @@ Here is a list and description of each **machine** and the assigned **IP**:
|
||||||
- `10.0.0.4`: GitLab application
|
- `10.0.0.4`: GitLab application
|
||||||
|
|
||||||
Please note that after the initial configuration, if a failover is initiated
|
Please note that after the initial configuration, if a failover is initiated
|
||||||
by the Sentinel nodes, the Redis nodes will be reconfigured and the **Primary**
|
by the Sentinel nodes, the Redis nodes are reconfigured and the **Primary**
|
||||||
will change permanently (including in `redis.conf`) from one node to the other,
|
changes permanently (including in `redis.conf`) from one node to the other,
|
||||||
until a new failover is initiated again.
|
until a new failover is initiated again.
|
||||||
|
|
||||||
The same thing will happen with `sentinel.conf` that will be overridden after the
|
The same thing happens with `sentinel.conf` that is overridden after the
|
||||||
initial execution, after any new sentinel node starts watching the **Primary**,
|
initial execution, after any new sentinel node starts watching the **Primary**,
|
||||||
or a failover promotes a different **Primary** node.
|
or a failover promotes a different **Primary** node.
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
||||||
|
|
||||||
# Create an A/B test with `Experimentation Module`
|
# Create an A/B test with `Experimentation Module`
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
We recommend using [GLEX](gitlab_experiment.md) for new experiments.
|
||||||
|
|
||||||
## Implement the experiment
|
## Implement the experiment
|
||||||
|
|
||||||
1. Add the experiment to the `Gitlab::Experimentation::EXPERIMENTS` hash in
|
1. Add the experiment to the `Gitlab::Experimentation::EXPERIMENTS` hash in
|
||||||
|
|
|
@ -46,16 +46,10 @@ One is built into GitLab directly and has been around for a while (this is calle
|
||||||
to as `Gitlab::Experiment` -- GLEX for short.
|
to as `Gitlab::Experiment` -- GLEX for short.
|
||||||
|
|
||||||
Both approaches use [experiment](../feature_flags/index.md#experiment-type)
|
Both approaches use [experiment](../feature_flags/index.md#experiment-type)
|
||||||
feature flags, and there is currently no strong suggestion to use one over the other.
|
feature flags. We recommend using GLEX rather than `Experimentation Module` for new experiments.
|
||||||
|
|
||||||
| Feature | `Experimentation Module` | GLEX |
|
|
||||||
| -------------------- |------------------------- | ---- |
|
|
||||||
| Record user grouping | Yes | No |
|
|
||||||
| Uses feature flags | Yes | Yes |
|
|
||||||
| Multivariate (A/B/n) | No | Yes |
|
|
||||||
|
|
||||||
- [Implementing an A/B experiment using `Experimentation Module`](experimentation.md)
|
|
||||||
- [Implementing an A/B/n experiment using GLEX](gitlab_experiment.md)
|
- [Implementing an A/B/n experiment using GLEX](gitlab_experiment.md)
|
||||||
|
- [Implementing an A/B experiment using `Experimentation Module`](experimentation.md)
|
||||||
|
|
||||||
Historical Context: `Experimentation Module` was built iteratively with the needs that
|
Historical Context: `Experimentation Module` was built iteratively with the needs that
|
||||||
appeared while implementing Growth sub-department experiments, while GLEX was built
|
appeared while implementing Growth sub-department experiments, while GLEX was built
|
||||||
|
|
|
@ -105,6 +105,12 @@ Default client accepts two parameters: `resolvers` and `config`.
|
||||||
- `baseUrl` allows us to pass a URL for GraphQL endpoint different from our main endpoint (for example, `${gon.relative_url_root}/api/graphql`)
|
- `baseUrl` allows us to pass a URL for GraphQL endpoint different from our main endpoint (for example, `${gon.relative_url_root}/api/graphql`)
|
||||||
- `assumeImmutableResults` (set to `false` by default) - this setting, when set to `true`, assumes that every single operation on updating Apollo Cache is immutable. It also sets `freezeResults` to `true`, so any attempt on mutating Apollo Cache throws a console warning in development environment. Please ensure you're following the immutability pattern on cache update operations before setting this option to `true`.
|
- `assumeImmutableResults` (set to `false` by default) - this setting, when set to `true`, assumes that every single operation on updating Apollo Cache is immutable. It also sets `freezeResults` to `true`, so any attempt on mutating Apollo Cache throws a console warning in development environment. Please ensure you're following the immutability pattern on cache update operations before setting this option to `true`.
|
||||||
- `fetchPolicy` determines how you want your component to interact with the Apollo cache. Defaults to "cache-first".
|
- `fetchPolicy` determines how you want your component to interact with the Apollo cache. Defaults to "cache-first".
|
||||||
|
|
||||||
|
### Multiple client queries for the same object
|
||||||
|
|
||||||
|
If you are make multiple queries to the same Apollo client object you might encounter the following error: "Store error: the application attempted to write an object with no provided ID but the store already contains an ID of SomeEntity". [This error only should occur when you have made a query with an ID field for a portion, then made another that returns what would be the same object, but is missing the ID field.](https://github.com/apollographql/apollo-client/issues/2510#issue-271829009)
|
||||||
|
|
||||||
|
Please note this is being tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/326101) and the documentation will be updated when this issue is resolved.
|
||||||
|
|
||||||
## GraphQL Queries
|
## GraphQL Queries
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,9 @@ the QA smoke suite.
|
||||||
|
|
||||||
You can also manually start the `review-qa-all`: it runs the full QA suite.
|
You can also manually start the `review-qa-all`: it runs the full QA suite.
|
||||||
|
|
||||||
|
After the end-to-end test runs have finished, [Allure reports](https://github.com/allure-framework/allure2) are generated and published by
|
||||||
|
the `allure-report-qa-smoke` and `allure-report-qa-all` jobs. A comment with links to the reports are added to the merge request.
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
On every [pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/125315730) in the `qa` stage, the
|
On every [pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/125315730) in the `qa` stage, the
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# rubocop: disable Style/Documentation
|
||||||
|
class Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl
|
||||||
|
# rubocop: disable Gitlab/NamespacedClass
|
||||||
|
class JiraTrackerData < ActiveRecord::Base
|
||||||
|
self.table_name = "jira_tracker_data"
|
||||||
|
self.inheritance_column = :_type_disabled
|
||||||
|
|
||||||
|
include ::Integrations::BaseDataFields
|
||||||
|
attr_encrypted :url, encryption_options
|
||||||
|
attr_encrypted :api_url, encryption_options
|
||||||
|
|
||||||
|
enum deployment_type: { unknown: 0, server: 1, cloud: 2 }, _prefix: :deployment
|
||||||
|
end
|
||||||
|
# rubocop: enable Gitlab/NamespacedClass
|
||||||
|
|
||||||
|
# https://rubular.com/r/uwgK7k9KH23efa
|
||||||
|
JIRA_CLOUD_REGEX = %r{^https?://[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.atlassian\.net$}ix.freeze
|
||||||
|
|
||||||
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
def perform(start_id, end_id)
|
||||||
|
trackers_data = JiraTrackerData
|
||||||
|
.where(deployment_type: 'unknown')
|
||||||
|
.where(id: start_id..end_id)
|
||||||
|
|
||||||
|
cloud, server = trackers_data.partition { |tracker_data| tracker_data.url.match?(JIRA_CLOUD_REGEX) }
|
||||||
|
|
||||||
|
cloud_mappings = cloud.each_with_object({}) do |tracker_data, hash|
|
||||||
|
hash[tracker_data] = { deployment_type: 2 }
|
||||||
|
end
|
||||||
|
|
||||||
|
server_mapppings = server.each_with_object({}) do |tracker_data, hash|
|
||||||
|
hash[tracker_data] = { deployment_type: 1 }
|
||||||
|
end
|
||||||
|
|
||||||
|
mappings = cloud_mappings.merge(server_mapppings)
|
||||||
|
|
||||||
|
::Gitlab::Database::BulkUpdate.execute(%i[deployment_type], mappings)
|
||||||
|
end
|
||||||
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
end
|
|
@ -77,7 +77,7 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_section_duration(duration)
|
def set_section_duration(duration)
|
||||||
@section_duration = Time.at(duration.to_i).strftime('%M:%S')
|
@section_duration = Time.at(duration.to_i).utc.strftime('%M:%S')
|
||||||
end
|
end
|
||||||
|
|
||||||
def flush_current_segment!
|
def flush_current_segment!
|
||||||
|
|
|
@ -18,11 +18,11 @@ module Gitlab
|
||||||
'font_src' => "'self'",
|
'font_src' => "'self'",
|
||||||
'form_action' => "'self' https: http:",
|
'form_action' => "'self' https: http:",
|
||||||
'frame_ancestors' => "'self'",
|
'frame_ancestors' => "'self'",
|
||||||
'frame_src' => "'self' https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
|
'frame_src' => "'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
|
||||||
'img_src' => "'self' data: blob: http: https:",
|
'img_src' => "'self' data: blob: http: https:",
|
||||||
'manifest_src' => "'self'",
|
'manifest_src' => "'self'",
|
||||||
'media_src' => "'self'",
|
'media_src' => "'self'",
|
||||||
'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com",
|
'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com",
|
||||||
'style_src' => "'self' 'unsafe-inline'",
|
'style_src' => "'self' 'unsafe-inline'",
|
||||||
'worker_src' => "'self'",
|
'worker_src' => "'self'",
|
||||||
'object_src' => "'none'",
|
'object_src' => "'none'",
|
||||||
|
|
|
@ -28955,6 +28955,9 @@ msgstr ""
|
||||||
msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the Merge Request."
|
msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the Merge Request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityConfiguration|Compliance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityConfiguration|Configuration guide"
|
msgid "SecurityConfiguration|Configuration guide"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -28994,6 +28997,9 @@ msgstr ""
|
||||||
msgid "SecurityConfiguration|Manage"
|
msgid "SecurityConfiguration|Manage"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityConfiguration|Manage scans"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityConfiguration|More information"
|
msgid "SecurityConfiguration|More information"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -29009,12 +29015,18 @@ msgstr ""
|
||||||
msgid "SecurityConfiguration|Security Control"
|
msgid "SecurityConfiguration|Security Control"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityConfiguration|Security testing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityConfiguration|Status"
|
msgid "SecurityConfiguration|Status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityConfiguration|Testing & Compliance"
|
msgid "SecurityConfiguration|Testing & Compliance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityConfiguration|The status of the tools only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityConfiguration|Using custom settings. You won't receive automatic updates on this variable. %{anchorStart}Restore to default%{anchorEnd}"
|
msgid "SecurityConfiguration|Using custom settings. You won't receive automatic updates on this variable. %{anchorStart}Restore to default%{anchorEnd}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -28,19 +28,19 @@ RSpec.describe Security::SecurityJobsFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with combination of security jobs and license management jobs' do
|
context 'with combination of security jobs and license scanning jobs' do
|
||||||
let!(:sast_build) { create(:ci_build, :sast, pipeline: pipeline) }
|
let!(:sast_build) { create(:ci_build, :sast, pipeline: pipeline) }
|
||||||
let!(:container_scanning_build) { create(:ci_build, :container_scanning, pipeline: pipeline) }
|
let!(:container_scanning_build) { create(:ci_build, :container_scanning, pipeline: pipeline) }
|
||||||
let!(:dast_build) { create(:ci_build, :dast, pipeline: pipeline) }
|
let!(:dast_build) { create(:ci_build, :dast, pipeline: pipeline) }
|
||||||
let!(:secret_detection_build) { create(:ci_build, :secret_detection, pipeline: pipeline) }
|
let!(:secret_detection_build) { create(:ci_build, :secret_detection, pipeline: pipeline) }
|
||||||
let!(:license_management_build) { create(:ci_build, :license_management, pipeline: pipeline) }
|
let!(:license_scanning_build) { create(:ci_build, :license_scanning, pipeline: pipeline) }
|
||||||
|
|
||||||
it 'returns only the security jobs' do
|
it 'returns only the security jobs' do
|
||||||
is_expected.to include(sast_build)
|
is_expected.to include(sast_build)
|
||||||
is_expected.to include(container_scanning_build)
|
is_expected.to include(container_scanning_build)
|
||||||
is_expected.to include(dast_build)
|
is_expected.to include(dast_build)
|
||||||
is_expected.to include(secret_detection_build)
|
is_expected.to include(secret_detection_build)
|
||||||
is_expected.not_to include(license_management_build)
|
is_expected.not_to include(license_scanning_build)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { GlTab, GlTabs } from '@gitlab/ui';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||||
|
import {
|
||||||
|
SAST_NAME,
|
||||||
|
SAST_SHORT_NAME,
|
||||||
|
SAST_DESCRIPTION,
|
||||||
|
SAST_HELP_PATH,
|
||||||
|
SAST_CONFIG_HELP_PATH,
|
||||||
|
} from '~/security_configuration/components/constants';
|
||||||
|
import FeatureCard from '~/security_configuration/components/feature_card.vue';
|
||||||
|
import RedesignedSecurityConfigurationApp, {
|
||||||
|
i18n,
|
||||||
|
} from '~/security_configuration/components/redesigned_app.vue';
|
||||||
|
import { REPORT_TYPE_SAST } from '~/vue_shared/security_reports/constants';
|
||||||
|
|
||||||
|
describe('NewApp component', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const createComponent = (propsData) => {
|
||||||
|
wrapper = extendedWrapper(
|
||||||
|
mount(RedesignedSecurityConfigurationApp, {
|
||||||
|
propsData,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const findMainHeading = () => wrapper.find('h1');
|
||||||
|
const findSubHeading = () => wrapper.find('h2');
|
||||||
|
const findTab = () => wrapper.findComponent(GlTab);
|
||||||
|
const findTabs = () => wrapper.findAllComponents(GlTabs);
|
||||||
|
const findByTestId = (id) => wrapper.findByTestId(id);
|
||||||
|
const findFeatureCards = () => wrapper.findAllComponents(FeatureCard);
|
||||||
|
|
||||||
|
const securityFeaturesMock = [
|
||||||
|
{
|
||||||
|
name: SAST_NAME,
|
||||||
|
shortName: SAST_SHORT_NAME,
|
||||||
|
description: SAST_DESCRIPTION,
|
||||||
|
helpPath: SAST_HELP_PATH,
|
||||||
|
configurationHelpPath: SAST_CONFIG_HELP_PATH,
|
||||||
|
type: REPORT_TYPE_SAST,
|
||||||
|
available: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('basic structure', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
createComponent({
|
||||||
|
augmentedSecurityFeatures: securityFeaturesMock,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders main-heading with correct text', () => {
|
||||||
|
const mainHeading = findMainHeading();
|
||||||
|
expect(mainHeading).toExist();
|
||||||
|
expect(mainHeading.text()).toContain('Security Configuration');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders GlTab Component ', () => {
|
||||||
|
expect(findTab()).toExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders right amount of tabs with correct title ', () => {
|
||||||
|
expect(findTabs().length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders security-testing tab', () => {
|
||||||
|
expect(findByTestId('security-testing-tab')).toExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders sub-heading with correct text', () => {
|
||||||
|
const subHeading = findSubHeading();
|
||||||
|
expect(subHeading).toExist();
|
||||||
|
expect(subHeading.text()).toContain(i18n.securityTesting);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders right amount of feature cards for given props with correct props', () => {
|
||||||
|
const cards = findFeatureCards();
|
||||||
|
expect(cards.length).toEqual(1);
|
||||||
|
expect(cards.at(0).props()).toEqual({ feature: securityFeaturesMock[0] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show latest pipeline link when latestPipelinePath is not defined', () => {
|
||||||
|
expect(findByTestId('latest-pipeline-info').exists()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when given latestPipelinePath props', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
createComponent({
|
||||||
|
augmentedSecurityFeatures: securityFeaturesMock,
|
||||||
|
latestPipelinePath: 'test/path',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show latest pipeline info with correct link when latestPipelinePath is defined', () => {
|
||||||
|
expect(findByTestId('latest-pipeline-info').exists()).toBe(true);
|
||||||
|
expect(findByTestId('latest-pipeline-info').text()).toMatchInterpolatedText(
|
||||||
|
i18n.securityTestingDescription,
|
||||||
|
);
|
||||||
|
expect(findByTestId('latest-pipeline-info').find('a').attributes('href')).toBe('test/path');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { augmentFeatures } from '~/security_configuration/utils';
|
||||||
|
|
||||||
|
const mockSecurityFeatures = [
|
||||||
|
{
|
||||||
|
name: 'SAST',
|
||||||
|
type: 'SAST',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockComplianceFeatures = [
|
||||||
|
{
|
||||||
|
name: 'LICENSE_COMPLIANCE',
|
||||||
|
type: 'LICENSE_COMPLIANCE',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockFeaturesWithSecondary = [
|
||||||
|
{
|
||||||
|
name: 'DAST',
|
||||||
|
type: 'DAST',
|
||||||
|
secondary: {
|
||||||
|
type: 'DAST PROFILES',
|
||||||
|
name: 'DAST PROFILES',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockInvalidCustomFeature = [
|
||||||
|
{
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockValidCustomFeature = [
|
||||||
|
{
|
||||||
|
name: 'SAST',
|
||||||
|
type: 'SAST',
|
||||||
|
customfield: 'customvalue',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const expectedOutputDefault = {
|
||||||
|
augmentedSecurityFeatures: mockSecurityFeatures,
|
||||||
|
augmentedComplianceFeatures: mockComplianceFeatures,
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedOutputSecondary = {
|
||||||
|
augmentedSecurityFeatures: mockSecurityFeatures,
|
||||||
|
augmentedComplianceFeatures: mockFeaturesWithSecondary,
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedOutputCustomFeature = {
|
||||||
|
augmentedSecurityFeatures: mockValidCustomFeature,
|
||||||
|
augmentedComplianceFeatures: mockComplianceFeatures,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('returns an object with augmentedSecurityFeatures and augmentedComplianceFeatures when', () => {
|
||||||
|
it('given an empty array', () => {
|
||||||
|
expect(augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, [])).toEqual(
|
||||||
|
expectedOutputDefault,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('given an invalid populated array', () => {
|
||||||
|
expect(
|
||||||
|
augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockInvalidCustomFeature),
|
||||||
|
).toEqual(expectedOutputDefault);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('features have secondary key', () => {
|
||||||
|
expect(augmentFeatures(mockSecurityFeatures, mockFeaturesWithSecondary, [])).toEqual(
|
||||||
|
expectedOutputSecondary,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('given a valid populated array', () => {
|
||||||
|
expect(
|
||||||
|
augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockValidCustomFeature),
|
||||||
|
).toEqual(expectedOutputCustomFeature);
|
||||||
|
});
|
||||||
|
});
|
|
@ -66,14 +66,16 @@ RSpec.describe Mutations::Ci::Runner::Update do
|
||||||
|
|
||||||
context 'with valid arguments' do
|
context 'with valid arguments' do
|
||||||
it 'updates runner with correct values' do
|
it 'updates runner with correct values' do
|
||||||
expected_attributes = mutation_params.except(:id)
|
expected_attributes = mutation_params.except(:id, :tag_list)
|
||||||
|
|
||||||
subject
|
subject
|
||||||
|
|
||||||
expect(subject[:errors]).to be_empty
|
expect(subject[:errors]).to be_empty
|
||||||
expect(subject[:runner]).to be_an_instance_of(Ci::Runner)
|
expect(subject[:runner]).to be_an_instance_of(Ci::Runner)
|
||||||
expect(subject[:runner]).to have_attributes(expected_attributes)
|
expect(subject[:runner]).to have_attributes(expected_attributes)
|
||||||
|
expect(subject[:runner].tag_list).to contain_exactly(*mutation_params[:tag_list])
|
||||||
expect(runner.reload).to have_attributes(expected_attributes)
|
expect(runner.reload).to have_attributes(expected_attributes)
|
||||||
|
expect(runner.tag_list).to contain_exactly(*mutation_params[:tag_list])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl do
|
||||||
|
let(:services_table) { table(:services) }
|
||||||
|
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
|
||||||
|
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
jira_tracker_data = Class.new(ApplicationRecord) do
|
||||||
|
self.table_name = 'jira_tracker_data'
|
||||||
|
|
||||||
|
def self.encryption_options
|
||||||
|
{
|
||||||
|
key: Settings.attr_encrypted_db_key_base_32,
|
||||||
|
encode: true,
|
||||||
|
mode: :per_attribute_iv,
|
||||||
|
algorithm: 'aes-256-gcm'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_encrypted :url, encryption_options
|
||||||
|
attr_encrypted :api_url, encryption_options
|
||||||
|
attr_encrypted :username, encryption_options
|
||||||
|
attr_encrypted :password, encryption_options
|
||||||
|
end
|
||||||
|
|
||||||
|
stub_const('JiraTrackerData', jira_tracker_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:tracker_data_cloud) { JiraTrackerData.create!(id: 1, service_id: service_jira_cloud.id, url: "https://test-domain.atlassian.net", deployment_type: 0) }
|
||||||
|
let!(:tracker_data_server) { JiraTrackerData.create!(id: 2, service_id: service_jira_server.id, url: "http://totally-not-jira-server.company.org", deployment_type: 0) }
|
||||||
|
|
||||||
|
subject { described_class.new.perform(tracker_data_cloud.id, tracker_data_server.id) }
|
||||||
|
|
||||||
|
it "changes unknown deployment_types based on URL" do
|
||||||
|
expect(JiraTrackerData.pluck(:deployment_type)).to eq([0, 0])
|
||||||
|
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(JiraTrackerData.pluck(:deployment_type)).to eq([2, 1])
|
||||||
|
end
|
||||||
|
end
|
|
@ -76,10 +76,30 @@ RSpec.describe Gitlab::Ci::Ansi2json::Line do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#set_section_duration' do
|
describe '#set_section_duration' do
|
||||||
it 'sets and formats the section_duration' do
|
shared_examples 'set_section_duration' do
|
||||||
subject.set_section_duration(75)
|
it 'sets and formats the section_duration' do
|
||||||
|
subject.set_section_duration(75)
|
||||||
|
|
||||||
expect(subject.section_duration).to eq('01:15')
|
expect(subject.section_duration).to eq('01:15')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with default timezone' do
|
||||||
|
it_behaves_like 'set_section_duration'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a timezone carrying minutes offset' do
|
||||||
|
before do
|
||||||
|
# The actual call by does use Time.at(...).utc that the following
|
||||||
|
# rubocop rule (Rails/TimeZone) suggests, but for this specific
|
||||||
|
# test's purposes we needed to mock at the Time.at call point.
|
||||||
|
|
||||||
|
# rubocop:disable Rails/TimeZone
|
||||||
|
allow(Time).to receive(:at).with(75).and_return(Time.at(75, in: '+05:30'))
|
||||||
|
# rubocop:enable Rails/TimeZone
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'set_section_duration'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
|
||||||
settings = described_class.default_settings_hash
|
settings = described_class.default_settings_hash
|
||||||
directives = settings['directives']
|
directives = settings['directives']
|
||||||
|
|
||||||
expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com https://example.com")
|
expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com https://example.com")
|
||||||
expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com")
|
expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require Rails.root.join('db', 'post_migrate', '20210421163509_schedule_update_jira_tracker_data_deployment_type_based_on_url.rb')
|
||||||
|
|
||||||
|
RSpec.describe ScheduleUpdateJiraTrackerDataDeploymentTypeBasedOnUrl, :migration do
|
||||||
|
let(:services_table) { table(:services) }
|
||||||
|
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
|
||||||
|
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
jira_tracker_data = Class.new(ApplicationRecord) do
|
||||||
|
self.table_name = 'jira_tracker_data'
|
||||||
|
|
||||||
|
def self.encryption_options
|
||||||
|
{
|
||||||
|
key: Settings.attr_encrypted_db_key_base_32,
|
||||||
|
encode: true,
|
||||||
|
mode: :per_attribute_iv,
|
||||||
|
algorithm: 'aes-256-gcm'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_encrypted :url, encryption_options
|
||||||
|
attr_encrypted :api_url, encryption_options
|
||||||
|
attr_encrypted :username, encryption_options
|
||||||
|
attr_encrypted :password, encryption_options
|
||||||
|
end
|
||||||
|
|
||||||
|
stub_const('JiraTrackerData', jira_tracker_data)
|
||||||
|
stub_const("#{described_class}::BATCH_SIZE", 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:tracker_data_cloud) { JiraTrackerData.create!(id: 1, service_id: service_jira_cloud.id, url: "https://test-domain.atlassian.net", deployment_type: 0) }
|
||||||
|
let!(:tracker_data_server) { JiraTrackerData.create!(id: 2, service_id: service_jira_server.id, url: "http://totally-not-jira-server.company.org", deployment_type: 0) }
|
||||||
|
|
||||||
|
around do |example|
|
||||||
|
freeze_time { Sidekiq::Testing.fake! { example.run } }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'schedules background migration' do
|
||||||
|
migrate!
|
||||||
|
|
||||||
|
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
|
||||||
|
expect(described_class::MIGRATION).to be_scheduled_migration(tracker_data_cloud.id, tracker_data_cloud.id)
|
||||||
|
expect(described_class::MIGRATION).to be_scheduled_migration(tracker_data_server.id, tracker_data_server.id)
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,7 +3,8 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker do
|
RSpec.describe AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker do
|
||||||
let(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
|
|
||||||
let(:user) { project.namespace.owner }
|
let(:user) { project.namespace.owner }
|
||||||
let(:start_user_id) { user.id }
|
let(:start_user_id) { user.id }
|
||||||
let(:end_user_id) { start_user_id }
|
let(:end_user_id) { start_user_id }
|
||||||
|
@ -64,6 +65,18 @@ RSpec.describe AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker do
|
||||||
|
|
||||||
execute_worker
|
execute_worker
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when load balancing is enabled' do
|
||||||
|
before do
|
||||||
|
allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'reads from the primary database' do
|
||||||
|
expect(Gitlab::Database::LoadBalancing::Session.current).to receive(:use_primary!)
|
||||||
|
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue