Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
a1b61286
Commit
a1b61286
authored
Aug 19, 2021
by
Saikat Sarkar
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Parse and persist vulnerability flags
parent
2c314d78
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
412 additions
and
34 deletions
+412
-34
ee/app/finders/security/findings_finder.rb
ee/app/finders/security/findings_finder.rb
+4
-6
ee/app/finders/security/pipeline_vulnerabilities_finder.rb
ee/app/finders/security/pipeline_vulnerabilities_finder.rb
+6
-9
ee/app/models/vulnerabilities/flag.rb
ee/app/models/vulnerabilities/flag.rb
+5
-0
ee/app/services/security/store_report_service.rb
ee/app/services/security/store_report_service.rb
+21
-1
ee/spec/factories/ci/job_artifacts.rb
ee/spec/factories/ci/job_artifacts.rb
+10
-0
ee/spec/finders/security/findings_finder_spec.rb
ee/spec/finders/security/findings_finder_spec.rb
+3
-4
ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb
.../finders/security/pipeline_vulnerabilities_finder_spec.rb
+3
-2
ee/spec/fixtures/security_reports/master/gl-sast-report-with-vulnerability-flags.json
...ports/master/gl-sast-report-with-vulnerability-flags.json
+202
-0
ee/spec/graphql/types/pipeline_security_report_finding_type_spec.rb
...aphql/types/pipeline_security_report_finding_type_spec.rb
+3
-4
ee/spec/lib/gitlab/ci/reports/security/finding_spec.rb
ee/spec/lib/gitlab/ci/reports/security/finding_spec.rb
+6
-0
ee/spec/models/vulnerabilities/flag_spec.rb
ee/spec/models/vulnerabilities/flag_spec.rb
+7
-0
ee/spec/requests/api/vulnerability_findings_spec.rb
ee/spec/requests/api/vulnerability_findings_spec.rb
+1
-2
ee/spec/services/security/store_report_service_spec.rb
ee/spec/services/security/store_report_service_spec.rb
+23
-5
lib/gitlab/ci/parsers/security/common.rb
lib/gitlab/ci/parsers/security/common.rb
+14
-0
lib/gitlab/ci/reports/security/finding.rb
lib/gitlab/ci/reports/security/finding.rb
+4
-1
lib/gitlab/ci/reports/security/flag.rb
lib/gitlab/ci/reports/security/flag.rb
+34
-0
spec/factories/ci/reports/security/flags.rb
spec/factories/ci/reports/security/flags.rb
+15
-0
spec/lib/gitlab/ci/parsers/security/common_spec.rb
spec/lib/gitlab/ci/parsers/security/common_spec.rb
+18
-0
spec/lib/gitlab/ci/reports/security/flag_spec.rb
spec/lib/gitlab/ci/reports/security/flag_spec.rb
+33
-0
No files found.
ee/app/finders/security/findings_finder.rb
View file @
a1b61286
...
...
@@ -47,7 +47,7 @@ module Security
report_finding
=
report_finding_for
(
security_finding
)
return
Vulnerabilities
::
Finding
.
new
unless
report_finding
finding_data
=
report_finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
,
:signatures
)
finding_data
=
report_finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
,
:signatures
,
:flags
)
identifiers
=
report_finding
.
identifiers
.
map
do
|
identifier
|
Vulnerabilities
::
Identifier
.
new
(
identifier
.
to_hash
)
end
...
...
@@ -63,7 +63,9 @@ module Security
finding
.
scanner
=
security_finding
.
scanner
if
calculate_false_positive?
finding
.
vulnerability_flags
=
existing_vulnerability_flags
.
fetch
(
security_finding
.
uuid
,
[])
finding
.
vulnerability_flags
=
report_finding
.
flags
.
map
do
|
flag
|
Vulnerabilities
::
Flag
.
new
(
flag
)
end
end
finding
.
identifiers
=
identifiers
...
...
@@ -79,10 +81,6 @@ module Security
existing_vulnerabilities
.
dig
(
security_finding
.
scan
.
scan_type
,
security_finding
.
project_fingerprint
)
&
.
first
end
def
existing_vulnerability_flags
@existing_vulnerability_flags
||=
project
.
vulnerability_flags_for
(
security_findings
.
map
(
&
:uuid
))
end
def
calculate_false_positive?
::
Feature
.
enabled?
(
:vulnerability_flags
,
project
)
&&
project
.
licensed_feature_available?
(
:sast_fp_reduction
)
end
...
...
ee/app/finders/security/pipeline_vulnerabilities_finder.rb
View file @
a1b61286
...
...
@@ -33,8 +33,7 @@ module Security
normalized_findings
=
normalize_report_findings
(
report
.
findings
,
vulnerabilities_by_finding_fingerprint
(
report
),
existing_vulnerability_flags_for
(
report
))
vulnerabilities_by_finding_fingerprint
(
report
))
filtered_findings
=
filter
(
normalized_findings
)
...
...
@@ -76,19 +75,15 @@ module Security
.
select
(
:vulnerability_id
,
:project_fingerprint
)
end
def
existing_vulnerability_flags_for
(
report
)
pipeline
.
project
.
vulnerability_flags_for
(
report
.
findings
.
map
(
&
:uuid
))
end
# This finder is used for fetching vulnerabilities for any pipeline, if we used it to fetch
# vulnerabilities for a non-default-branch, the findings will be unpersisted, so we
# coerce the POROs into unpersisted AR records to give them a common object.
# See https://gitlab.com/gitlab-org/gitlab/issues/33588#note_291849433 for more context
# on why this happens.
def
normalize_report_findings
(
report_findings
,
vulnerabilities
,
vulnerability_flags
)
def
normalize_report_findings
(
report_findings
,
vulnerabilities
)
report_findings
.
map
do
|
report_finding
|
finding_hash
=
report_finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
,
:signatures
)
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
,
:signatures
,
:flags
)
finding
=
Vulnerabilities
::
Finding
.
new
(
finding_hash
)
# assigning Vulnerabilities to Findings to enable the computed state
...
...
@@ -108,7 +103,9 @@ module Security
end
if
calculate_false_positive?
finding
.
vulnerability_flags
=
vulnerability_flags
.
fetch
(
finding
.
uuid
,
[])
finding
.
vulnerability_flags
=
report_finding
.
flags
.
map
do
|
flag
|
Vulnerabilities
::
Flag
.
new
(
flag
)
end
end
finding
...
...
ee/app/models/vulnerabilities/flag.rb
View file @
a1b61286
...
...
@@ -13,5 +13,10 @@ module Vulnerabilities
enum
flag_type:
{
false_positive:
0
}
def
initialize
(
attributes
)
attributes
=
attributes
.
to_hash
if
attributes
.
instance_of?
(
Gitlab
::
Ci
::
Reports
::
Security
::
Flag
)
super
(
attributes
)
end
end
end
ee/app/services/security/store_report_service.rb
View file @
a1b61286
...
...
@@ -59,6 +59,10 @@ module Security
update_vulnerabilities_identifiers
update_vulnerabilities_finding_identifiers
if
::
Feature
.
enabled?
(
:vulnerability_flags
,
project
)
&&
project
.
licensed_feature_available?
(
:sast_fp_reduction
)
create_vulnerability_flags_info
end
vulnerability_ids
end
...
...
@@ -75,7 +79,7 @@ module Security
return
end
vulnerability_params
=
finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:scan
,
:links
,
:signatures
)
vulnerability_params
=
finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:scan
,
:links
,
:signatures
,
:flags
)
entity_params
=
Gitlab
::
Json
.
parse
(
vulnerability_params
&
.
dig
(
:raw_metadata
)).
slice
(
'description'
,
'message'
,
'solution'
,
'cve'
,
'location'
)
# Vulnerabilities::Finding (`vulnerability_occurrences`)
vulnerability_finding
=
vulnerability_findings_by_uuid
[
finding
.
uuid
]
||
...
...
@@ -254,6 +258,22 @@ module Security
rescue
ActiveRecord
::
RecordNotUnique
end
def
create_vulnerability_flags_info
timestamps
=
{
created_at:
Time
.
current
,
updated_at:
Time
.
current
}
vulnerability_finding_to_finding_map
.
each_slice
(
BATCH_SIZE
)
do
|
vf_to_findings
|
records
=
vf_to_findings
.
flat_map
do
|
vulnerability_finding
,
finding
|
finding
.
flags
.
map
{
|
flag
|
timestamps
.
merge
(
**
flag
.
to_hash
,
vulnerability_occurrence_id:
vulnerability_finding
.
id
)
}
end
records
.
uniq!
Vulnerabilities
::
Flag
.
insert_all
(
records
)
if
records
.
present?
end
rescue
StandardError
=>
e
Gitlab
::
ErrorTracking
.
track_exception
(
e
,
project_id:
project
.
id
,
pipeline_id:
pipeline
.
id
)
end
def
update_vulnerability_links_info
timestamps
=
{
created_at:
Time
.
current
,
updated_at:
Time
.
current
}
...
...
ee/spec/factories/ci/job_artifacts.rb
View file @
a1b61286
...
...
@@ -12,6 +12,16 @@ FactoryBot.define do
end
end
trait
:sast_with_vulnerability_flags
do
file_type
{
:sast
}
file_format
{
:raw
}
after
(
:build
)
do
|
artifact
,
_
|
artifact
.
file
=
fixture_file_upload
(
Rails
.
root
.
join
(
'ee/spec/fixtures/security_reports/master/gl-sast-report-with-vulnerability-flags.json'
),
'application/json'
)
end
end
trait
:dast
do
file_format
{
:raw
}
file_type
{
:dast
}
...
...
ee/spec/finders/security/findings_finder_spec.rb
View file @
a1b61286
...
...
@@ -59,8 +59,6 @@ RSpec.describe Security::FindingsFinder do
deduplicated:
true
,
position:
index
,
scan:
scan
)
create
(
:vulnerabilities_finding
,
uuid:
finding
.
uuid
,
project:
pipeline
.
project
)
end
end
...
...
@@ -186,8 +184,9 @@ RSpec.describe Security::FindingsFinder do
context
'with some vulnerability flags present'
do
before
do
create
(
:vulnerabilities_flag
,
finding:
pipeline
.
project
.
vulnerability_findings
.
first
)
create
(
:vulnerabilities_flag
,
finding:
pipeline
.
project
.
vulnerability_findings
.
last
)
allow_next_instance_of
(
Gitlab
::
Ci
::
Reports
::
Security
::
Finding
)
do
|
finding
|
allow
(
finding
).
to
receive
(
:flags
).
and_return
([
create
(
:ci_reports_security_flag
)])
if
finding
.
report_type
==
'sast'
end
end
it
'has some vulnerability_findings with vulnerability flag'
do
...
...
ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb
View file @
a1b61286
...
...
@@ -140,8 +140,9 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
context
"false-positive"
do
before
do
vulnerability_finding
=
create
(
:vulnerabilities_finding
,
uuid:
sast_report_uuids
.
first
,
project:
pipeline
.
project
)
create
(
:vulnerabilities_flag
,
finding:
vulnerability_finding
)
allow_next_instance_of
(
Gitlab
::
Ci
::
Reports
::
Security
::
Finding
)
do
|
finding
|
allow
(
finding
).
to
receive
(
:flags
).
and_return
([
create
(
:ci_reports_security_flag
)])
if
finding
.
report_type
==
'sast'
end
end
it
'includes findings with false-positive'
do
...
...
ee/spec/fixtures/security_reports/master/gl-sast-report-with-vulnerability-flags.json
0 → 100644
View file @
a1b61286
{
"version"
:
"14.0.0"
,
"vulnerabilities"
:
[
{
"category"
:
"sast"
,
"name"
:
"Predictable pseudorandom number generator"
,
"message"
:
"Predictable pseudorandom number generator"
,
"cve"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:47:PREDICTABLE_RANDOM"
,
"severity"
:
"Medium"
,
"confidence"
:
"Medium"
,
"scanner"
:
{
"id"
:
"find_sec_bugs"
,
"name"
:
"Find Security Bugs"
},
"location"
:
{
"file"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy"
,
"start_line"
:
47
,
"end_line"
:
47
,
"class"
:
"com.gitlab.security_products.tests.App"
,
"method"
:
"generateSecretToken2"
},
"flags"
:
[
{
"type"
:
"flagged-as-likely-false-positive"
,
"origin"
:
"vet"
,
"description"
:
"This vulnerability has been identified as a potential false positive by the VET post-analyzer"
},
{
"type"
:
"flagged-as-likely-false-positive"
,
"origin"
:
"post analyzer Y"
,
"description"
:
"integer to sink"
}
],
"identifiers"
:
[
{
"type"
:
"find_sec_bugs_type"
,
"name"
:
"Find Security Bugs-PREDICTABLE_RANDOM"
,
"value"
:
"PREDICTABLE_RANDOM"
,
"url"
:
"https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM"
}
]
},
{
"category"
:
"sast"
,
"name"
:
"Predictable pseudorandom number generator"
,
"message"
:
"Predictable pseudorandom number generator"
,
"cve"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:41:PREDICTABLE_RANDOM"
,
"severity"
:
"Low"
,
"confidence"
:
"Low"
,
"scanner"
:
{
"id"
:
"find_sec_bugs"
,
"name"
:
"Find Security Bugs"
},
"location"
:
{
"file"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy"
,
"start_line"
:
41
,
"end_line"
:
41
,
"class"
:
"com.gitlab.security_products.tests.App"
,
"method"
:
"generateSecretToken1"
},
"identifiers"
:
[
{
"type"
:
"find_sec_bugs_type"
,
"name"
:
"Find Security Bugs-PREDICTABLE_RANDOM"
,
"value"
:
"PREDICTABLE_RANDOM"
,
"url"
:
"https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM"
}
]
},
{
"category"
:
"sast"
,
"name"
:
"ECB mode is insecure"
,
"message"
:
"ECB mode is insecure"
,
"description"
:
"The cipher uses ECB mode, which provides poor confidentiality for encrypted data"
,
"cve"
:
"ea0f905fc76f2739d5f10a1fd1e37a10:ECB_MODE:java-maven/src/main/java/com/gitlab/security_products/tests/App.java:29"
,
"severity"
:
"Medium"
,
"confidence"
:
"High"
,
"scanner"
:
{
"id"
:
"find_sec_bugs"
,
"name"
:
"Find Security Bugs"
},
"location"
:
{
"file"
:
"java-maven/src/main/java/com/gitlab/security_products/tests/App.java"
,
"start_line"
:
29
,
"end_line"
:
29
,
"class"
:
"com.gitlab.security_products.tests.App"
,
"method"
:
"insecureCypher"
},
"identifiers"
:
[
{
"type"
:
"find_sec_bugs_type"
,
"name"
:
"Find Security Bugs-ECB_MODE"
,
"value"
:
"ECB_MODE"
,
"url"
:
"https://find-sec-bugs.github.io/bugs.htm#ECB_MODE"
},
{
"type"
:
"cwe"
,
"name"
:
"CWE-327"
,
"value"
:
"327"
,
"url"
:
"https://cwe.mitre.org/data/definitions/327.html"
}
]
},
{
"category"
:
"sast"
,
"name"
:
"Hard coded key"
,
"message"
:
"Hard coded key"
,
"description"
:
"Hard coded cryptographic key found"
,
"cve"
:
"102ac67e0975ecec02a056008e0faad8:HARD_CODE_KEY:scala-sbt/src/main/scala/example/Main.scala:12"
,
"severity"
:
"Medium"
,
"confidence"
:
"High"
,
"scanner"
:
{
"id"
:
"find_sec_bugs"
,
"name"
:
"Find Security Bugs"
},
"location"
:
{
"file"
:
"scala-sbt/src/main/scala/example/Main.scala"
,
"start_line"
:
12
,
"end_line"
:
12
,
"class"
:
"example.Main$"
,
"method"
:
"getBytes"
},
"identifiers"
:
[
{
"type"
:
"find_sec_bugs_type"
,
"name"
:
"Find Security Bugs-HARD_CODE_KEY"
,
"value"
:
"HARD_CODE_KEY"
,
"url"
:
"https://find-sec-bugs.github.io/bugs.htm#HARD_CODE_KEY"
},
{
"type"
:
"cwe"
,
"name"
:
"CWE-321"
,
"value"
:
"321"
,
"url"
:
"https://cwe.mitre.org/data/definitions/321.html"
}
]
},
{
"category"
:
"sast"
,
"name"
:
"Cipher with no integrity"
,
"message"
:
"Cipher with no integrity"
,
"cve"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:CIPHER_INTEGRITY"
,
"severity"
:
"Medium"
,
"confidence"
:
"High"
,
"scanner"
:
{
"id"
:
"find_sec_bugs"
,
"name"
:
"Find Security Bugs"
},
"location"
:
{
"file"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy"
,
"start_line"
:
29
,
"end_line"
:
29
,
"class"
:
"com.gitlab.security_products.tests.App"
,
"method"
:
"insecureCypher"
},
"identifiers"
:
[
{
"type"
:
"find_sec_bugs_type"
,
"name"
:
"Find Security Bugs-CIPHER_INTEGRITY"
,
"value"
:
"CIPHER_INTEGRITY"
,
"url"
:
"https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
}
],
"tracking"
:
{
"type"
:
"source"
,
"items"
:
[
{
"file"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy"
,
"start_line"
:
29
,
"end_line"
:
29
,
"signatures"
:
[
{
"algorithm"
:
"hash"
,
"value"
:
"HASHVALUE"
},
{
"algorithm"
:
"scope_offset"
,
"value"
:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:App[0]:insecureCypher[0]:2"
}
]
}
]
}
}
],
"remediations"
:
[],
"scan"
:
{
"scanner"
:
{
"id"
:
"find_sec_bugs"
,
"name"
:
"Find Security Bugs"
,
"url"
:
"https://spotbugs.github.io"
,
"vendor"
:
{
"name"
:
"GitLab"
},
"version"
:
"4.0.2"
},
"type"
:
"sast"
,
"status"
:
"success"
,
"start_time"
:
"placeholder-value"
,
"end_time"
:
"placeholder-value"
}
}
ee/spec/graphql/types/pipeline_security_report_finding_type_spec.rb
View file @
a1b61286
...
...
@@ -55,16 +55,15 @@ RSpec.describe GitlabSchema.types['PipelineSecurityReportFinding'] do
context
'when the vulnerability has a false-positive flag'
do
before
do
security_finding
=
pipeline
.
security_reports
.
reports
[
'sast'
].
findings
.
first
vulnerability_finding
=
create
(
:vulnerabilities_finding
,
uuid:
security_finding
.
uuid
,
pipelines:
[
pipeline
],
project:
pipeline
.
project
)
create
(
:vulnerabilities_flag
,
finding:
vulnerability_finding
)
allow_next_instance_of
(
Gitlab
::
Ci
::
Reports
::
Security
::
Finding
)
do
|
finding
|
allow
(
finding
).
to
receive
(
:flags
).
and_return
([
create
(
:ci_reports_security_flag
)])
if
finding
.
report_type
==
'sast'
end
end
it
'returns false-positive value'
do
vulnerabilities
=
subject
.
dig
(
'data'
,
'project'
,
'pipeline'
,
'securityReportFindings'
,
'nodes'
)
expect
(
vulnerabilities
.
first
[
'falsePositive'
]).
to
be
(
true
)
expect
(
vulnerabilities
.
last
[
'falsePositive'
]).
to
be
(
false
)
end
end
...
...
ee/spec/lib/gitlab/ci/reports/security/finding_spec.rb
View file @
a1b61286
...
...
@@ -15,12 +15,16 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
let_it_be
(
:location
)
{
build
(
:ci_reports_security_locations_sast
)
}
let_it_be
(
:remediation
)
{
build
(
:ci_reports_security_remediation
)
}
let
(
:flag_1
)
{
build
(
:ci_reports_security_flag
)
}
let
(
:flag_2
)
{
build
(
:ci_reports_security_flag
)
}
let
(
:params
)
do
{
compare_key:
'this_is_supposed_to_be_a_unique_value'
,
confidence: :medium
,
identifiers:
[
primary_identifier
,
other_identifier
],
links:
[
link
],
flags:
[
flag_1
,
flag_2
],
remediations:
[
remediation
],
location:
location
,
metadata_version:
'sast:1.0'
,
...
...
@@ -62,6 +66,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
project_fingerprint:
'9a73f32d58d87d94e3dc61c4c1a94803f6014258'
,
identifiers:
[
primary_identifier
,
other_identifier
],
links:
[
link
],
flags:
[
flag_1
,
flag_2
],
remediations:
[
remediation
],
location:
location
,
metadata_version:
'sast:1.0'
,
...
...
@@ -127,6 +132,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
confidence:
occurrence
.
confidence
,
identifiers:
occurrence
.
identifiers
,
links:
occurrence
.
links
,
flags:
occurrence
.
flags
,
location:
occurrence
.
location
,
metadata_version:
occurrence
.
metadata_version
,
name:
occurrence
.
name
,
...
...
ee/spec/models/vulnerabilities/flag_spec.rb
View file @
a1b61286
...
...
@@ -16,4 +16,11 @@ RSpec.describe Vulnerabilities::Flag do
it
{
is_expected
.
to
validate_uniqueness_of
(
:flag_type
).
scoped_to
(
:vulnerability_occurrence_id
,
:origin
).
ignoring_case_sensitivity
}
it
{
is_expected
.
to
define_enum_for
(
:flag_type
).
with_values
(
false_positive:
0
)
}
end
describe
'#initialize'
do
it
'creates a valid flag with flag_type attribute'
do
flag
=
described_class
.
new
(
flag_type:
Vulnerabilities
::
Flag
.
flag_types
[
:false_positive
],
origin:
'post analyzer X'
,
description:
'static string to sink'
,
finding:
build
(
:vulnerabilities_finding
))
expect
(
flag
).
to
be_valid
end
end
end
ee/spec/requests/api/vulnerability_findings_spec.rb
View file @
a1b61286
...
...
@@ -69,8 +69,7 @@ RSpec.describe API::VulnerabilityFindings do
# Threshold is required for the extra query performed in Security::PipelineVulnerabilitiesFinder to load
# the Vulnerabilities providing computed states for the associated Vulnerability::Findings
# and all associated vulnerability_flags
expect
{
get
api
(
project_vulnerability_findings_path
,
user
)
}.
not_to
exceed_query_limit
(
control_count
).
with_threshold
(
3
)
expect
{
get
api
(
project_vulnerability_findings_path
,
user
)
}.
not_to
exceed_query_limit
(
control_count
).
with_threshold
(
1
)
end
describe
'using different finders'
do
...
...
ee/spec/services/security/store_report_service_spec.rb
View file @
a1b61286
...
...
@@ -26,6 +26,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
dependency_scanning:
true
,
container_scanning:
true
,
security_dashboard:
true
,
sast_fp_reduction:
true
,
vulnerability_finding_signatures:
vulnerability_finding_signatures
)
allow
(
Security
::
AutoFixWorker
).
to
receive
(
:perform_async
)
...
...
@@ -43,11 +44,12 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
context
'for different security reports'
do
where
(
:case_name
,
:trait
,
:scanners
,
:identifiers
,
:findings
,
:finding_identifiers
,
:finding_pipelines
,
:remediations
,
:signatures
,
:finding_links
)
do
'with SAST report'
|
:sast
|
1
|
6
|
5
|
7
|
5
|
0
|
2
|
0
'with exceeding identifiers'
|
:with_exceeding_identifiers
|
1
|
20
|
1
|
20
|
1
|
0
|
0
|
0
'with Dependency Scanning report'
|
:dependency_scanning_remediation
|
1
|
3
|
2
|
3
|
2
|
1
|
0
|
6
'with Container Scanning report'
|
:container_scanning
|
1
|
8
|
8
|
8
|
8
|
0
|
0
|
8
where
(
:case_name
,
:trait
,
:scanners
,
:identifiers
,
:findings
,
:finding_identifiers
,
:finding_pipelines
,
:remediations
,
:signatures
,
:finding_links
,
:finding_flags
)
do
'with SAST report'
|
:sast
|
1
|
6
|
5
|
7
|
5
|
0
|
2
|
0
|
0
'with exceeding identifiers'
|
:with_exceeding_identifiers
|
1
|
20
|
1
|
20
|
1
|
0
|
0
|
0
|
0
'with Dependency Scanning report'
|
:dependency_scanning_remediation
|
1
|
3
|
2
|
3
|
2
|
1
|
0
|
6
|
0
'with Container Scanning report'
|
:container_scanning
|
1
|
8
|
8
|
8
|
8
|
0
|
0
|
8
|
0
'with vulnerability flags'
|
:sast_with_vulnerability_flags
|
1
|
6
|
5
|
7
|
5
|
0
|
2
|
0
|
2
end
with_them
do
...
...
@@ -63,6 +65,22 @@ RSpec.describe Security::StoreReportService, '#execute' do
expect
{
subject
}.
to
change
{
Vulnerabilities
::
Finding
.
count
}.
by
(
findings
)
end
context
'vulnerability flags'
do
it
'inserts all finding flags'
do
expect
{
subject
}.
to
change
(
Vulnerabilities
::
Flag
,
:count
).
by
(
finding_flags
)
end
context
'with vulnerability_flags disabled'
do
before
do
stub_feature_flags
(
vulnerability_flags:
false
)
end
it
'does not insert any vulnerability flag'
do
expect
{
subject
}.
not_to
change
(
Vulnerabilities
::
Flag
,
:count
)
end
end
end
it
'inserts all finding links'
do
expect
{
subject
}.
to
change
{
Vulnerabilities
::
FindingLink
.
count
}.
by
(
finding_links
)
end
...
...
lib/gitlab/ci/parsers/security/common.rb
View file @
a1b61286
...
...
@@ -86,6 +86,7 @@ module Gitlab
def
create_finding
(
data
,
remediations
=
[])
identifiers
=
create_identifiers
(
data
[
'identifiers'
])
flags
=
create_flags
(
data
[
'flags'
])
links
=
create_links
(
data
[
'links'
])
location
=
create_location
(
data
[
'location'
]
||
{})
signatures
=
create_signatures
(
tracking_data
(
data
))
...
...
@@ -111,6 +112,7 @@ module Gitlab
scanner:
create_scanner
(
data
[
'scanner'
]),
scan:
report
&
.
scan
,
identifiers:
identifiers
,
flags:
flags
,
links:
links
,
remediations:
remediations
,
raw_metadata:
data
.
to_json
,
...
...
@@ -205,6 +207,18 @@ module Gitlab
url:
identifier
[
'url'
]))
end
def
create_flags
(
flags
)
return
[]
unless
flags
.
is_a?
(
Array
)
flags
.
map
{
|
flag
|
create_flag
(
flag
)
}.
compact
end
def
create_flag
(
flag
)
return
unless
flag
.
is_a?
(
Hash
)
::
Gitlab
::
Ci
::
Reports
::
Security
::
Flag
.
new
(
type:
flag
[
'type'
],
origin:
flag
[
'origin'
],
description:
flag
[
'description'
])
end
def
create_links
(
links
)
return
[]
unless
links
.
is_a?
(
Array
)
...
...
lib/gitlab/ci/reports/security/finding.rb
View file @
a1b61286
...
...
@@ -10,6 +10,7 @@ module Gitlab
attr_reader
:compare_key
attr_reader
:confidence
attr_reader
:identifiers
attr_reader
:flags
attr_reader
:links
attr_reader
:location
attr_reader
:metadata_version
...
...
@@ -30,10 +31,11 @@ module Gitlab
delegate
:file_path
,
:start_line
,
:end_line
,
to: :location
def
initialize
(
compare_key
:,
identifiers
:,
links:
[],
remediations:
[],
location
:,
metadata_version
:,
name
:,
raw_metadata
:,
report_type
:,
scanner
:,
scan
:,
uuid
:,
confidence:
nil
,
severity:
nil
,
details:
{},
signatures:
[],
project_id:
nil
,
vulnerability_finding_signatures_enabled:
false
)
# rubocop:disable Metrics/ParameterLists
def
initialize
(
compare_key
:,
identifiers
:,
flags:
[],
links:
[],
remediations:
[],
location
:,
metadata_version
:,
name
:,
raw_metadata
:,
report_type
:,
scanner
:,
scan
:,
uuid
:,
confidence:
nil
,
severity:
nil
,
details:
{},
signatures:
[],
project_id:
nil
,
vulnerability_finding_signatures_enabled:
false
)
# rubocop:disable Metrics/ParameterLists
@compare_key
=
compare_key
@confidence
=
confidence
@identifiers
=
identifiers
@flags
=
flags
@links
=
links
@location
=
location
@metadata_version
=
metadata_version
...
...
@@ -58,6 +60,7 @@ module Gitlab
compare_key
confidence
identifiers
flags
links
location
metadata_version
...
...
lib/gitlab/ci/reports/security/flag.rb
0 → 100644
View file @
a1b61286
# frozen_string_literal: true
module
Gitlab
module
Ci
module
Reports
module
Security
class
Flag
attr_reader
:type
,
:origin
,
:description
MAP
=
{
'flagged-as-likely-false-positive'
=>
:false_positive
}.
freeze
DEFAULT_FLAG_TYPE
=
:false_positive
def
flag_type
MAP
.
fetch
(
type
,
DEFAULT_FLAG_TYPE
)
end
def
initialize
(
type:
nil
,
origin:
nil
,
description:
nil
)
@type
=
type
@origin
=
origin
@description
=
description
end
def
to_hash
{
flag_type:
flag_type
,
origin:
origin
,
description:
description
}.
compact
end
end
end
end
end
end
spec/factories/ci/reports/security/flags.rb
0 → 100644
View file @
a1b61286
# frozen_string_literal: true
FactoryBot
.
define
do
factory
:ci_reports_security_flag
,
class:
'::Gitlab::Ci::Reports::Security::Flag'
do
type
{
'flagged-as-likely-false-positive'
}
origin
{
'post analyzer X'
}
description
{
'static string to sink'
}
skip_create
initialize_with
do
::
Gitlab
::
Ci
::
Reports
::
Security
::
Flag
.
new
(
**
attributes
)
end
end
end
spec/lib/gitlab/ci/parsers/security/common_spec.rb
View file @
a1b61286
...
...
@@ -13,11 +13,18 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
# The path 'yarn.lock' was initially used by DependencyScanning, it is okay for SAST locations to use it, but this could be made better
let
(
:location
)
{
::
Gitlab
::
Ci
::
Reports
::
Security
::
Locations
::
Sast
.
new
(
file_path:
'yarn.lock'
,
start_line:
1
,
end_line:
1
)
}
let
(
:tracking_data
)
{
nil
}
let
(
:vulnerability_flags_data
)
do
[
::
Gitlab
::
Ci
::
Reports
::
Security
::
Flag
.
new
(
type:
'flagged-as-likely-false-positive'
,
origin:
'post analyzer X'
,
description:
'static string to sink'
),
::
Gitlab
::
Ci
::
Reports
::
Security
::
Flag
.
new
(
type:
'flagged-as-likely-false-positive'
,
origin:
'post analyzer Y'
,
description:
'integer to sink'
)
]
end
before
do
allow_next_instance_of
(
described_class
)
do
|
parser
|
allow
(
parser
).
to
receive
(
:create_location
).
and_return
(
location
)
allow
(
parser
).
to
receive
(
:tracking_data
).
and_return
(
tracking_data
)
allow
(
parser
).
to
receive
(
:create_flags
).
and_return
(
vulnerability_flags_data
)
end
artifact
.
each_blob
{
|
blob
|
described_class
.
parse!
(
blob
,
report
,
vulnerability_finding_signatures_enabled
)
}
...
...
@@ -231,6 +238,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
end
end
describe
'parsing flags'
do
it
'returns flags object for each finding'
do
flags
=
report
.
findings
.
first
.
flags
expect
(
flags
).
to
contain_exactly
(
have_attributes
(
type:
'flagged-as-likely-false-positive'
,
origin:
'post analyzer X'
,
description:
'static string to sink'
),
have_attributes
(
type:
'flagged-as-likely-false-positive'
,
origin:
'post analyzer Y'
,
description:
'integer to sink'
)
)
end
end
describe
'parsing links'
do
it
'returns links object for each finding'
,
:aggregate_failures
do
links
=
report
.
findings
.
flat_map
(
&
:links
)
...
...
spec/lib/gitlab/ci/reports/security/flag_spec.rb
0 → 100644
View file @
a1b61286
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
Ci
::
Reports
::
Security
::
Flag
do
subject
(
:security_flag
)
{
described_class
.
new
(
type:
'flagged-as-likely-false-positive'
,
origin:
'post analyzer X'
,
description:
'static string to sink'
)
}
describe
'#initialize'
do
context
'when all params are given'
do
it
'initializes an instance'
do
expect
{
subject
}.
not_to
raise_error
expect
(
subject
).
to
have_attributes
(
type:
'flagged-as-likely-false-positive'
,
origin:
'post analyzer X'
,
description:
'static string to sink'
)
end
end
describe
'#to_hash'
do
it
'returns expected hash'
do
expect
(
security_flag
.
to_hash
).
to
eq
(
{
flag_type: :false_positive
,
origin:
'post analyzer X'
,
description:
'static string to sink'
}
)
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment