Commit 7de0f9cc authored by rossfuhrman's avatar rossfuhrman Committed by James Fargher

SAST mutation now supports analyzers section

The SAST Config GraphQL mutation now supports writing the analyzers
section to .gitlab-ci.yml
parent 7a6911ba
...@@ -15400,6 +15400,26 @@ type SastCiConfigurationAnalyzersEntityEdge { ...@@ -15400,6 +15400,26 @@ type SastCiConfigurationAnalyzersEntityEdge {
node: SastCiConfigurationAnalyzersEntity node: SastCiConfigurationAnalyzersEntity
} }
"""
Represents the analyzers entity in SAST CI configuration
"""
input SastCiConfigurationAnalyzersEntityInput {
"""
State of the analyzer
"""
enabled: Boolean!
"""
Name of analyzer
"""
name: String!
"""
List of variables for the analyzer
"""
variables: [SastCiConfigurationEntityInput!]
}
""" """
Represents an entity in SAST CI configuration Represents an entity in SAST CI configuration
""" """
...@@ -15524,6 +15544,11 @@ input SastCiConfigurationEntityInput { ...@@ -15524,6 +15544,11 @@ input SastCiConfigurationEntityInput {
Represents a CI configuration of SAST Represents a CI configuration of SAST
""" """
input SastCiConfigurationInput { input SastCiConfigurationInput {
"""
List of analyzers and related variables for the SAST configuration
"""
analyzers: [SastCiConfigurationAnalyzersEntityInput!]
""" """
List of global entities related to SAST configuration List of global entities related to SAST configuration
""" """
......
...@@ -44826,6 +44826,63 @@ ...@@ -44826,6 +44826,63 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationAnalyzersEntityInput",
"description": "Represents the analyzers entity in SAST CI configuration",
"fields": null,
"inputFields": [
{
"name": "name",
"description": "Name of analyzer",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "enabled",
"description": "State of the analyzer",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "variables",
"description": "List of variables for the analyzer",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationEntityInput",
"ofType": null
}
}
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "SastCiConfigurationEntity", "name": "SastCiConfigurationEntity",
...@@ -45196,6 +45253,24 @@ ...@@ -45196,6 +45253,24 @@
} }
}, },
"defaultValue": null "defaultValue": null
},
{
"name": "analyzers",
"description": "List of analyzers and related variables for the SAST configuration",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationAnalyzersEntityInput",
"ofType": null
}
}
},
"defaultValue": null
} }
], ],
"interfaces": null, "interfaces": null,
# frozen_string_literal: true
module Types
module CiConfiguration
module Sast
# rubocop: disable Graphql/AuthorizeTypes
class AnalyzersEntityInputType < BaseInputObject
graphql_name 'SastCiConfigurationAnalyzersEntityInput'
description 'Represents the analyzers entity in SAST CI configuration'
argument :name, GraphQL::STRING_TYPE, required: true,
description: 'Name of analyzer'
argument :enabled, GraphQL::BOOLEAN_TYPE, required: true,
description: 'State of the analyzer'
argument :variables, [::Types::CiConfiguration::Sast::EntityInputType],
description: 'List of variables for the analyzer',
required: false
end
end
end
end
...@@ -14,6 +14,10 @@ module Types ...@@ -14,6 +14,10 @@ module Types
argument :pipeline, [::Types::CiConfiguration::Sast::EntityInputType], argument :pipeline, [::Types::CiConfiguration::Sast::EntityInputType],
description: 'List of pipeline entities related to SAST configuration', description: 'List of pipeline entities related to SAST configuration',
required: false required: false
argument :analyzers, [::Types::CiConfiguration::Sast::AnalyzersEntityInputType],
description: 'List of analyzers and related variables for the SAST configuration',
required: false
end end
end end
end end
......
---
title: SAST mutation now supports analyzers section
merge_request: 42542
author:
type: changed
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module Security module Security
module CiConfiguration module CiConfiguration
class SastBuildActions class SastBuildActions
SAST_DEFAULT_ANALYZERS = 'bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, sobelow, pmd-apex, kubesec'
def initialize(auto_devops_enabled, params, existing_gitlab_ci_content) def initialize(auto_devops_enabled, params, existing_gitlab_ci_content)
@auto_devops_enabled = auto_devops_enabled @auto_devops_enabled = auto_devops_enabled
@variables = variables(params) @variables = variables(params)
...@@ -35,9 +37,30 @@ module Security ...@@ -35,9 +37,30 @@ module Security
end end
def collect_values(config, key) def collect_values(config, key)
global_variables = config['global']&.collect {|k| [k['field'], k[key]]}.to_h global_variables = config['global']&.to_h { |k| [k['field'], k[key]] } || {}
pipeline_variables = config['pipeline']&.collect {|k| [k['field'], k[key]]}.to_h pipeline_variables = config['pipeline']&.to_h { |k| [k['field'], k[key]] } || {}
global_variables.merge!(pipeline_variables)
analyzer_variables = collect_analyzer_values(config, key)
global_variables.merge!(pipeline_variables).merge!(analyzer_variables)
end
def collect_analyzer_values(config, key)
analyzer_variables = config['analyzers']
&.select {|a| a['enabled'] && a['variables'] }
&.flat_map {|a| a['variables'] }
&.collect {|v| [v['field'], v[key]] }.to_h
analyzer_variables['SAST_DEFAULT_ANALYZERS'] = if key == 'value'
config['analyzers']
&.select {|a| a['enabled'] }
&.collect {|a| a['name'] }
&.join(', ')
else
SAST_DEFAULT_ANALYZERS
end
analyzer_variables
end end
def update_existing_content! def update_existing_content!
...@@ -131,6 +154,11 @@ module Security ...@@ -131,6 +154,11 @@ module Security
SAST_ANALYZER_IMAGE_TAG SAST_ANALYZER_IMAGE_TAG
SAST_EXCLUDED_PATHS SAST_EXCLUDED_PATHS
SEARCH_MAX_DEPTH SEARCH_MAX_DEPTH
SAST_DEFAULT_ANALYZERS
SAST_BRAKEMAN_LEVEL
SAST_BANDIT_EXCLUDED_PATHS
SAST_FLAWFINDER_LEVEL
SAST_GOSEC_LEVEL
) )
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['SastCiConfigurationAnalyzersEntityInput'] do
it { expect(described_class.graphql_name).to eq('SastCiConfigurationAnalyzersEntityInput') }
it { expect(described_class.arguments.keys).to match_array(%w[enabled name variables]) }
end
...@@ -5,5 +5,5 @@ require 'spec_helper' ...@@ -5,5 +5,5 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['SastCiConfigurationInput'] do RSpec.describe GitlabSchema.types['SastCiConfigurationInput'] do
it { expect(described_class.graphql_name).to eq('SastCiConfigurationInput') } it { expect(described_class.graphql_name).to eq('SastCiConfigurationInput') }
it { expect(described_class.arguments.keys).to match_array(%w[global pipeline]) } it { expect(described_class.arguments.keys).to match_array(%w[global pipeline analyzers]) }
end end
...@@ -31,6 +31,50 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do ...@@ -31,6 +31,50 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do
] } ] }
end end
let(:params_with_analyzer_info) do
params.merge( { 'analyzers' =>
[
{
'name' => "bandit",
'enabled' => false
},
{
'name' => "brakeman",
'enabled' => true,
'variables' => [
{ 'field' => "SAST_BRAKEMAN_LEVEL",
'defaultValue' => "1",
'value' => "2" }
]
},
{
'name' => "flawfinder",
'enabled' => true,
'variables' => [
{ 'field' => "SAST_FLAWFINDER_LEVEL",
'defaultValue' => "1",
'value' => "1" }
]
}
] }
)
end
let(:params_with_all_analyzers_enabled) do
params.merge( { 'analyzers' =>
[
{
'name' => "brakeman",
'enabled' => true
},
{
'name' => "flawfinder",
'enabled' => true
}
] }
)
end
context 'with existing .gitlab-ci.yml' do context 'with existing .gitlab-ci.yml' do
let(:auto_devops_enabled) { false } let(:auto_devops_enabled) { false }
...@@ -86,6 +130,28 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do ...@@ -86,6 +130,28 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do
it 'reports defaults have not been overwritten' do it 'reports defaults have not been overwritten' do
expect(result.first[:default_values_overwritten]).to eq(false) expect(result.first[:default_values_overwritten]).to eq(false)
end end
context 'analyzer section' do
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_with_sast_and_default_stage }
subject(:result) { described_class.new(auto_devops_enabled, params_with_analyzer_info, gitlab_ci_content).generate }
it 'generates the correct YML' do
expect(result.first[:content]).to eq(sast_yaml_with_no_variables_set_but_analyzers)
end
context 'all analyzers are enabled' do
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_with_sast_and_default_stage }
subject(:result) { described_class.new(auto_devops_enabled, params_with_all_analyzers_enabled, gitlab_ci_content).generate }
it 'does not write SAST_DEFAULT_ANALYZERS' do
stub_const('Security::CiConfiguration::SastBuildActions::SAST_DEFAULT_ANALYZERS', 'brakeman, flawfinder')
expect(result.first[:content]).to eq(sast_yaml_with_no_variables_set)
end
end
end
end end
context 'with update stage and SEARCH_MAX_DEPTH and set SECURE_ANALYZERS_PREFIX to default' do context 'with update stage and SEARCH_MAX_DEPTH and set SECURE_ANALYZERS_PREFIX to default' do
...@@ -245,6 +311,24 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do ...@@ -245,6 +311,24 @@ RSpec.describe Security::CiConfiguration::SastBuildActions do
auto_devops_template['stages'] auto_devops_template['stages']
end end
def sast_yaml_with_no_variables_set_but_analyzers
<<-CI_YML.strip_heredoc
# You can override the included template(s) by including variable overrides
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
stages:
- test
sast:
variables:
SAST_DEFAULT_ANALYZERS: brakeman, flawfinder
SAST_BRAKEMAN_LEVEL: '2'
stage: test
include:
- template: Security/SAST.gitlab-ci.yml
CI_YML
end
def sast_yaml_with_no_variables_set def sast_yaml_with_no_variables_set
<<-CI_YML.strip_heredoc <<-CI_YML.strip_heredoc
# You can override the included template(s) by including variable overrides # You can override the included template(s) by including variable overrides
......
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