Commit 6982ab91 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 386abf71 4ee3f688
......@@ -4,6 +4,7 @@ import initUserPopovers from '../../user_popovers';
import highlightCurrentUser from './highlight_current_user';
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
import renderSandboxedMermaid from './render_sandboxed_mermaid';
import renderMetrics from './render_metrics';
// Render GitLab flavoured Markdown
......@@ -13,7 +14,11 @@ import renderMetrics from './render_metrics';
$.fn.renderGFM = function renderGFM() {
syntaxHighlight(this.find('.js-syntax-highlight').get());
renderMath(this.find('.js-render-math'));
renderMermaid(this.find('.js-render-mermaid'));
if (gon.features?.sandboxedMermaid) {
renderSandboxedMermaid(this.find('.js-render-mermaid'));
} else {
renderMermaid(this.find('.js-render-mermaid'));
}
highlightCurrentUser(this.find('.gfm-project_member').get());
initUserPopovers(this.find('.js-user-link').get());
......
import $ from 'jquery';
import { once, countBy } from 'lodash';
import { __ } from '~/locale';
import {
getBaseURL,
relativePathToAbsolute,
setUrlParams,
joinPaths,
} from '~/lib/utils/url_utility';
import { darkModeEnabled } from '~/lib/utils/color_utils';
import { setAttributes } from '~/lib/utils/dom_utils';
// Renders diagrams and flowcharts from text using Mermaid in any element with the
// `js-render-mermaid` class.
//
// Example markup:
//
// <pre class="js-render-mermaid">
// graph TD;
// A-- > B;
// A-- > C;
// B-- > D;
// C-- > D;
// </pre>
//
const SANDBOX_FRAME_PATH = '/-/sandbox/mermaid';
// This is an arbitrary number; Can be iterated upon when suitable.
const MAX_CHAR_LIMIT = 2000;
// Max # of mermaid blocks that can be rendered in a page.
const MAX_MERMAID_BLOCK_LIMIT = 50;
// Max # of `&` allowed in Chaining of links syntax
const MAX_CHAINING_OF_LINKS_LIMIT = 30;
// Keep a map of mermaid blocks we've already rendered.
const elsProcessingMap = new WeakMap();
let renderedMermaidBlocks = 0;
// Pages without any restrictions on mermaid rendering
const PAGES_WITHOUT_RESTRICTIONS = [
// Group wiki
'groups:wikis:show',
'groups:wikis:edit',
'groups:wikis:create',
// Project wiki
'projects:wikis:show',
'projects:wikis:edit',
'projects:wikis:create',
// Project files
'projects:show',
'projects:blob:show',
];
function shouldLazyLoadMermaidBlock(source) {
/**
* If source contains `&`, which means that it might
* contain Chaining of links a new syntax in Mermaid.
*/
if (countBy(source)['&'] > MAX_CHAINING_OF_LINKS_LIMIT) {
return true;
}
return false;
}
function fixElementSource(el) {
// Mermaid doesn't like `<br />` tags, so collapse all like tags into `<br>`, which is parsed correctly.
const source = el.textContent?.replace(/<br\s*\/>/g, '<br>');
// Remove any extra spans added by the backend syntax highlighting.
Object.assign(el, { textContent: source });
return { source };
}
function getSandboxFrameSrc() {
const path = joinPaths(gon.relative_url_root || '', SANDBOX_FRAME_PATH);
if (!darkModeEnabled()) {
return path;
}
const absoluteUrl = relativePathToAbsolute(path, getBaseURL());
return setUrlParams({ darkMode: darkModeEnabled() }, absoluteUrl);
}
function renderMermaidEl(el, source) {
const iframeEl = document.createElement('iframe');
setAttributes(iframeEl, {
src: getSandboxFrameSrc(),
sandbox: 'allow-scripts',
frameBorder: 0,
scrolling: 'no',
});
// Add the original source into the DOM
// to allow Copy-as-GFM to access it.
const sourceEl = document.createElement('text');
sourceEl.textContent = source;
sourceEl.classList.add('gl-display-none');
const wrapper = document.createElement('div');
wrapper.appendChild(iframeEl);
wrapper.appendChild(sourceEl);
el.closest('pre').replaceWith(wrapper);
// Event Listeners
iframeEl.addEventListener('load', () => {
// Potential risk associated with '*' discussed in below thread
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74414#note_735183398
iframeEl.contentWindow.postMessage(source, '*');
});
window.addEventListener(
'message',
(event) => {
if (event.origin !== 'null' || event.source !== iframeEl.contentWindow) {
return;
}
const { h, w } = event.data;
iframeEl.width = w;
iframeEl.height = h;
},
false,
);
}
function renderMermaids($els) {
if (!$els.length) return;
const pageName = document.querySelector('body').dataset.page;
// A diagram may have been truncated in search results which will cause errors, so abort the render.
if (pageName === 'search:show') return;
let renderedChars = 0;
$els.each((i, el) => {
// Skipping all the elements which we've already queued in requestIdleCallback
if (elsProcessingMap.has(el)) {
return;
}
const { source } = fixElementSource(el);
/**
* Restrict the rendering to a certain amount of character
* and mermaid blocks to prevent mermaidjs from hanging
* up the entire thread and causing a DoS.
*/
if (
!PAGES_WITHOUT_RESTRICTIONS.includes(pageName) &&
((source && source.length > MAX_CHAR_LIMIT) ||
renderedChars > MAX_CHAR_LIMIT ||
renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT ||
shouldLazyLoadMermaidBlock(source))
) {
const html = `
<div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert">
<div>
<div>
<div class="js-warning-text"></div>
<div class="gl-alert-actions">
<button type="button" class="js-lazy-render-mermaid btn gl-alert-action btn-warning btn-md gl-button">Display</button>
</div>
</div>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
`;
const $parent = $(el).parent();
if (!$parent.hasClass('lazy-alert-shown')) {
$parent.after(html);
$parent
.siblings()
.find('.js-warning-text')
.text(
__('Warning: Displaying this diagram might cause performance issues on this page.'),
);
$parent.addClass('lazy-alert-shown');
}
return;
}
renderedChars += source.length;
renderedMermaidBlocks += 1;
const requestId = window.requestIdleCallback(() => {
renderMermaidEl(el, source);
});
elsProcessingMap.set(el, requestId);
});
}
const hookLazyRenderMermaidEvent = once(() => {
$(document.body).on('click', '.js-lazy-render-mermaid', function eventHandler() {
const parent = $(this).closest('.js-lazy-render-mermaid-container');
const pre = parent.prev();
const el = pre.find('.js-render-mermaid');
parent.remove();
// sandbox update
const element = el.get(0);
const { source } = fixElementSource(element);
renderMermaidEl(element, source);
});
});
export default function renderMermaid($els) {
if (!$els.length) return;
const visibleMermaids = $els.filter(function filter() {
return $(this).closest('details').length === 0 && $(this).is(':visible');
});
renderMermaids(visibleMermaids);
$els.closest('details').one('toggle', function toggle() {
if (this.open) {
renderMermaids($(this).find('.js-render-mermaid'));
}
});
hookLazyRenderMermaidEvent();
}
import mermaid from 'mermaid';
import { getParameterByName } from '~/lib/utils/url_utility';
const setIframeRenderedSize = (h, w) => {
const { origin } = window.location;
window.parent.postMessage({ h, w }, origin);
};
const drawDiagram = (source) => {
const element = document.getElementById('app');
const insertSvg = (svgCode) => {
element.innerHTML = svgCode;
const height = parseInt(element.firstElementChild.getAttribute('height'), 10);
const width = parseInt(element.firstElementChild.style.maxWidth, 10);
setIframeRenderedSize(height, width);
};
mermaid.mermaidAPI.render('mermaid', source, insertSvg);
};
const darkModeEnabled = () => getParameterByName('darkMode') === 'true';
const initMermaid = () => {
let theme = 'neutral';
if (darkModeEnabled()) {
theme = 'dark';
}
mermaid.initialize({
// mermaid core options
mermaid: {
startOnLoad: false,
},
// mermaidAPI options
theme,
flowchart: {
useMaxWidth: true,
htmlLabels: true,
},
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize', 'htmlLabels'],
securityLevel: 'strict',
});
};
const addListener = () => {
window.addEventListener(
'message',
(event) => {
if (event.origin !== window.location.origin) {
return;
}
drawDiagram(event.data);
},
false,
);
};
addListener();
initMermaid();
export default {};
......@@ -212,7 +212,9 @@ export default {
</script>
<template>
<div class="js-pipeline-header-container">
<gl-alert v-if="hasError" :variant="failure.variant">{{ failure.text }}</gl-alert>
<gl-alert v-if="hasError" :variant="failure.variant" :dismissible="false">{{
failure.text
}}</gl-alert>
<ci-header
v-if="shouldRenderContent"
:status="pipeline.detailedStatus"
......
<script>
import { GlIcon, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { GlIcon, GlLink, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { __, n__, sprintf } from '~/locale';
import createFlash from '~/flash';
import { convertToGraphQLId } from '~/graphql_shared/utils';
......@@ -10,6 +10,7 @@ import issueCrmContactsSubscription from './queries/issue_crm_contacts.subscript
export default {
components: {
GlIcon,
GlLink,
GlPopover,
},
directives: {
......@@ -85,9 +86,6 @@ export default {
);
},
},
i18n: {
help: __('Work in progress- click here to find out more'),
},
};
</script>
......@@ -97,11 +95,10 @@ export default {
<gl-icon name="users" />
<span> {{ contactCount }} </span>
</div>
<div
v-gl-tooltip.left.viewport="$options.i18n.help"
class="hide-collapsed help-button float-right"
>
<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/2256"><gl-icon name="question-o" /></a>
<div class="hide-collapsed help-button gl-float-right">
<gl-link href="https://docs.gitlab.com/ee/user/crm/" target="_blank"
><gl-icon name="question-o"
/></gl-link>
</div>
<div class="title hide-collapsed gl-mb-2 gl-line-height-20">
{{ contactsLabel }}
......
# frozen_string_literal: true
class SandboxController < ApplicationController # rubocop:disable Gitlab/NamespacedClass
skip_before_action :authenticate_user!
feature_category :not_owned
def mermaid
render layout: false
end
end
......@@ -24,7 +24,7 @@ module ResolvesPipelines
argument :source,
GraphQL::Types::String,
required: false,
description: "Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled."
description: "Filter pipelines by their source."
end
class_methods do
......@@ -38,8 +38,6 @@ module ResolvesPipelines
end
def resolve_pipelines(project, params = {})
params.delete(:source) unless Feature.enabled?(:dast_view_scans, project, default_enabled: :yaml)
Ci::PipelinesFinder.new(project, context[:current_user], params).execute
end
end
<!DOCTYPE html>
<html>
<head>
<%= webpack_bundle_tag("sandboxed_mermaid") %>
</head>
<body>
<div id="app"></div>
</body>
</html>
---
name: dast_view_scans
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69571
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340388
milestone: '14.3'
name: sandboxed_mermaid
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74414
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349755
milestone: '14.7'
type: development
group: group::dynamic analysis
default_enabled: true
group: group::analyzer frontend
default_enabled: false
......@@ -8,7 +8,7 @@ product_stage: create
product_group: group::code review
product_category: code_review
value_type: number
status: active
status: removed
time_frame: 28d
data_source: database
distribution:
......@@ -20,3 +20,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
milestone_removed: '14.7'
......@@ -7,7 +7,7 @@ product_stage: create
product_group: group::code review
product_category: code_review
value_type: number
status: active
status: removed
time_frame: all
data_source: database
distribution:
......@@ -18,3 +18,4 @@ tier:
- premium
- ultimate
milestone: "<13.9"
milestone_removed: '14.7'
......@@ -108,6 +108,9 @@ Rails.application.routes.draw do
get '/autocomplete/namespace_routes' => 'autocomplete#namespace_routes'
end
# sandbox
get '/sandbox/mermaid' => 'sandbox#mermaid'
get '/whats_new' => 'whats_new#index'
# '/-/health' implemented by BasicHealthCheck middleware
......
......@@ -141,6 +141,7 @@ function generateEntries() {
sentry: './sentry/index.js',
performance_bar: './performance_bar/index.js',
jira_connect_app: './jira_connect/subscriptions/index.js',
sandboxed_mermaid: './lib/mermaid.js',
};
return Object.assign(manualEntries, incrementalCompiler.filterEntryPoints(autoEntries));
......
......@@ -9158,7 +9158,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="commitpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. |
| <a id="commitpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. |
| <a id="commitpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. |
| <a id="commitpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled. |
| <a id="commitpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. |
| <a id="commitpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. |
### `ComplianceFramework`
......@@ -11911,7 +11911,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="mergerequestpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. |
| <a id="mergerequestpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. |
| <a id="mergerequestpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. |
| <a id="mergerequestpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled. |
| <a id="mergerequestpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. |
| <a id="mergerequestpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. |
##### `MergeRequest.reference`
......@@ -12945,7 +12945,7 @@ Represents a file or directory in the project repository that has been locked.
| <a id="pipelineconfigsource"></a>`configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). |
| <a id="pipelinecoverage"></a>`coverage` | [`Float`](#float) | Coverage percentage. |
| <a id="pipelinecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of the pipeline's creation. |
| <a id="pipelinedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | DAST profile associated with the pipeline. Returns `null`if `dast_view_scans` feature flag is disabled. |
| <a id="pipelinedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | DAST profile associated with the pipeline. |
| <a id="pipelinedetailedstatus"></a>`detailedStatus` | [`DetailedStatus!`](#detailedstatus) | Detailed status of the pipeline. |
| <a id="pipelinedownstream"></a>`downstream` | [`PipelineConnection`](#pipelineconnection) | Pipelines this pipeline will trigger. (see [Connections](#connections)) |
| <a id="pipelineduration"></a>`duration` | [`Int`](#int) | Duration of the pipeline in seconds. |
......@@ -13377,7 +13377,7 @@ Returns [`DastProfile`](#dastprofile).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectdastprofilehasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. Will be ignored if `dast_view_scans` feature flag is disabled. |
| <a id="projectdastprofilehasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. |
| <a id="projectdastprofileid"></a>`id` | [`DastProfileID!`](#dastprofileid) | ID of the DAST Profile. |
##### `Project.dastProfiles`
......@@ -13394,7 +13394,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectdastprofileshasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. Will be ignored if `dast_view_scans` feature flag is disabled. |
| <a id="projectdastprofileshasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. |
##### `Project.dastSiteProfile`
......@@ -13854,7 +13854,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. |
| <a id="projectpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. |
| <a id="projectpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. |
| <a id="projectpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled. |
| <a id="projectpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. |
| <a id="projectpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. |
##### `Project.projectMembers`
......@@ -11,7 +11,6 @@ module Projects
feature_category :dynamic_application_security_testing
def index
redirect_to new_project_on_demand_scan_path(project) unless Feature.enabled?(:dast_view_scans, @project, default_enabled: :yaml)
end
def new
......
......@@ -28,15 +28,14 @@ module EE
field :dast_profile,
::Types::Dast::ProfileType,
null: true,
description: 'DAST profile associated with the pipeline. Returns `null`' \
'if `dast_view_scans` feature flag is disabled.'
description: 'DAST profile associated with the pipeline.'
def code_quality_reports
pipeline.codequality_reports.sort_degradations!.values.presence
end
def dast_profile
pipeline.dast_profile if ::Feature.enabled?(:dast_view_scans, pipeline.project, default_enabled: :yaml)
pipeline.dast_profile
end
end
end
......
......@@ -12,7 +12,7 @@ module Resolvers
argument :has_dast_profile_schedule, ::GraphQL::Types::Boolean,
required: false,
description: 'Filter DAST Profiles by whether or not they have a schedule. Will be ignored if `dast_view_scans` feature flag is disabled.'
description: 'Filter DAST Profiles by whether or not they have a schedule.'
when_single do
argument :id, ::Types::GlobalIDType[::Dast::Profile],
......@@ -21,7 +21,6 @@ module Resolvers
end
def resolve_with_lookahead(**args)
args.delete(:has_dast_profile_schedule) unless Feature.enabled?(:dast_view_scans, project, default_enabled: :yaml)
apply_lookahead(find_dast_profiles(args))
end
......
......@@ -91,11 +91,7 @@ module EE
return ::Sidebars::NilMenuItem.new(item_id: :on_demand_scans)
end
link = if ::Feature.enabled?(:dast_view_scans, context.project, default_enabled: :yaml)
project_on_demand_scans_path(context.project)
else
new_project_on_demand_scan_path(context.project)
end
link = project_on_demand_scans_path(context.project)
::Sidebars::MenuItem.new(
title: s_('OnDemandScans|On-demand scans'),
......
......@@ -136,16 +136,8 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do
it_behaves_like 'with different scan type' do
let(:expected_configuration) do
{
'secret-detection-0': {
'secret-detection-0': hash_including(
rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }],
script:
['if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi',
'if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi',
'git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME',
'git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/$CI_DEFAULT_BRANCH...refs/remotes/origin/$CI_COMMIT_REF_NAME > "$CI_COMMIT_SHA"_commit_list.txt',
'export SECRET_DETECTION_COMMITS_FILE="$CI_COMMIT_SHA"_commit_list.txt',
'/analyzer run',
'rm "$CI_COMMIT_SHA"_commit_list.txt'],
stage: 'test',
image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION',
services: [],
......@@ -160,8 +152,7 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do
SECRETS_ANALYZER_VERSION: '3',
SECRET_DETECTION_EXCLUDED_PATHS: '',
SECRET_DETECTION_HISTORIC_SCAN: 'false'
}
}
})
}
end
end
......
......@@ -109,33 +109,13 @@ RSpec.describe 'Query.project(fullPath).dastProfiles' do
expect { subject }.not_to exceed_query_limit(control)
end
context 'when `dast_view_scans` feature flag is disabled' do
before do
stub_feature_flags(dast_view_scans: false)
end
context 'when hasDastProfileSchedule is false' do
let(:query_args) { { hasDastProfileSchedule: false } }
include_examples 'returns all dastProfiles'
end
context 'when hasDastProfileSchedule is true' do
let(:query_args) { { hasDastProfileSchedule: true } }
context 'when hasDastProfileSchedule is true' do
let(:query_args) { { hasDastProfileSchedule: true } }
include_examples 'returns all dastProfiles'
end
end
context 'when `dast_view_scans` feature flag is enabled' do
context 'when hasDastProfileSchedule is true' do
let(:query_args) { { hasDastProfileSchedule: true } }
it 'returns all dastProfiles with a schedule' do
subject
it 'returns all dastProfiles with a schedule' do
subject
expect(graphql_data_at(:project, :dast_profiles, :nodes, :id)).to contain_exactly(dast_profile5.to_global_id.to_s)
end
expect(graphql_data_at(:project, :dast_profiles, :nodes, :id)).to contain_exactly(dast_profile5.to_global_id.to_s)
end
end
end
......
......@@ -78,16 +78,6 @@ RSpec.describe 'Query.project(fullPath).pipelines.dastProfile' do
expect { subject }.not_to exceed_query_limit(control)
expect(dast_profile_data.size).to eq(6)
end
context 'when feature flag is not enabled' do
it 'does not return dast profile data' do
stub_feature_flags(dast_view_scans: false)
subject
expect(dast_profile_data).to contain_exactly(nil)
end
end
end
end
end
......@@ -73,20 +73,6 @@ RSpec.describe Projects::OnDemandScansController, type: :request do
it_behaves_like 'on-demand scans page' do
let(:path) { project_on_demand_scans_path(project) }
end
context 'when dast_view_scans feature flag is disabled' do
before do
stub_licensed_features(security_on_demand_scans: true)
stub_feature_flags(dast_view_scans: false)
project.add_developer(user)
login_as(user)
get project_on_demand_scans_path(project)
end
it 'redirects to new on-demands scans form' do
expect(response).to redirect_to(new_project_on_demand_scan_path(project))
end
end
end
describe 'GET #new' do
......
......@@ -29,14 +29,6 @@ RSpec.describe Security::SecurityOrchestrationPolicies::CiConfigurationService d
it 'returns prepared CI configuration with Secret Detection scans' do
expected_configuration = {
rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }],
script:
['if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi',
'if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi',
'git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME',
'git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/$CI_DEFAULT_BRANCH...refs/remotes/origin/$CI_COMMIT_REF_NAME > "$CI_COMMIT_SHA"_commit_list.txt',
'export SECRET_DETECTION_COMMITS_FILE="$CI_COMMIT_SHA"_commit_list.txt',
'/analyzer run',
'rm "$CI_COMMIT_SHA"_commit_list.txt'],
stage: 'test',
image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION',
services: [],
......@@ -54,7 +46,7 @@ RSpec.describe Security::SecurityOrchestrationPolicies::CiConfigurationService d
}
}
expect(subject.deep_symbolize_keys).to eq(expected_configuration)
expect(subject.deep_symbolize_keys).to include(expected_configuration)
end
end
......
......@@ -210,22 +210,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
expect(rendered).to have_link('Audit events', href: project_audit_events_path(project))
end
end
context 'when dast_view_scans feature flag is disabled' do
before do
allow(view).to receive(:current_user).and_return(user)
stub_feature_flags(dast_view_scans: false)
stub_licensed_features(
security_on_demand_scans: true
)
render
end
it 'links to on-demand scans form instead of index page' do
expect(rendered).to have_link('On-demand scans', href: new_project_on_demand_scan_path(project))
end
end
end
describe 'Operations' do
......
......@@ -29,8 +29,16 @@ secret_detection:
script:
- if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi
- if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi
- git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME
- git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/$CI_DEFAULT_BRANCH...refs/remotes/origin/$CI_COMMIT_REF_NAME > "$CI_COMMIT_SHA"_commit_list.txt
- export SECRET_DETECTION_COMMITS_FILE="$CI_COMMIT_SHA"_commit_list.txt
- |
git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME
git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/${CI_DEFAULT_BRANCH}..refs/remotes/origin/${CI_COMMIT_REF_NAME} >${CI_COMMIT_SHA}_commit_list.txt
if [[ $(wc -l <${CI_COMMIT_SHA}_commit_list.txt) -eq "0" ]]; then
# if git log produces 0 or 1 commits we should scan $CI_COMMIT_SHA only
export SECRET_DETECTION_COMMITS=$CI_COMMIT_SHA
else
# +1 because busybox wc only counts \n and there is no trailing \n
echo "scanning $(($(wc -l <${CI_COMMIT_SHA}_commit_list.txt) + 1)) commits"
export SECRET_DETECTION_COMMITS_FILE=${CI_COMMIT_SHA}_commit_list.txt
fi
- /analyzer run
- rm "$CI_COMMIT_SHA"_commit_list.txt
......@@ -147,7 +147,7 @@ module Gitlab
# Using 'self' in the CSP introduces several CSP bypass opportunities
# for this reason we list the URLs where GitLab frames itself instead
def self.allow_framed_gitlab_paths(directives)
['/admin/', '/assets/', '/-/speedscope/index.html'].map do |path|
['/admin/', '/assets/', '/-/speedscope/index.html', '/-/sandbox/mermaid'].map do |path|
append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, path))
end
end
......
......@@ -57,6 +57,7 @@ module Gitlab
push_frontend_feature_flag(:improved_emoji_picker, default_enabled: :yaml)
push_frontend_feature_flag(:new_header_search, default_enabled: :yaml)
push_frontend_feature_flag(:bootstrap_confirmation_modals, default_enabled: :yaml)
push_frontend_feature_flag(:sandboxed_mermaid, default_enabled: :yaml)
end
# Exposes the state of a feature flag to the frontend code.
......
......@@ -522,11 +522,7 @@ module Gitlab
projects_with_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: true))),
projects_without_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: [false, nil]))),
remote_mirrors: distinct_count(::Project.with_remote_mirrors.where(time_period), :creator_id),
snippets: distinct_count(::Snippet.where(time_period), :author_id),
suggestions: distinct_count(::Note.with_suggestions.where(time_period),
:author_id,
start: minimum_id(::User),
finish: maximum_id(::User))
snippets: distinct_count(::Snippet.where(time_period), :author_id)
}.tap do |h|
if time_period.present?
h[:merge_requests_users] = merge_requests_users(time_period)
......
......@@ -40360,9 +40360,6 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
msgid "Work in progress- click here to find out more"
msgstr ""
msgid "WorkItem|Work Items"
msgstr ""
......
......@@ -11,6 +11,7 @@ RSpec.describe "User comments on issue", :js do
before do
stub_feature_flags(tribute_autocomplete: false)
stub_feature_flags(sandboxed_mermaid: false)
project.add_guest(user)
sign_in(user)
......
......@@ -5,6 +5,10 @@ require 'spec_helper'
RSpec.describe 'Mermaid rendering', :js do
let_it_be(:project) { create(:project, :public) }
before do
stub_feature_flags(sandboxed_mermaid: false)
end
it 'renders Mermaid diagrams correctly' do
description = <<~MERMAID
```mermaid
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Sandboxed Mermaid rendering', :js do
let_it_be(:project) { create(:project, :public) }
before do
stub_feature_flags(sandboxed_mermaid: true)
end
it 'includes mermaid frame correctly' do
description = <<~MERMAID
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
MERMAID
issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue)
wait_for_requests
expected = %(<iframe src="/-/sandbox/mermaid" sandbox="allow-scripts" frameborder="0" scrolling="no")
expect(page.html).to include(expected)
end
end
......@@ -62,24 +62,12 @@ RSpec.describe ResolvesPipelines do
context 'filtering by source' do
let_it_be(:source_pipeline) { create(:ci_pipeline, project: project, source: 'web') }
context 'when `dast_view_scans` feature flag is disabled' do
before do
stub_feature_flags(dast_view_scans: false)
end
it 'does not filter by source' do
expect(resolve_pipelines(source: 'web')).to contain_exactly(*all_pipelines, source_pipeline)
end
it 'does filter by source' do
expect(resolve_pipelines(source: 'web')).to contain_exactly(source_pipeline)
end
context 'when `dast_view_scans` feature flag is enabled' do
it 'does filter by source' do
expect(resolve_pipelines(source: 'web')).to contain_exactly(source_pipeline)
end
it 'returns all the pipelines' do
expect(resolve_pipelines).to contain_exactly(*all_pipelines, source_pipeline)
end
it 'returns all the pipelines' do
expect(resolve_pipelines).to contain_exactly(*all_pipelines, source_pipeline)
end
end
......
......@@ -85,7 +85,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://cdn.example.com")
expect(directives['font_src']).to eq("'self' https://cdn.example.com")
expect(directives['worker_src']).to eq('http://localhost/assets/ blob: data: https://cdn.example.com')
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " https://cdn.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html")
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " https://cdn.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html http://localhost/-/sandbox/mermaid")
end
end
......@@ -113,7 +113,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
it 'does not add CUSTOMER_PORTAL_URL to CSP' do
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html")
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html http://localhost/-/sandbox/mermaid")
end
end
......@@ -123,7 +123,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
it 'adds CUSTOMER_PORTAL_URL to CSP' do
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/rails/letter_opener/ https://customers.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html")
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/rails/letter_opener/ https://customers.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html http://localhost/-/sandbox/mermaid")
end
end
end
......
......@@ -161,7 +161,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
another_project = create(:project, :repository, creator: another_user)
create(:remote_mirror, project: another_project, enabled: false)
create(:snippet, author: user)
create(:suggestion, note: create(:note, project: project))
end
expect(described_class.usage_activity_by_stage_create({})).to include(
......@@ -171,8 +170,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
projects_with_disable_overriding_approvers_per_merge_request: 2,
projects_without_disable_overriding_approvers_per_merge_request: 6,
remote_mirrors: 2,
snippets: 2,
suggestions: 2
snippets: 2
)
expect(described_class.usage_activity_by_stage_create(described_class.monthly_time_range_db_params)).to include(
deploy_keys: 1,
......@@ -181,8 +179,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
projects_with_disable_overriding_approvers_per_merge_request: 1,
projects_without_disable_overriding_approvers_per_merge_request: 3,
remote_mirrors: 1,
snippets: 1,
suggestions: 1
snippets: 1
)
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe SandboxController do
describe 'GET #mermaid' do
it 'renders page without template' do
get sandbox_mermaid_path
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(layout: nil)
end
end
end
......@@ -364,6 +364,12 @@ RSpec.describe AutocompleteController, 'routing' do
end
end
RSpec.describe SandboxController, 'routing' do
it 'to #mermaid' do
expect(get("/-/sandbox/mermaid")).to route_to('sandbox#mermaid')
end
end
RSpec.describe Snippets::BlobsController, "routing" do
it "to #raw" do
expect(get('/-/snippets/1/raw/master/lib/version.rb'))
......
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