Commit 92fa6bd0 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 1737c368 9e27f8a7
......@@ -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',
......
<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 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,
......
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>
<template>
<div v-if="isLoaded" class="mr-state-widget gl-mt-3">
<header
class="gl-overflow-hidden gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100"
>
<header class="gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100">
<mr-widget-alert-message v-if="shouldRenderCollaborationStatus" type="info">
{{ s__('mrWidget|Members who can merge are allowed to add commits.') }}
</mr-widget-alert-message>
......
......@@ -7,6 +7,10 @@ module Projects
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
render_403 unless can?(current_user, :read_security_configuration, project)
end
......
......@@ -17,7 +17,7 @@
= link_to _('Forgot your password?'), new_password_path(:user)
%div
- if captcha_enabled? || captcha_on_login_required?
= recaptcha_tags
= recaptcha_tags nonce: content_security_policy_nonce
.submit-container.move-submit-down
= f.submit _('Sign in'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'sign_in_button' }
......@@ -9,7 +9,7 @@
.devise-errors
= render 'devise/shared/error_messages', resource: resource
- if Gitlab::CurrentSettings.invisible_captcha_enabled
= invisible_captcha
= invisible_captcha nonce: true
.name.form-row
.col.form-group
= 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 }
%div
- if show_recaptcha_sign_up?
= recaptcha_tags
= recaptcha_tags nonce: content_security_policy_nonce
.submit-container
= 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
......
......@@ -20,7 +20,7 @@
- if captcha_required?
.row.recaptcha
.col-sm-4
= recaptcha_tags
= recaptcha_tags nonce: content_security_policy_nonce
.row
.col-sm-12
= f.submit _('Create group'), class: "btn gl-button btn-confirm"
......
......@@ -10,7 +10,7 @@
= hidden_field(resource_name, field, value: value)
= hidden_field_tag(:spam_log_id, spammable.spam_log.id)
-# 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
-# can get to the (mocked) SpamVerdictService check.
= hidden_field_tag('g-recaptcha-response', 'abc123') if Rails.env.test?
......
......@@ -15,10 +15,8 @@
- audit_reports
- authentication_and_authorization
- auto_devops
- auto_portfolio_mgmt
- backup_restore
- boards
- browser_performance
- chatops
- cloud_native_installation
- cluster_cost_management
......@@ -78,7 +76,6 @@
- license
- license_compliance
- live_preview
- load_testing
- logging
- memory
- merge_trains
......@@ -87,9 +84,11 @@
- mobile_signing_deployment
- navigation
- omnibus_package
- on_call_schedule_management
- onboarding
- package_registry
- pages
- performance_testing
- pipeline_authoring
- planning_analytics
- privacy_control_center
......@@ -111,6 +110,7 @@
- self_monitoring
- serverless
- service_desk
- sharding
- snippets
- source_code_management
- static_application_security_testing
......@@ -130,3 +130,4 @@
- web_firewall
- web_ide
- wiki
- workflow_automation
---
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
# 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
0f6019cc094481cafbf0c9bd42f53ae09034ea87e3f360b02f9ec03192caab9d
\ No newline at end of file
......@@ -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`)
- `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".
### 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
......
......@@ -252,9 +252,7 @@ export default {
</script>
<template>
<div v-if="isLoaded" class="mr-state-widget gl-mt-3">
<header
class="gl-overflow-hidden gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100"
>
<header class="gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100">
<mr-widget-alert-message v-if="shouldRenderCollaborationStatus" type="info">
{{ s__('mrWidget|Members who can merge are allowed to add commits.') }}
</mr-widget-alert-message>
......
# 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
......@@ -18,11 +18,11 @@ module Gitlab
'font_src' => "'self'",
'form_action' => "'self' https: http:",
'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:",
'manifest_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'",
'worker_src' => "'self'",
'object_src' => "'none'",
......
......@@ -28958,6 +28958,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."
msgstr ""
msgid "SecurityConfiguration|Compliance"
msgstr ""
msgid "SecurityConfiguration|Configuration guide"
msgstr ""
......@@ -28997,6 +29000,9 @@ msgstr ""
msgid "SecurityConfiguration|Manage"
msgstr ""
msgid "SecurityConfiguration|Manage scans"
msgstr ""
msgid "SecurityConfiguration|More information"
msgstr ""
......@@ -29012,12 +29018,18 @@ msgstr ""
msgid "SecurityConfiguration|Security Control"
msgstr ""
msgid "SecurityConfiguration|Security testing"
msgstr ""
msgid "SecurityConfiguration|Status"
msgstr ""
msgid "SecurityConfiguration|Testing & Compliance"
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}"
msgstr ""
......
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');
});
});
});
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);
});
});
# 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
......@@ -47,7 +47,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
settings = described_class.default_settings_hash
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")
end
end
......
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment