Commit 400dd846 authored by Avielle Wolfe's avatar Avielle Wolfe Committed by Michael Kozono

Add outline for config page

* Add `/:namespace/:project/security/configuration` route
* Add `Projects::Security::ConfigurationController`
* Add `ProjectsHelper#project_security_configuration`
* add `projects/security/configuration/show.html.haml`
parent f1197b91
......@@ -9,7 +9,7 @@ export default {
Icon,
},
props: {
autoDevOpsEnabled: {
autoDevopsEnabled: {
type: Boolean,
required: false,
default: false,
......@@ -22,7 +22,7 @@ export default {
type: String,
required: true,
},
autoDevOpsHelpPagePath: {
autoDevopsHelpPagePath: {
type: String,
required: true,
},
......@@ -44,8 +44,8 @@ export default {
return sprintf(body, { wordBreakOpportunity }, false);
},
callOutLink() {
if (this.autoDevOpsEnabled) {
return this.autoDevOpsHelpPagePath;
if (this.autoDevopsEnabled) {
return this.autoDevopsHelpPagePath;
}
if (this.latestPipelinePath) {
......@@ -59,11 +59,11 @@ export default {
is based on the %{linkStart}latest pipeline%{linkEnd}.
Once you've configured a scan for the default branch, any subsequent feature branch you create will include the scan.`);
const bodyAutoDevOpsEnabled = __(
const bodyAutoDevopsEnabled = __(
'All security scans are enabled because %{linkStart}Auto DevOps%{linkEnd} is enabled on this project',
);
const body = this.autoDevOpsEnabled ? bodyAutoDevOpsEnabled : bodyDefault;
const body = this.autoDevopsEnabled ? bodyAutoDevopsEnabled : bodyDefault;
const linkStart = `<a href="${this.callOutLink}" target="_blank" rel="noopener">`;
const linkEnd = '</a>';
......
......@@ -4,8 +4,8 @@ import SecurityConfigurationApp from './components/app.vue';
export default function init() {
const el = document.getElementById('js-security-configuration');
const {
autoDevOpsEnabled,
autoDevOpsHelpPagePath,
autoDevopsEnabled,
autoDevopsHelpPagePath,
features,
helpPagePath,
latestPipelinePath,
......@@ -20,8 +20,8 @@ export default function init() {
render(createElement) {
return createElement(SecurityConfigurationApp, {
props: {
autoDevOpsEnabled,
autoDevOpsHelpPagePath,
autoDevopsEnabled,
autoDevopsHelpPagePath,
features: JSON.parse(features),
helpPagePath,
latestPipelinePath,
......
# frozen_string_literal: true
module Projects
module Security
class ConfigurationController < Projects::ApplicationController
include SecurityDashboardsPermissions
alias_method :vulnerable, :project
def show
@configuration = ConfigurationPresenter.new(project)
end
end
end
end
# frozen_string_literal: true
module Projects
module Security
class ConfigurationPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
presents :project
SECURITY_SCAN_TYPES = ::Security::JobsFinder::JOB_TYPES
SCAN_DESCRIPTIONS = {
container_scanning: _('Check your Docker images for known vulnerabilities'),
dast: _('Analyze a review version of your web application.'),
dependency_scanning: _('Analyze your dependencies for known vulnerabilities'),
license_management: _('Search your project dependencies for their licenses and apply policies'),
sast: _('Analyze your source code for known vulnerabilities')
}.freeze
SCAN_DOCS = {
container_scanning: 'user/application_security/container_scanning/index',
dast: 'user/application_security/dast/index',
dependency_scanning: 'user/application_security/dependency_scanning/index',
license_management: 'user/application_security/license_compliance/index',
sast: 'user/application_security/sast/index'
}.freeze
SCAN_NAMES = {
container_scanning: _('Container Scanning'),
dast: _('Dynamic Application Security Testing (DAST)'),
dependency_scanning: _('Dependency Scanning'),
license_management: _('License Compliance'),
sast: _('Static Application Security Testing (SAST)')
}.freeze
def to_h
{
auto_devops_enabled: auto_devops_source?,
auto_devops_help_page_path: help_page_path('topics/autodevops/index'),
features: features.to_json,
help_page_path: help_page_path('user/application_security/index'),
latest_pipeline_path: latest_pipeline_path
}
end
private
def features
SECURITY_SCAN_TYPES.map do |scan_type|
if auto_devops_source?
scan(scan_type, configured: true)
elsif latest_builds_reports.include?(scan_type)
scan(scan_type, configured: true)
else
scan(scan_type, configured: false)
end
end
end
def latest_builds_reports
strong_memoize(:reports) do
latest_security_builds.map do |build|
if Feature.enabled?(:ci_build_metadata_config)
build.metadata.config_options[:artifacts][:reports].keys.map(&:to_sym)
else
build.options[:artifacts][:reports].keys
end
end.flatten
end
end
def latest_security_builds
return [] unless latest_default_branch_pipeline
::Security::JobsFinder.new(pipeline: latest_default_branch_pipeline).execute
end
def latest_default_branch_pipeline
strong_memoize(:pipeline) { latest_pipeline_for_ref }
end
def latest_pipeline_path
return help_page_path('ci/pipelines') unless latest_default_branch_pipeline
project_pipeline_path(self, latest_default_branch_pipeline)
end
def scan(type, configured: false)
{
configured: configured,
description: SCAN_DESCRIPTIONS[type],
link: help_page_path(SCAN_DOCS[type]),
name: SCAN_NAMES[type]
}
end
def auto_devops_source?
latest_default_branch_pipeline&.auto_devops_source?
end
end
end
end
- breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration")
#js-security-configuration{ data: @configuration.to_h }
......@@ -162,6 +162,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
namespace :security do
resource :dashboard, only: [:show], controller: :dashboard
resource :configuration, only: [:show], controller: :configuration
resources :dependencies, only: [:index]
resources :licenses, only: [:index]
# We have to define both legacy and new routes for Vulnerability Findings
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::Security::ConfigurationController do
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
subject { get :show, params: { namespace_id: project.namespace, project_id: project } }
it_behaves_like SecurityDashboardsPermissions do
let(:vulnerable) { project }
let(:security_dashboard_action) do
subject
end
end
describe 'GET #show' do
let(:user) { create(:user) }
render_views
before do
stub_licensed_features(security_dashboard: true)
group.add_developer(user)
sign_in(user)
end
it "renders data on the project's security configuration" do
subject
expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show)
expect(response.body).to have_css(
'div#js-security-configuration'\
"[data-auto-devops-help-page-path=\"#{help_page_path('topics/autodevops/index')}\"]"\
"[data-help-page-path=\"#{help_page_path('user/application_security/index')}\"]"\
"[data-latest-pipeline-path=\"#{help_page_path('ci/pipelines')}\"]"
)
end
context 'when the latest pipeline used Auto DevOps' do
let!(:pipeline) do
create(
:ci_pipeline,
:auto_devops_source,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
end
it 'reports that Auto DevOps is enabled' do
subject
expect(response).to have_gitlab_http_status(200)
expect(response.body).to have_css(
'div#js-security-configuration'\
'[data-auto-devops-enabled]'\
"[data-auto-devops-help-page-path=\"#{help_page_path('topics/autodevops/index')}\"]"\
"[data-help-page-path=\"#{help_page_path('user/application_security/index')}\"]"\
"[data-latest-pipeline-path=\"#{project_pipeline_path(project, pipeline)}\"]"
)
end
end
end
end
......@@ -12,9 +12,9 @@ describe('Security Configuration App', () => {
localVue,
propsData: {
features: [],
autoDevOpsEnabled: false,
autoDevopsEnabled: false,
latestPipelinePath: 'http://latestPipelinePath',
autoDevOpsHelpPagePath: 'http://autoDevOpsHelpPagePath',
autoDevopsHelpPagePath: 'http://autoDevopsHelpPagePath',
helpPagePath: 'http://helpPagePath',
pipelinesHelpPagePath: 'http://pipelineHelpPagePath',
...props,
......@@ -49,14 +49,14 @@ describe('Security Configuration App', () => {
});
it.each`
autoDevOpsEnabled | latestPipelinePath | expectedUrl
${true} | ${'http://latestPipeline'} | ${'http://autoDevOpsHelpPagePath'}
autoDevopsEnabled | latestPipelinePath | expectedUrl
${true} | ${'http://latestPipeline'} | ${'http://autoDevopsHelpPagePath'}
${false} | ${'http://latestPipeline'} | ${'http://latestPipeline'}
${false} | ${undefined} | ${'http://pipelineHelpPagePath'}
`(
'displays a link to "$expectedUrl" when autoDevOps is "$autoDevOpsEnabled" and pipelinesPath is $latestPipelinePath',
({ autoDevOpsEnabled, latestPipelinePath, expectedUrl }) => {
createComponent({ autoDevOpsEnabled, latestPipelinePath });
'displays a link to "$expectedUrl" when autoDevops is "$autoDevopsEnabled" and pipelinesPath is $latestPipelinePath',
({ autoDevopsEnabled, latestPipelinePath, expectedUrl }) => {
createComponent({ autoDevopsEnabled, latestPipelinePath });
expect(getPipelinesLink().attributes('href')).toBe(expectedUrl);
expect(getPipelinesLink().attributes('rel')).toBe('noopener');
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::Security::ConfigurationPresenter do
include Gitlab::Routing.url_helpers
let(:project) { create(:project, :repository) }
it 'presents the given project' do
presenter = described_class.new(project)
expect(presenter.id).to be(project.id)
end
describe '#to_h' do
subject { described_class.new(project).to_h }
it 'includes links to auto devops and secure product docs' do
expect(subject[:auto_devops_help_page_path]).to eq(help_page_path('topics/autodevops/index'))
expect(subject[:help_page_path]).to eq(help_page_path('user/application_security/index'))
end
context "when the latest default branch pipeline's source is auto devops" do
before do
create(
:ci_pipeline,
:auto_devops_source,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
end
it 'reports that auto devops is enabled' do
expect(subject[:auto_devops_enabled]).to be_truthy
end
it 'reports that all security jobs are configured' do
expect(JSON.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: true),
security_scan(:sast, configured: true),
security_scan(:container_scanning, configured: true),
security_scan(:dependency_scanning, configured: true)
)
end
end
context "when the project has no default branch pipeline" do
it 'reports that auto devops is disabled' do
expect(subject[:auto_devops_enabled]).to be_falsy
end
it "includes a link to CI pipeline docs" do
expect(subject[:latest_pipeline_path]).to eq(help_page_path('ci/pipelines'))
end
it 'reports all security jobs as unconfigured' do
expect(JSON.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: false),
security_scan(:sast, configured: false),
security_scan(:container_scanning, configured: false),
security_scan(:dependency_scanning, configured: false)
)
end
end
context "when latest default branch pipeline's source is not auto devops" do
let(:pipeline) do
create(
:ci_pipeline,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
end
before do
create(:ci_build, :sast, pipeline: pipeline)
create(:ci_build, :dast, pipeline: pipeline)
end
it 'uses the latest default branch pipeline to determine whether a security job is configured' do
expect(JSON.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: true),
security_scan(:sast, configured: true),
security_scan(:container_scanning, configured: false),
security_scan(:dependency_scanning, configured: false)
)
end
it 'works with both legacy and current job formats' do
stub_feature_flags(ci_build_metadata_config: false)
create(:ci_build, :sast, pipeline: pipeline)
expect(JSON.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: false),
security_scan(:sast, configured: true),
security_scan(:container_scanning, configured: false),
security_scan(:dependency_scanning, configured: false)
)
end
it 'detects security jobs even when the job has more than one report' do
config = { artifacts: { reports: { other_job: ['gl-other-report.json'], sast: ['gl-sast-report.json'] } } }
complicated_metadata = double(:complicated_metadata, config_options: config)
complicated_job = double(:complicated_job, metadata: complicated_metadata)
allow_next_instance_of(::Security::JobsFinder) do |finder|
allow(finder).to receive(:execute).and_return([complicated_job])
end
subject
expect(JSON.parse(subject[:features])).to contain_exactly(
security_scan(:dast, configured: false),
security_scan(:sast, configured: true),
security_scan(:container_scanning, configured: false),
security_scan(:dependency_scanning, configured: false)
)
end
it 'includes a link to the latest pipeline' do
expect(subject[:latest_pipeline_path]).to eq(project_pipeline_path(project, pipeline))
end
end
end
def security_scan(type, configured:)
{
"configured" => configured,
"description" => described_class::SCAN_DESCRIPTIONS[type],
"link" => help_page_path(described_class::SCAN_DOCS[type]),
"name" => described_class::SCAN_NAMES[type]
}
end
end
......@@ -1783,6 +1783,15 @@ msgstr ""
msgid "Analytics"
msgstr ""
msgid "Analyze a review version of your web application."
msgstr ""
msgid "Analyze your dependencies for known vulnerabilities"
msgstr ""
msgid "Analyze your source code for known vulnerabilities"
msgstr ""
msgid "Ancestors"
msgstr ""
......@@ -3118,6 +3127,9 @@ msgstr ""
msgid "Check your .gitlab-ci.yml"
msgstr ""
msgid "Check your Docker images for known vulnerabilities"
msgstr ""
msgid "Checking %{text} availability…"
msgstr ""
......@@ -4557,6 +4569,9 @@ msgstr ""
msgid "Container Registry"
msgstr ""
msgid "Container Scanning"
msgstr ""
msgid "Container registry images"
msgstr ""
......@@ -5533,6 +5548,9 @@ msgstr ""
msgid "Dependency Proxy"
msgstr ""
msgid "Dependency Scanning"
msgstr ""
msgid "Dependency proxy"
msgstr ""
......@@ -6076,6 +6094,9 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
msgid "Dynamic Application Security Testing (DAST)"
msgstr ""
msgid "Each Runner can be in one of the following states:"
msgstr ""
......@@ -15050,6 +15071,9 @@ msgstr ""
msgid "Search users or groups"
msgstr ""
msgid "Search your project dependencies for their licenses and apply policies"
msgstr ""
msgid "Search your projects"
msgstr ""
......@@ -15168,6 +15192,9 @@ msgstr ""
msgid "Security & Compliance"
msgstr ""
msgid "Security Configuration"
msgstr ""
msgid "Security Dashboard"
msgstr ""
......@@ -16511,6 +16538,9 @@ msgstr ""
msgid "State your message to activate"
msgstr ""
msgid "Static Application Security Testing (SAST)"
msgstr ""
msgid "Statistics"
msgstr ""
......
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