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
56fcaba4
Commit
56fcaba4
authored
Apr 15, 2021
by
James Johnson
Committed by
Dmitry Gruzd
Apr 15, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve Vulnerability Tracking: Use Signatures [RUN ALL RSPEC] [RUN AS-IF-FOSS]
parent
d61c5521
Changes
34
Show whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
2275 additions
and
1688 deletions
+2275
-1688
app/models/concerns/vulnerability_finding_helpers.rb
app/models/concerns/vulnerability_finding_helpers.rb
+7
-0
app/models/concerns/vulnerability_finding_signature_helpers.rb
...odels/concerns/vulnerability_finding_signature_helpers.rb
+7
-0
ee/app/finders/security/findings_finder.rb
ee/app/finders/security/findings_finder.rb
+5
-1
ee/app/finders/security/pipeline_vulnerabilities_finder.rb
ee/app/finders/security/pipeline_vulnerabilities_finder.rb
+27
-6
ee/app/models/concerns/ee/vulnerability_finding_helpers.rb
ee/app/models/concerns/ee/vulnerability_finding_helpers.rb
+39
-0
ee/app/models/concerns/ee/vulnerability_finding_signature_helpers.rb
...ls/concerns/ee/vulnerability_finding_signature_helpers.rb
+30
-0
ee/app/models/license.rb
ee/app/models/license.rb
+1
-0
ee/app/models/vulnerabilities/finding.rb
ee/app/models/vulnerabilities/finding.rb
+11
-6
ee/app/models/vulnerabilities/finding_signature.rb
ee/app/models/vulnerabilities/finding_signature.rb
+12
-0
ee/app/services/security/store_report_service.rb
ee/app/services/security/store_report_service.rb
+76
-6
ee/config/feature_flags/development/vulnerability_finding_signatures.yml
...re_flags/development/vulnerability_finding_signatures.yml
+8
-0
ee/lib/gitlab/ci/parsers/security/common.rb
ee/lib/gitlab/ci/parsers/security/common.rb
+34
-19
ee/lib/gitlab/ci/reports/security/finding.rb
ee/lib/gitlab/ci/reports/security/finding.rb
+21
-6
ee/lib/gitlab/ci/reports/security/finding_signature.rb
ee/lib/gitlab/ci/reports/security/finding_signature.rb
+18
-1
ee/lib/gitlab/ci/reports/security/locations/container_scanning.rb
...itlab/ci/reports/security/locations/container_scanning.rb
+2
-2
ee/lib/gitlab/ci/reports/security/locations/coverage_fuzzing.rb
.../gitlab/ci/reports/security/locations/coverage_fuzzing.rb
+0
-2
ee/lib/gitlab/ci/reports/security/locations/dast.rb
ee/lib/gitlab/ci/reports/security/locations/dast.rb
+0
-2
ee/lib/gitlab/ci/reports/security/locations/dependency_scanning.rb
...tlab/ci/reports/security/locations/dependency_scanning.rb
+0
-2
ee/lib/gitlab/ci/reports/security/locations/sast.rb
ee/lib/gitlab/ci/reports/security/locations/sast.rb
+0
-2
ee/lib/gitlab/ci/reports/security/locations/secret_detection.rb
.../gitlab/ci/reports/security/locations/secret_detection.rb
+0
-2
ee/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb
...lab/ci/reports/security/vulnerability_reports_comparer.rb
+27
-9
ee/spec/factories/ci/reports/security/findings.rb
ee/spec/factories/ci/reports/security/findings.rb
+1
-0
ee/spec/factories/vulnerabilities/feedback.rb
ee/spec/factories/vulnerabilities/feedback.rb
+1
-0
ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb
.../finders/security/pipeline_vulnerabilities_finder_spec.rb
+102
-45
ee/spec/lib/gitlab/ci/parsers/security/common_spec.rb
ee/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+192
-159
ee/spec/lib/gitlab/ci/reports/security/finding_signature_spec.rb
.../lib/gitlab/ci/reports/security/finding_signature_spec.rb
+14
-8
ee/spec/lib/gitlab/ci/reports/security/finding_spec.rb
ee/spec/lib/gitlab/ci/reports/security/finding_spec.rb
+122
-59
ee/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
...i/reports/security/vulnerability_reports_comparer_spec.rb
+85
-80
ee/spec/models/concerns/vulnerability_finding_signature_helpers_spec.rb
.../concerns/vulnerability_finding_signature_helpers_spec.rb
+32
-0
ee/spec/models/vulnerabilities/finding_spec.rb
ee/spec/models/vulnerabilities/finding_spec.rb
+853
-739
ee/spec/requests/api/vulnerability_findings_spec.rb
ee/spec/requests/api/vulnerability_findings_spec.rb
+2
-1
ee/spec/serializers/vulnerabilities/feedback_entity_spec.rb
ee/spec/serializers/vulnerabilities/feedback_entity_spec.rb
+1
-1
ee/spec/services/ci/compare_security_reports_service_spec.rb
ee/spec/services/ci/compare_security_reports_service_spec.rb
+212
-205
ee/spec/services/security/store_report_service_spec.rb
ee/spec/services/security/store_report_service_spec.rb
+333
-325
No files found.
app/models/concerns/vulnerability_finding_helpers.rb
0 → 100644
View file @
56fcaba4
# frozen_string_literal: true
module
VulnerabilityFindingHelpers
extend
ActiveSupport
::
Concern
end
VulnerabilityFindingHelpers
.
prepend_if_ee
(
'EE::VulnerabilityFindingHelpers'
)
app/models/concerns/vulnerability_finding_signature_helpers.rb
0 → 100644
View file @
56fcaba4
# frozen_string_literal: true
module
VulnerabilityFindingSignatureHelpers
extend
ActiveSupport
::
Concern
end
VulnerabilityFindingSignatureHelpers
.
prepend_if_ee
(
'EE::VulnerabilityFindingSignatureHelpers'
)
ee/app/finders/security/findings_finder.rb
View file @
56fcaba4
...
@@ -47,10 +47,13 @@ module Security
...
@@ -47,10 +47,13 @@ module Security
report_finding
=
report_finding_for
(
security_finding
)
report_finding
=
report_finding_for
(
security_finding
)
return
Vulnerabilities
::
Finding
.
new
unless
report_finding
return
Vulnerabilities
::
Finding
.
new
unless
report_finding
finding_data
=
report_finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
)
finding_data
=
report_finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
,
:signatures
)
identifiers
=
report_finding
.
identifiers
.
map
do
|
identifier
|
identifiers
=
report_finding
.
identifiers
.
map
do
|
identifier
|
Vulnerabilities
::
Identifier
.
new
(
identifier
.
to_hash
)
Vulnerabilities
::
Identifier
.
new
(
identifier
.
to_hash
)
end
end
signatures
=
report_finding
.
signatures
.
map
do
|
signature
|
Vulnerabilities
::
FindingSignature
.
new
(
signature
.
to_hash
)
end
Vulnerabilities
::
Finding
.
new
(
finding_data
).
tap
do
|
finding
|
Vulnerabilities
::
Finding
.
new
(
finding_data
).
tap
do
|
finding
|
finding
.
location_fingerprint
=
report_finding
.
location
.
fingerprint
finding
.
location_fingerprint
=
report_finding
.
location
.
fingerprint
...
@@ -59,6 +62,7 @@ module Security
...
@@ -59,6 +62,7 @@ module Security
finding
.
sha
=
pipeline
.
sha
finding
.
sha
=
pipeline
.
sha
finding
.
scanner
=
security_finding
.
scanner
finding
.
scanner
=
security_finding
.
scanner
finding
.
identifiers
=
identifiers
finding
.
identifiers
=
identifiers
finding
.
signatures
=
signatures
end
end
end
end
...
...
ee/app/finders/security/pipeline_vulnerabilities_finder.rb
View file @
56fcaba4
...
@@ -75,7 +75,7 @@ module Security
...
@@ -75,7 +75,7 @@ module Security
def
normalize_report_findings
(
report_findings
,
vulnerabilities
)
def
normalize_report_findings
(
report_findings
,
vulnerabilities
)
report_findings
.
map
do
|
report_finding
|
report_findings
.
map
do
|
report_finding
|
finding_hash
=
report_finding
.
to_hash
finding_hash
=
report_finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
)
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:links
,
:signatures
)
finding
=
Vulnerabilities
::
Finding
.
new
(
finding_hash
)
finding
=
Vulnerabilities
::
Finding
.
new
(
finding_hash
)
# assigning Vulnerabilities to Findings to enable the computed state
# assigning Vulnerabilities to Findings to enable the computed state
...
@@ -90,6 +90,9 @@ module Security
...
@@ -90,6 +90,9 @@ module Security
finding
.
identifiers
=
report_finding
.
identifiers
.
map
do
|
identifier
|
finding
.
identifiers
=
report_finding
.
identifiers
.
map
do
|
identifier
|
Vulnerabilities
::
Identifier
.
new
(
identifier
.
to_hash
)
Vulnerabilities
::
Identifier
.
new
(
identifier
.
to_hash
)
end
end
finding
.
signatures
=
report_finding
.
signatures
.
map
do
|
signature
|
Vulnerabilities
::
FindingSignature
.
new
(
signature
.
to_hash
)
end
finding
finding
end
end
...
@@ -111,18 +114,36 @@ module Security
...
@@ -111,18 +114,36 @@ module Security
end
end
def
dismissal_feedback?
(
finding
)
def
dismissal_feedback?
(
finding
)
dismissal_feedback_by_fingerprint
[
finding
.
project_fingerprint
]
if
::
Feature
.
enabled?
(
:vulnerability_finding_signatures
,
pipeline
.
project
)
&&
!
finding
.
signatures
.
empty?
dismissal_feedback_by_finding_signatures
(
finding
)
else
dismissal_feedback_by_project_fingerprint
(
finding
)
end
end
end
def
dismissal_feedback_by_fingerprint
def
all_dismissal_feedbacks
strong_memoize
(
:
dismissal_feedback_by_fingerprint
)
do
strong_memoize
(
:
all_dismissal_feedbacks
)
do
pipeline
.
project
pipeline
.
project
.
vulnerability_feedback
.
vulnerability_feedback
.
for_dismissal
.
for_dismissal
.
group_by
(
&
:project_fingerprint
)
end
end
end
end
def
dismissal_feedback_by_finding_signatures
(
finding
)
potential_uuids
=
Set
.
new
([
*
finding
.
signature_uuids
,
finding
.
uuid
].
compact
)
all_dismissal_feedbacks
.
any?
{
|
dismissal
|
potential_uuids
.
include?
(
dismissal
.
finding_uuid
)
}
end
def
dismissal_feedback_by_fingerprint
strong_memoize
(
:dismissal_feedback_by_fingerprint
)
do
all_dismissal_feedbacks
.
group_by
(
&
:project_fingerprint
)
end
end
def
dismissal_feedback_by_project_fingerprint
(
finding
)
dismissal_feedback_by_fingerprint
[
finding
.
project_fingerprint
]
end
def
confidence_levels
def
confidence_levels
Array
(
params
.
fetch
(
:confidence
,
Vulnerabilities
::
Finding
.
confidences
.
keys
))
Array
(
params
.
fetch
(
:confidence
,
Vulnerabilities
::
Finding
.
confidences
.
keys
))
end
end
...
...
ee/app/models/concerns/ee/vulnerability_finding_helpers.rb
0 → 100644
View file @
56fcaba4
# frozen_string_literal: true
module
EE
module
VulnerabilityFindingHelpers
extend
ActiveSupport
::
Concern
def
matches_signatures
(
other_signatures
,
other_uuid
)
other_signature_types
=
other_signatures
.
index_by
(
&
:algorithm_type
)
# highest first
match_result
=
nil
signatures
.
sort_by
(
&
:priority
).
reverse_each
do
|
signature
|
matching_other_signature
=
other_signature_types
[
signature
.
algorithm_type
]
next
if
matching_other_signature
.
nil?
match_result
=
matching_other_signature
==
signature
break
end
if
match_result
.
nil?
[
uuid
,
*
signature_uuids
].
include?
(
other_uuid
)
else
match_result
end
end
def
signature_uuids
signatures
.
map
do
|
signature
|
hex_sha
=
signature
.
signature_hex
::
Security
::
VulnerabilityUUID
.
generate
(
report_type:
report_type
,
location_fingerprint:
hex_sha
,
primary_identifier_fingerprint:
primary_identifier
&
.
fingerprint
,
project_id:
project_id
)
end
end
end
end
ee/app/models/concerns/ee/vulnerability_finding_signature_helpers.rb
0 → 100644
View file @
56fcaba4
# frozen_string_literal: true
module
EE
module
VulnerabilityFindingSignatureHelpers
extend
ActiveSupport
::
Concern
# If the location object describes a physical location within a file
# (filename + line numbers), the 'location' algorithm_type should be used
#
# If the location object describes arbitrary data, then the 'hash'
# algorithm_type should be used.
PRIORITIES
=
{
scope_offset:
3
,
location:
2
,
hash:
1
}.
with_indifferent_access
.
freeze
class_methods
do
def
priority
(
algorithm_type
)
raise
ArgumentError
.
new
(
"No priority for
#{
algorithm_type
.
inspect
}
"
)
unless
PRIORITIES
.
key?
(
algorithm_type
)
PRIORITIES
[
algorithm_type
]
end
end
def
priority
self
.
class
.
priority
(
algorithm_type
)
end
end
end
ee/app/models/license.rb
View file @
56fcaba4
...
@@ -180,6 +180,7 @@ class License < ApplicationRecord
...
@@ -180,6 +180,7 @@ class License < ApplicationRecord
subepics
subepics
threat_monitoring
threat_monitoring
vulnerability_auto_fix
vulnerability_auto_fix
vulnerability_finding_signatures
]
]
EEU_FEATURES
.
freeze
EEU_FEATURES
.
freeze
...
...
ee/app/models/vulnerabilities/finding.rb
View file @
56fcaba4
...
@@ -5,6 +5,7 @@ module Vulnerabilities
...
@@ -5,6 +5,7 @@ module Vulnerabilities
include
ShaAttribute
include
ShaAttribute
include
::
Gitlab
::
Utils
::
StrongMemoize
include
::
Gitlab
::
Utils
::
StrongMemoize
include
Presentable
include
Presentable
include
::
VulnerabilityFindingHelpers
# https://gitlab.com/groups/gitlab-org/-/epics/3148
# https://gitlab.com/groups/gitlab-org/-/epics/3148
# https://gitlab.com/gitlab-org/gitlab/-/issues/214563#note_370782508 is why the table names are not renamed
# https://gitlab.com/gitlab-org/gitlab/-/issues/214563#note_370782508 is why the table names are not renamed
...
@@ -332,12 +333,16 @@ module Vulnerabilities
...
@@ -332,12 +333,16 @@ module Vulnerabilities
end
end
end
end
alias_method
:==
,
:eql?
# eql? is necessary in some cases like array intersection
alias_method
:==
,
:eql?
def
eql?
(
other
)
def
eql?
(
other
)
other
.
report_type
==
report_type
&&
return
false
unless
other
.
report_type
==
report_type
&&
other
.
primary_identifier_fingerprint
==
primary_identifier_fingerprint
other
.
location_fingerprint
==
location_fingerprint
&&
other
.
first_fingerprint
==
first_fingerprint
if
::
Feature
.
enabled?
(
:vulnerability_finding_signatures
,
project
)
matches_signatures
(
other
.
signatures
,
other
.
uuid
)
else
other
.
location_fingerprint
==
location_fingerprint
end
end
end
# Array.difference (-) method uses hash and eql? methods to do comparison
# Array.difference (-) method uses hash and eql? methods to do comparison
...
@@ -348,7 +353,7 @@ module Vulnerabilities
...
@@ -348,7 +353,7 @@ module Vulnerabilities
# when Finding is persisted and identifiers are not preloaded.
# when Finding is persisted and identifiers are not preloaded.
return
super
if
persisted?
&&
!
identifiers
.
loaded?
return
super
if
persisted?
&&
!
identifiers
.
loaded?
report_type
.
hash
^
location_fingerprint
.
hash
^
first
_fingerprint
.
hash
report_type
.
hash
^
location_fingerprint
.
hash
^
primary_identifier
_fingerprint
.
hash
end
end
def
severity_value
def
severity_value
...
@@ -380,7 +385,7 @@ module Vulnerabilities
...
@@ -380,7 +385,7 @@ module Vulnerabilities
protected
protected
def
first
_fingerprint
def
primary_identifier
_fingerprint
identifiers
.
first
&
.
fingerprint
identifiers
.
first
&
.
fingerprint
end
end
...
...
ee/app/models/vulnerabilities/finding_signature.rb
View file @
56fcaba4
...
@@ -5,11 +5,23 @@ module Vulnerabilities
...
@@ -5,11 +5,23 @@ module Vulnerabilities
self
.
table_name
=
'vulnerability_finding_signatures'
self
.
table_name
=
'vulnerability_finding_signatures'
include
BulkInsertSafe
include
BulkInsertSafe
include
VulnerabilityFindingSignatureHelpers
belongs_to
:finding
,
foreign_key:
'finding_id'
,
inverse_of: :signatures
,
class_name:
'Vulnerabilities::Finding'
belongs_to
:finding
,
foreign_key:
'finding_id'
,
inverse_of: :signatures
,
class_name:
'Vulnerabilities::Finding'
enum
algorithm_type:
{
hash:
1
,
location:
2
,
scope_offset:
3
},
_prefix: :algorithm
enum
algorithm_type:
{
hash:
1
,
location:
2
,
scope_offset:
3
},
_prefix: :algorithm
validates
:finding
,
presence:
true
validates
:finding
,
presence:
true
def
signature_hex
signature_sha
.
unpack1
(
"H*"
)
end
def
eql?
(
other
)
other
.
algorithm_type
==
algorithm_type
&&
other
.
signature_sha
==
signature_sha
end
alias_method
:==
,
:eql?
end
end
end
end
ee/app/services/security/store_report_service.rb
View file @
56fcaba4
...
@@ -61,17 +61,21 @@ module Security
...
@@ -61,17 +61,21 @@ module Security
return
return
end
end
vulnerability_params
=
finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:scan
,
:links
)
vulnerability_params
=
finding
.
to_hash
.
except
(
:compare_key
,
:identifiers
,
:location
,
:scanner
,
:scan
,
:links
,
:signatures
)
entity_params
=
Gitlab
::
Json
.
parse
(
vulnerability_params
&
.
dig
(
:raw_metadata
)).
slice
(
'description'
,
'message'
,
'solution'
,
'cve'
,
'location'
)
entity_params
=
Gitlab
::
Json
.
parse
(
vulnerability_params
&
.
dig
(
:raw_metadata
)).
slice
(
'description'
,
'message'
,
'solution'
,
'cve'
,
'location'
)
# Vulnerabilities::Finding (`vulnerability_occurrences`)
# Vulnerabilities::Finding (`vulnerability_occurrences`)
vulnerability_finding
=
vulnerability_findings_by_uuid
[
finding
.
uuid
]
||
vulnerability_finding
=
vulnerability_findings_by_uuid
[
finding
.
uuid
]
||
create_new
_vulnerability_finding
(
finding
,
vulnerability_params
.
merge
(
entity_params
))
find_or_create
_vulnerability_finding
(
finding
,
vulnerability_params
.
merge
(
entity_params
))
update_vulnerability_scanner
(
finding
)
unless
Feature
.
enabled?
(
:optimize_sql_query_for_security_report
,
project
)
update_vulnerability_scanner
(
finding
)
unless
Feature
.
enabled?
(
:optimize_sql_query_for_security_report
,
project
)
update_vulnerability_finding
(
vulnerability_finding
,
vulnerability_params
)
update_vulnerability_finding
(
vulnerability_finding
,
vulnerability_params
)
reset_remediations_for
(
vulnerability_finding
,
finding
)
reset_remediations_for
(
vulnerability_finding
,
finding
)
if
::
Feature
.
enabled?
(
:vulnerability_finding_signatures
,
project
)
update_feedbacks
(
vulnerability_finding
,
vulnerability_params
[
:uuid
])
update_finding_signatures
(
finding
,
vulnerability_finding
)
update_finding_signatures
(
finding
,
vulnerability_finding
)
end
# The maximum number of identifiers is not used in validation
# The maximum number of identifiers is not used in validation
# we just want to ignore the rest if a finding has more than that.
# we just want to ignore the rest if a finding has more than that.
...
@@ -86,8 +90,16 @@ module Security
...
@@ -86,8 +90,16 @@ module Security
create_vulnerability
(
vulnerability_finding
,
pipeline
)
create_vulnerability
(
vulnerability_finding
,
pipeline
)
end
end
def
find_or_create_vulnerability_finding
(
finding
,
create_params
)
if
::
Feature
.
enabled?
(
:vulnerability_finding_signatures
,
project
)
find_or_create_vulnerability_finding_with_signatures
(
finding
,
create_params
)
else
find_or_create_vulnerability_finding_with_location
(
finding
,
create_params
)
end
end
# rubocop: disable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def
create_new_vulnerability_finding
(
finding
,
create_params
)
def
find_or_create_vulnerability_finding_with_location
(
finding
,
create_params
)
find_params
=
{
find_params
=
{
scanner:
scanners_objects
[
finding
.
scanner
.
key
],
scanner:
scanners_objects
[
finding
.
scanner
.
key
],
primary_identifier:
identifiers_objects
[
finding
.
primary_identifier
.
key
],
primary_identifier:
identifiers_objects
[
finding
.
primary_identifier
.
key
],
...
@@ -120,6 +132,56 @@ module Security
...
@@ -120,6 +132,56 @@ module Security
end
end
end
end
def
get_matched_findings
(
finding
,
normalized_signatures
,
find_params
)
project
.
vulnerability_findings
.
where
(
**
find_params
).
filter
do
|
vf
|
vf
.
matches_signatures
(
normalized_signatures
,
finding
.
uuid
)
end
end
def
find_or_create_vulnerability_finding_with_signatures
(
finding
,
create_params
)
find_params
=
{
# this isn't taking prioritization into account (happens in the filter
# block below), but it *does* limit the number of findings we have to sift through
location_fingerprint:
[
finding
.
location
.
fingerprint
,
*
finding
.
signatures
.
map
(
&
:signature_hex
)],
scanner:
scanners_objects
[
finding
.
scanner
.
key
],
primary_identifier:
identifiers_objects
[
finding
.
primary_identifier
.
key
]
}
normalized_signatures
=
finding
.
signatures
.
map
do
|
signature
|
::
Vulnerabilities
::
FindingSignature
.
new
(
signature
.
to_hash
)
end
matched_findings
=
get_matched_findings
(
finding
,
normalized_signatures
,
find_params
)
begin
vulnerability_finding
=
matched_findings
.
first
if
vulnerability_finding
.
nil?
find_params
[
:uuid
]
=
finding
.
uuid
vulnerability_finding
=
project
.
vulnerability_findings
.
create_with
(
create_params
)
.
find_or_initialize_by
(
find_params
)
end
vulnerability_finding
.
uuid
=
finding
.
uuid
vulnerability_finding
.
location_fingerprint
=
if
finding
.
signatures
.
empty?
finding
.
location
.
fingerprint
else
finding
.
signatures
.
max_by
(
&
:priority
).
signature_hex
end
vulnerability_finding
.
location
=
create_params
.
dig
(
:location
)
vulnerability_finding
.
save!
vulnerability_finding
rescue
ActiveRecord
::
RecordNotUnique
get_matched_findings
(
finding
,
normalized_signatures
,
find_params
).
first
rescue
ActiveRecord
::
RecordInvalid
=>
e
Gitlab
::
ErrorTracking
.
track_and_raise_exception
(
e
,
create_params:
create_params
&
.
dig
(
:raw_metadata
))
end
end
def
update_vulnerability_scanner
(
finding
)
def
update_vulnerability_scanner
(
finding
)
scanner
=
scanners_objects
[
finding
.
scanner
.
key
]
scanner
=
scanners_objects
[
finding
.
scanner
.
key
]
scanner
.
update!
(
finding
.
scanner
.
to_hash
)
scanner
.
update!
(
finding
.
scanner
.
to_hash
)
...
@@ -223,6 +285,14 @@ module Security
...
@@ -223,6 +285,14 @@ module Security
end
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: enable CodeReuse/ActiveRecord
def
update_feedbacks
(
vulnerability_finding
,
new_uuid
)
vulnerability_finding
.
load_feedback
.
each
do
|
feedback
|
feedback
.
finding_uuid
=
new_uuid
feedback
.
vulnerability_data
=
vulnerability_finding
.
raw_metadata
feedback
.
save!
end
end
def
update_finding_signatures
(
finding
,
vulnerability_finding
)
def
update_finding_signatures
(
finding
,
vulnerability_finding
)
to_update
=
{}
to_update
=
{}
to_create
=
[]
to_create
=
[]
...
@@ -240,12 +310,12 @@ module Security
...
@@ -240,12 +310,12 @@ module Security
next
if
poro_signature
.
nil?
next
if
poro_signature
.
nil?
poro_signatures
.
delete
(
signature
.
algorithm_type
)
poro_signatures
.
delete
(
signature
.
algorithm_type
)
to_update
[
signature
.
id
]
=
poro_signature
.
to_h
to_update
[
signature
.
id
]
=
poro_signature
.
to_h
ash
end
end
# any remaining poro signatures left are new
# any remaining poro signatures left are new
poro_signatures
.
values
.
each
do
|
poro_signature
|
poro_signatures
.
values
.
each
do
|
poro_signature
|
attributes
=
poro_signature
.
to_h
.
merge
(
finding_id:
vulnerability_finding
.
id
)
attributes
=
poro_signature
.
to_h
ash
.
merge
(
finding_id:
vulnerability_finding
.
id
)
to_create
<<
::
Vulnerabilities
::
FindingSignature
.
new
(
attributes:
attributes
,
created_at:
Time
.
zone
.
now
,
updated_at:
Time
.
zone
.
now
)
to_create
<<
::
Vulnerabilities
::
FindingSignature
.
new
(
attributes:
attributes
,
created_at:
Time
.
zone
.
now
,
updated_at:
Time
.
zone
.
now
)
end
end
...
...
ee/config/feature_flags/development/vulnerability_finding_signatures.yml
0 → 100644
View file @
56fcaba4
---
name
:
vulnerability_finding_signatures
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54608
rollout_issue_url
:
https://gitlab.com/gitlab-org/gitlab/-/issues/322044
milestone
:
'
13.11'
type
:
development
group
:
group::vulnerability research
default_enabled
:
false
ee/lib/gitlab/ci/parsers/security/common.rb
View file @
56fcaba4
...
@@ -7,13 +7,14 @@ module Gitlab
...
@@ -7,13 +7,14 @@ module Gitlab
class
Common
class
Common
SecurityReportParserError
=
Class
.
new
(
Gitlab
::
Ci
::
Parsers
::
ParserError
)
SecurityReportParserError
=
Class
.
new
(
Gitlab
::
Ci
::
Parsers
::
ParserError
)
def
self
.
parse!
(
json_data
,
report
)
def
self
.
parse!
(
json_data
,
report
,
vulnerability_finding_signatures_enabled
=
false
)
new
(
json_data
,
report
).
parse!
new
(
json_data
,
report
,
vulnerability_finding_signatures_enabled
).
parse!
end
end
def
initialize
(
json_data
,
report
)
def
initialize
(
json_data
,
report
,
vulnerability_finding_signatures_enabled
=
false
)
@json_data
=
json_data
@json_data
=
json_data
@report
=
report
@report
=
report
@vulnerability_finding_signatures_enabled
=
vulnerability_finding_signatures_enabled
end
end
def
parse!
def
parse!
...
@@ -80,11 +81,20 @@ module Gitlab
...
@@ -80,11 +81,20 @@ module Gitlab
links
=
create_links
(
data
[
'links'
])
links
=
create_links
(
data
[
'links'
])
location
=
create_location
(
data
[
'location'
]
||
{})
location
=
create_location
(
data
[
'location'
]
||
{})
remediations
=
create_remediations
(
data
[
'remediations'
])
remediations
=
create_remediations
(
data
[
'remediations'
])
signatures
=
create_signatures
(
tracking_data
(
data
))
signatures
=
create_signatures
(
location
,
tracking_data
(
data
))
if
@vulnerability_finding_signatures_enabled
&&
!
signatures
.
empty?
# NOT the signature_sha - the compare key is hashed
# to create the project_fingerprint
highest_priority_signature
=
signatures
.
max_by
(
&
:priority
)
uuid
=
calculate_uuid_v5
(
identifiers
.
first
,
highest_priority_signature
.
signature_hex
)
else
uuid
=
calculate_uuid_v5
(
identifiers
.
first
,
location
&
.
fingerprint
)
end
report
.
add_finding
(
report
.
add_finding
(
::
Gitlab
::
Ci
::
Reports
::
Security
::
Finding
.
new
(
::
Gitlab
::
Ci
::
Reports
::
Security
::
Finding
.
new
(
uuid:
calculate_uuid_v5
(
identifiers
.
first
,
location
)
,
uuid:
uuid
,
report_type:
report
.
type
,
report_type:
report
.
type
,
name:
finding_name
(
data
,
identifiers
,
location
),
name:
finding_name
(
data
,
identifiers
,
location
),
compare_key:
data
[
'cve'
]
||
''
,
compare_key:
data
[
'cve'
]
||
''
,
...
@@ -99,14 +109,17 @@ module Gitlab
...
@@ -99,14 +109,17 @@ module Gitlab
raw_metadata:
data
.
to_json
,
raw_metadata:
data
.
to_json
,
metadata_version:
report_version
,
metadata_version:
report_version
,
details:
data
[
'details'
]
||
{},
details:
data
[
'details'
]
||
{},
signatures:
signatures
))
signatures:
signatures
,
project_id:
report
.
project_id
,
vulnerability_finding_signatures_enabled:
@vulnerability_finding_signatures_enabled
))
end
end
def
create_signatures
(
data
)
def
create_signatures
(
location
,
tracking
)
return
[]
if
data
.
nil?
||
data
[
'items'
].
nil?
tracking
||=
{
'items'
=>
[]
}
signature_algorithms
=
Hash
.
new
{
|
hash
,
key
|
hash
[
key
]
=
[]
}
signature_algorithms
=
Hash
.
new
{
|
hash
,
key
|
hash
[
key
]
=
[]
}
data
[
'items'
].
each
do
|
item
|
tracking
[
'items'
].
each
do
|
item
|
next
unless
item
.
key?
(
'signatures'
)
next
unless
item
.
key?
(
'signatures'
)
item
[
'signatures'
].
each
do
|
signature
|
item
[
'signatures'
].
each
do
|
signature
|
...
@@ -117,14 +130,16 @@ module Gitlab
...
@@ -117,14 +130,16 @@ module Gitlab
signature_algorithms
.
map
do
|
algorithm
,
values
|
signature_algorithms
.
map
do
|
algorithm
,
values
|
value
=
values
.
join
(
'|'
)
value
=
values
.
join
(
'|'
)
begin
signature
=
::
Gitlab
::
Ci
::
Reports
::
Security
::
FindingSignature
.
new
(
signature
=
::
Gitlab
::
Ci
::
Reports
::
Security
::
FindingSignature
.
new
(
algorithm_type:
algorithm
,
algorithm_type:
algorithm
,
signature_value:
value
signature_value:
value
)
)
signature
.
valid?
?
signature
:
nil
rescue
ArgumentError
=>
e
if
signature
.
valid?
Gitlab
::
ErrorTracking
.
track_and_raise_for_dev_exception
(
e
)
signature
else
e
=
SecurityReportParserError
.
new
(
"Vulnerability tracking signature is not valid:
#{
signature
}
"
)
Gitlab
::
ErrorTracking
.
track_exception
(
e
)
nil
nil
end
end
end
.
compact
end
.
compact
...
@@ -201,11 +216,11 @@ module Gitlab
...
@@ -201,11 +216,11 @@ module Gitlab
"
#{
identifier
.
name
}
in
#{
location
&
.
fingerprint_path
}
"
"
#{
identifier
.
name
}
in
#{
location
&
.
fingerprint_path
}
"
end
end
def
calculate_uuid_v5
(
primary_identifier
,
location
)
def
calculate_uuid_v5
(
primary_identifier
,
location
_fingerprint
)
uuid_v5_name_components
=
{
uuid_v5_name_components
=
{
report_type:
report
.
type
,
report_type:
report
.
type
,
primary_identifier_fingerprint:
primary_identifier
&
.
fingerprint
,
primary_identifier_fingerprint:
primary_identifier
&
.
fingerprint
,
location_fingerprint:
location
&
.
fingerprint
,
location_fingerprint:
location
_
fingerprint
,
project_id:
report
.
project_id
project_id:
report
.
project_id
}
}
...
...
ee/lib/gitlab/ci/reports/security/finding.rb
View file @
56fcaba4
...
@@ -5,6 +5,8 @@ module Gitlab
...
@@ -5,6 +5,8 @@ module Gitlab
module
Reports
module
Reports
module
Security
module
Security
class
Finding
class
Finding
include
::
VulnerabilityFindingHelpers
UNSAFE_SEVERITIES
=
%w[unknown high critical]
.
freeze
UNSAFE_SEVERITIES
=
%w[unknown high critical]
.
freeze
attr_reader
:compare_key
attr_reader
:compare_key
...
@@ -25,10 +27,11 @@ module Gitlab
...
@@ -25,10 +27,11 @@ module Gitlab
attr_reader
:remediations
attr_reader
:remediations
attr_reader
:details
attr_reader
:details
attr_reader
:signatures
attr_reader
:signatures
attr_reader
:project_id
delegate
:file_path
,
:start_line
,
:end_line
,
to: :location
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:
[])
# rubocop:disable Metrics/ParameterLists
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
@compare_key
=
compare_key
@compare_key
=
compare_key
@confidence
=
confidence
@confidence
=
confidence
@identifiers
=
identifiers
@identifiers
=
identifiers
...
@@ -45,6 +48,8 @@ module Gitlab
...
@@ -45,6 +48,8 @@ module Gitlab
@remediations
=
remediations
@remediations
=
remediations
@details
=
details
@details
=
details
@signatures
=
signatures
@signatures
=
signatures
@project_id
=
project_id
@vulnerability_finding_signatures_enabled
=
vulnerability_finding_signatures_enabled
@project_fingerprint
=
generate_project_fingerprint
@project_fingerprint
=
generate_project_fingerprint
end
end
...
@@ -66,6 +71,7 @@ module Gitlab
...
@@ -66,6 +71,7 @@ module Gitlab
severity
severity
uuid
uuid
details
details
signatures
]
.
each_with_object
({})
do
|
key
,
hash
|
]
.
each_with_object
({})
do
|
key
,
hash
|
hash
[
key
]
=
public_send
(
key
)
# rubocop:disable GitlabSecurity/PublicSend
hash
[
key
]
=
public_send
(
key
)
# rubocop:disable GitlabSecurity/PublicSend
end
end
...
@@ -85,13 +91,22 @@ module Gitlab
...
@@ -85,13 +91,22 @@ module Gitlab
end
end
def
eql?
(
other
)
def
eql?
(
other
)
report_type
==
other
.
report_type
&&
return
false
unless
report_type
==
other
.
report_type
&&
primary_identifier_fingerprint
==
other
.
primary_identifier_fingerprint
location
.
fingerprint
==
other
.
location
.
fingerprint
&&
primary_fingerprint
==
other
.
primary_fingerprint
if
@vulnerability_finding_signatures_enabled
matches_signatures
(
other
.
signatures
,
other
.
uuid
)
else
location
.
fingerprint
==
other
.
location
.
fingerprint
end
end
end
def
hash
def
hash
report_type
.
hash
^
location
.
fingerprint
.
hash
^
primary_fingerprint
.
hash
if
@vulnerability_finding_signatures_enabled
&&
!
signatures
.
empty?
highest_signature
=
signatures
.
max_by
(
&
:priority
)
report_type
.
hash
^
highest_signature
.
signature_hex
.
hash
^
primary_identifier_fingerprint
.
hash
else
report_type
.
hash
^
location
.
fingerprint
.
hash
^
primary_identifier_fingerprint
.
hash
end
end
end
def
valid?
def
valid?
...
@@ -104,7 +119,7 @@ module Gitlab
...
@@ -104,7 +119,7 @@ module Gitlab
end
end
end
end
def
primary_fingerprint
def
primary_
identifier_
fingerprint
primary_identifier
&
.
fingerprint
primary_identifier
&
.
fingerprint
end
end
...
...
ee/lib/gitlab/ci/reports/security/finding_signature.rb
View file @
56fcaba4
...
@@ -5,6 +5,8 @@ module Gitlab
...
@@ -5,6 +5,8 @@ module Gitlab
module
Reports
module
Reports
module
Security
module
Security
class
FindingSignature
class
FindingSignature
include
VulnerabilityFindingSignatureHelpers
attr_accessor
:algorithm_type
,
:signature_value
attr_accessor
:algorithm_type
,
:signature_value
def
initialize
(
params
=
{})
def
initialize
(
params
=
{})
...
@@ -12,11 +14,19 @@ module Gitlab
...
@@ -12,11 +14,19 @@ module Gitlab
@signature_value
=
params
.
dig
(
:signature_value
)
@signature_value
=
params
.
dig
(
:signature_value
)
end
end
def
priority
::
Vulnerabilities
::
FindingSignature
.
priority
(
algorithm_type
)
end
def
signature_sha
def
signature_sha
Digest
::
SHA1
.
digest
(
signature_value
)
Digest
::
SHA1
.
digest
(
signature_value
)
end
end
def
to_h
def
signature_hex
signature_sha
.
unpack1
(
"H*"
)
end
def
to_hash
{
{
algorithm_type:
algorithm_type
,
algorithm_type:
algorithm_type
,
signature_sha:
signature_sha
signature_sha:
signature_sha
...
@@ -26,6 +36,13 @@ module Gitlab
...
@@ -26,6 +36,13 @@ module Gitlab
def
valid?
def
valid?
::
Vulnerabilities
::
FindingSignature
.
algorithm_types
.
key?
(
algorithm_type
)
::
Vulnerabilities
::
FindingSignature
.
algorithm_types
.
key?
(
algorithm_type
)
end
end
def
eql?
(
other
)
other
.
algorithm_type
==
algorithm_type
&&
other
.
signature_sha
==
signature_sha
end
alias_method
:==
,
:eql?
end
end
end
end
end
end
...
...
ee/lib/gitlab/ci/reports/security/locations/container_scanning.rb
View file @
56fcaba4
...
@@ -18,12 +18,12 @@ module Gitlab
...
@@ -18,12 +18,12 @@ module Gitlab
@package_version
=
package_version
@package_version
=
package_version
end
end
private
def
fingerprint_data
def
fingerprint_data
"
#{
docker_image_name_without_tag
}
:
#{
package_name
}
"
"
#{
docker_image_name_without_tag
}
:
#{
package_name
}
"
end
end
private
def
docker_image_name_without_tag
def
docker_image_name_without_tag
base_name
,
version
=
image
.
split
(
':'
)
base_name
,
version
=
image
.
split
(
':'
)
...
...
ee/lib/gitlab/ci/reports/security/locations/coverage_fuzzing.rb
View file @
56fcaba4
...
@@ -16,8 +16,6 @@ module Gitlab
...
@@ -16,8 +16,6 @@ module Gitlab
@crash_state
=
crash_state
@crash_state
=
crash_state
end
end
private
def
fingerprint_data
def
fingerprint_data
"
#{
crash_type
}
:
#{
crash_state
}
"
"
#{
crash_type
}
:
#{
crash_state
}
"
end
end
...
...
ee/lib/gitlab/ci/reports/security/locations/dast.rb
View file @
56fcaba4
...
@@ -20,8 +20,6 @@ module Gitlab
...
@@ -20,8 +20,6 @@ module Gitlab
alias_method
:fingerprint_path
,
:path
alias_method
:fingerprint_path
,
:path
private
def
fingerprint_data
def
fingerprint_data
"
#{
path
}
:
#{
method_name
}
:
#{
param
}
"
"
#{
path
}
:
#{
method_name
}
:
#{
param
}
"
end
end
...
...
ee/lib/gitlab/ci/reports/security/locations/dependency_scanning.rb
View file @
56fcaba4
...
@@ -18,8 +18,6 @@ module Gitlab
...
@@ -18,8 +18,6 @@ module Gitlab
@package_version
=
package_version
@package_version
=
package_version
end
end
private
def
fingerprint_data
def
fingerprint_data
"
#{
file_path
}
:
#{
package_name
}
"
"
#{
file_path
}
:
#{
package_name
}
"
end
end
...
...
ee/lib/gitlab/ci/reports/security/locations/sast.rb
View file @
56fcaba4
...
@@ -22,8 +22,6 @@ module Gitlab
...
@@ -22,8 +22,6 @@ module Gitlab
@start_line
=
start_line
@start_line
=
start_line
end
end
private
def
fingerprint_data
def
fingerprint_data
"
#{
file_path
}
:
#{
start_line
}
:
#{
end_line
}
"
"
#{
file_path
}
:
#{
start_line
}
:
#{
end_line
}
"
end
end
...
...
ee/lib/gitlab/ci/reports/security/locations/secret_detection.rb
View file @
56fcaba4
...
@@ -22,8 +22,6 @@ module Gitlab
...
@@ -22,8 +22,6 @@ module Gitlab
@start_line
=
start_line
@start_line
=
start_line
end
end
private
def
fingerprint_data
def
fingerprint_data
"
#{
file_path
}
:
#{
start_line
}
:
#{
end_line
}
"
"
#{
file_path
}
:
#{
start_line
}
:
#{
end_line
}
"
end
end
...
...
ee/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb
View file @
56fcaba4
...
@@ -7,13 +7,17 @@ module Gitlab
...
@@ -7,13 +7,17 @@ module Gitlab
class
VulnerabilityReportsComparer
class
VulnerabilityReportsComparer
include
Gitlab
::
Utils
::
StrongMemoize
include
Gitlab
::
Utils
::
StrongMemoize
attr_reader
:base_report
,
:head_report
attr_reader
:base_report
,
:head_report
,
:added
,
:fixed
ACCEPTABLE_REPORT_AGE
=
1
.
week
ACCEPTABLE_REPORT_AGE
=
1
.
week
def
initialize
(
base_report
,
head_report
)
def
initialize
(
base_report
,
head_report
)
@base_report
=
base_report
@base_report
=
base_report
@head_report
=
head_report
@head_report
=
head_report
@added
=
[]
@fixed
=
[]
calculate_changes
end
end
def
base_report_created_at
def
base_report_created_at
...
@@ -30,16 +34,30 @@ module Gitlab
...
@@ -30,16 +34,30 @@ module Gitlab
ACCEPTABLE_REPORT_AGE
.
ago
>
@base_report
.
created_at
ACCEPTABLE_REPORT_AGE
.
ago
>
@base_report
.
created_at
end
end
def
added
def
calculate_changes
strong_memoize
(
:added
)
do
base_findings
=
base_report
.
findings
head_report
.
findings
-
base_report
.
findings
head_findings
=
head_report
.
findings
end
head_findings_hash
=
head_findings
.
index_by
(
&
:object_id
)
# This is slow - O(N^2). If we didn't need to worry about one high
# priority fingerprint that doesn't match overruling a lower
# priority fingerprint that does match, we'd be able to do some
# set operations here
base_findings
.
each
do
|
base_finding
|
still_exists
=
false
head_findings
.
each
do
|
head_finding
|
next
unless
base_finding
.
eql?
(
head_finding
)
still_exists
=
true
head_findings_hash
.
delete
(
head_finding
.
object_id
)
break
end
end
def
fixed
@fixed
<<
base_finding
unless
still_exists
strong_memoize
(
:fixed
)
do
base_report
.
findings
-
head_report
.
findings
end
end
@added
=
head_findings_hash
.
values
end
end
end
end
end
end
...
...
ee/spec/factories/ci/reports/security/findings.rb
View file @
56fcaba4
...
@@ -39,6 +39,7 @@ FactoryBot.define do
...
@@ -39,6 +39,7 @@ FactoryBot.define do
project_id:
n
project_id:
n
)
)
end
end
vulnerability_finding_signatures_enabled
{
false
}
skip_create
skip_create
...
...
ee/spec/factories/vulnerabilities/feedback.rb
View file @
56fcaba4
...
@@ -17,6 +17,7 @@ FactoryBot.define do
...
@@ -17,6 +17,7 @@ FactoryBot.define do
category
{
'sast'
}
category
{
'sast'
}
project_fingerprint
{
generate
(
:project_fingerprint
)
}
project_fingerprint
{
generate
(
:project_fingerprint
)
}
vulnerability_data
{
{
category:
'sast'
}
}
vulnerability_data
{
{
category:
'sast'
}
}
finding_uuid
{
Gitlab
::
UUID
.
v5
(
SecureRandom
.
hex
)
}
trait
:dismissal
do
trait
:dismissal
do
feedback_type
{
'dismissal'
}
feedback_type
{
'dismissal'
}
...
...
ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb
View file @
56fcaba4
...
@@ -83,12 +83,12 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -83,12 +83,12 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
# use the same number of queries, regardless of the number of findings
# use the same number of queries, regardless of the number of findings
# contained in the pipeline report.
# contained in the pipeline report.
sast_findings
=
pipeline
.
security_reports
.
reports
[
'sast
'
].
findings
container_scanning_findings
=
pipeline
.
security_reports
.
reports
[
'container_scanning
'
].
findings
dep_findings
=
pipeline
.
security_reports
.
reports
[
'dependency_scanning'
].
findings
dep_findings
=
pipeline
.
security_reports
.
reports
[
'dependency_scanning'
].
findings
# this test is invalid if we don't have more
sast
findings than dep findings
# this test is invalid if we don't have more
container_scanning
findings than dep findings
expect
(
sast
_findings
.
count
).
to
be
>
dep_findings
.
count
expect
(
container_scanning
_findings
.
count
).
to
be
>
dep_findings
.
count
(
sast
_findings
+
dep_findings
).
each
do
|
report_finding
|
(
container_scanning
_findings
+
dep_findings
).
each
do
|
report_finding
|
# create a finding and a vulnerability for each report finding
# create a finding and a vulnerability for each report finding
# (the vulnerability is created with the :confirmed trait)
# (the vulnerability is created with the :confirmed trait)
create
(
:vulnerabilities_finding
,
create
(
:vulnerabilities_finding
,
...
@@ -103,7 +103,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -103,7 +103,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
# should be the same number of queries between different report types
# should be the same number of queries between different report types
expect
do
expect
do
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[
sast
]
}).
execute
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[
container_scanning
]
}).
execute
end
.
to
issue_same_number_of_queries_as
{
end
.
to
issue_same_number_of_queries_as
{
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[dependency_scanning]
}).
execute
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[dependency_scanning]
}).
execute
}
}
...
@@ -117,11 +117,11 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -117,11 +117,11 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
orig_security_reports
=
pipeline
.
security_reports
orig_security_reports
=
pipeline
.
security_reports
new_finding
=
create
(
:ci_reports_security_finding
)
new_finding
=
create
(
:ci_reports_security_finding
)
expect
do
expect
do
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[
sast
]
}).
execute
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[
container_scanning
]
}).
execute
end
.
to
issue_same_number_of_queries_as
{
end
.
to
issue_same_number_of_queries_as
{
orig_security_reports
.
reports
[
'
sast
'
].
add_finding
(
new_finding
)
orig_security_reports
.
reports
[
'
container_scanning
'
].
add_finding
(
new_finding
)
allow
(
pipeline
).
to
receive
(
:security_reports
).
and_return
(
orig_security_reports
)
allow
(
pipeline
).
to
receive
(
:security_reports
).
and_return
(
orig_security_reports
)
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[
sast
]
}).
execute
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[
container_scanning
]
}).
execute
}
}
end
end
end
end
...
@@ -130,9 +130,11 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -130,9 +130,11 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
context
'when sast'
do
context
'when sast'
do
let
(
:params
)
{
{
report_type:
%w[sast]
}
}
let
(
:params
)
{
{
report_type:
%w[sast]
}
}
let
(
:sast_report_fingerprints
)
{
pipeline
.
security_reports
.
reports
[
'sast'
].
findings
.
map
(
&
:location
).
map
(
&
:fingerprint
)
}
let
(
:sast_report_fingerprints
)
{
pipeline
.
security_reports
.
reports
[
'sast'
].
findings
.
map
(
&
:location
).
map
(
&
:fingerprint
)
}
let
(
:sast_report_uuids
)
{
pipeline
.
security_reports
.
reports
[
'sast'
].
findings
.
map
(
&
:uuid
)
}
it
'includes only sast'
do
it
'includes only sast'
do
expect
(
subject
.
findings
.
map
(
&
:location_fingerprint
)).
to
match_array
(
sast_report_fingerprints
)
expect
(
subject
.
findings
.
map
(
&
:location_fingerprint
)).
to
match_array
(
sast_report_fingerprints
)
expect
(
subject
.
findings
.
map
(
&
:uuid
)).
to
match_array
(
sast_report_uuids
)
expect
(
subject
.
findings
.
count
).
to
eq
(
sast_count
)
expect
(
subject
.
findings
.
count
).
to
eq
(
sast_count
)
end
end
end
end
...
@@ -172,6 +174,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -172,6 +174,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
let
(
:ds_finding
)
{
pipeline
.
security_reports
.
reports
[
"dependency_scanning"
].
findings
.
first
}
let
(
:ds_finding
)
{
pipeline
.
security_reports
.
reports
[
"dependency_scanning"
].
findings
.
first
}
let
(
:sast_finding
)
{
pipeline
.
security_reports
.
reports
[
"sast"
].
findings
.
first
}
let
(
:sast_finding
)
{
pipeline
.
security_reports
.
reports
[
"sast"
].
findings
.
first
}
context
'when vulnerability_finding_signatures feature flag is disabled'
do
let!
(
:feedback
)
do
let!
(
:feedback
)
do
[
[
create
(
create
(
...
@@ -181,7 +184,8 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -181,7 +184,8 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
project:
project
,
project:
project
,
pipeline:
pipeline
,
pipeline:
pipeline
,
project_fingerprint:
ds_finding
.
project_fingerprint
,
project_fingerprint:
ds_finding
.
project_fingerprint
,
vulnerability_data:
ds_finding
.
raw_metadata
vulnerability_data:
ds_finding
.
raw_metadata
,
finding_uuid:
ds_finding
.
uuid
),
),
create
(
create
(
:vulnerability_feedback
,
:vulnerability_feedback
,
...
@@ -190,11 +194,16 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -190,11 +194,16 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
project:
project
,
project:
project
,
pipeline:
pipeline
,
pipeline:
pipeline
,
project_fingerprint:
sast_finding
.
project_fingerprint
,
project_fingerprint:
sast_finding
.
project_fingerprint
,
vulnerability_data:
sast_finding
.
raw_metadata
vulnerability_data:
sast_finding
.
raw_metadata
,
finding_uuid:
sast_finding
.
uuid
)
)
]
]
end
end
before
do
stub_feature_flags
(
vulnerability_finding_signatures:
false
)
end
context
'when unscoped'
do
context
'when unscoped'
do
subject
{
described_class
.
new
(
pipeline:
pipeline
).
execute
}
subject
{
described_class
.
new
(
pipeline:
pipeline
).
execute
}
...
@@ -222,6 +231,54 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
...
@@ -222,6 +231,54 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
end
end
end
end
context
'when vulnerability_finding_signatures feature flag is enabled'
do
let!
(
:feedback
)
do
[
create
(
:vulnerability_feedback
,
:dismissal
,
:sast
,
project:
project
,
pipeline:
pipeline
,
project_fingerprint:
sast_finding
.
project_fingerprint
,
vulnerability_data:
sast_finding
.
raw_metadata
,
finding_uuid:
sast_finding
.
uuid
)
]
end
before
do
stub_feature_flags
(
vulnerability_finding_signatures:
true
)
end
context
'when unscoped'
do
subject
{
described_class
.
new
(
pipeline:
pipeline
).
execute
}
it
'returns non-dismissed vulnerabilities'
do
expect
(
subject
.
findings
.
count
).
to
eq
(
cs_count
+
dast_count
+
ds_count
+
sast_count
-
feedback
.
count
)
expect
(
subject
.
findings
.
map
(
&
:project_fingerprint
)).
not_to
include
(
*
feedback
.
map
(
&
:project_fingerprint
))
end
end
context
'when `dismissed`'
do
subject
{
described_class
.
new
(
pipeline:
pipeline
,
params:
{
report_type:
%w[sast]
,
scope:
'dismissed'
}
).
execute
}
it
'returns non-dismissed vulnerabilities'
do
expect
(
subject
.
findings
.
count
).
to
eq
(
sast_count
-
1
)
expect
(
subject
.
findings
.
map
(
&
:project_fingerprint
)).
not_to
include
(
sast_finding
.
project_fingerprint
)
end
end
context
'when `all`'
do
let
(
:params
)
{
{
report_type:
%w[sast dast container_scanning dependency_scanning]
,
scope:
'all'
}
}
it
'returns all vulnerabilities'
do
expect
(
subject
.
findings
.
count
).
to
eq
(
cs_count
+
dast_count
+
ds_count
+
sast_count
)
end
end
end
end
context
'by severity'
do
context
'by severity'
do
context
'when unscoped'
do
context
'when unscoped'
do
subject
{
described_class
.
new
(
pipeline:
pipeline
).
execute
}
subject
{
described_class
.
new
(
pipeline:
pipeline
).
execute
}
...
...
ee/spec/lib/gitlab/ci/parsers/security/common_spec.rb
View file @
56fcaba4
...
@@ -4,30 +4,22 @@ require 'spec_helper'
...
@@ -4,30 +4,22 @@ require 'spec_helper'
RSpec
.
describe
Gitlab
::
Ci
::
Parsers
::
Security
::
Common
do
RSpec
.
describe
Gitlab
::
Ci
::
Parsers
::
Security
::
Common
do
describe
'#parse!'
do
describe
'#parse!'
do
where
(
vulnerability_finding_signatures_enabled:
[
true
,
false
])
with_them
do
let_it_be
(
:pipeline
)
{
create
(
:ci_pipeline
)
}
let_it_be
(
:pipeline
)
{
create
(
:ci_pipeline
)
}
let
(
:artifact
)
{
build
(
:ee_ci_job_artifact
,
:common_security_report
)
}
let
(
:artifact
)
{
build
(
:ee_ci_job_artifact
,
:common_security_report
)
}
let
(
:report
)
{
Gitlab
::
Ci
::
Reports
::
Security
::
Report
.
new
(
artifact
.
file_type
,
pipeline
,
2
.
weeks
.
ago
)
}
let
(
:report
)
{
Gitlab
::
Ci
::
Reports
::
Security
::
Report
.
new
(
artifact
.
file_type
,
pipeline
,
2
.
weeks
.
ago
)
}
let
(
:location
)
{
::
Gitlab
::
Ci
::
Reports
::
Security
::
Locations
::
DependencyScanning
.
new
(
file_path:
'yarn/yarn.lock'
,
package_version:
'v2'
,
package_name:
'saml2'
)
}
let
(
:location
)
{
::
Gitlab
::
Ci
::
Reports
::
Security
::
Locations
::
DependencyScanning
.
new
(
file_path:
'yarn/yarn.lock'
,
package_version:
'v2'
,
package_name:
'saml2'
)
}
let
(
:tracking_data
)
do
let
(
:tracking_data
)
{
nil
}
{
'type'
=>
'source'
,
'items'
=>
[
'signatures'
=>
[
{
'algorithm'
=>
'hash'
,
'value'
=>
'hash_value'
},
{
'algorithm'
=>
'location'
,
'value'
=>
'location_value'
},
{
'algorithm'
=>
'scope_offset'
,
'value'
=>
'scope_offset_value'
}
]
]
}
end
before
do
before
do
allow_next_instance_of
(
described_class
)
do
|
parser
|
allow_next_instance_of
(
described_class
)
do
|
parser
|
allow
(
parser
).
to
receive
(
:create_location
).
and_return
(
location
)
allow
(
parser
).
to
receive
(
:create_location
).
and_return
(
location
)
allow
(
parser
).
to
receive
(
:tracking_data
).
and_return
(
tracking_data
)
allow
(
parser
).
to
receive
(
:tracking_data
).
and_return
(
tracking_data
)
end
end
artifact
.
each_blob
{
|
blob
|
described_class
.
parse!
(
blob
,
report
)
}
artifact
.
each_blob
{
|
blob
|
described_class
.
parse!
(
blob
,
report
,
vulnerability_finding_signatures_enabled
)
}
end
end
describe
'parsing finding.name'
do
describe
'parsing finding.name'
do
...
@@ -200,8 +192,21 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
...
@@ -200,8 +192,21 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
end
end
end
end
describe
'parsing signature'
do
describe
'parsing tracking'
do
context
'with valid signature information'
do
let
(
:tracking_data
)
do
{
'type'
=>
'source'
,
'items'
=>
[
'signatures'
=>
[
{
'algorithm'
=>
'hash'
,
'value'
=>
'hash_value'
},
{
'algorithm'
=>
'location'
,
'value'
=>
'location_value'
},
{
'algorithm'
=>
'scope_offset'
,
'value'
=>
'scope_offset_value'
}
]
]
}
end
context
'with valid tracking information'
do
it
'creates signatures for each algorithm'
do
it
'creates signatures for each algorithm'
do
finding
=
report
.
findings
.
first
finding
=
report
.
findings
.
first
expect
(
finding
.
signatures
.
size
).
to
eq
(
3
)
expect
(
finding
.
signatures
.
size
).
to
eq
(
3
)
...
@@ -209,7 +214,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
...
@@ -209,7 +214,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
end
end
end
end
context
'with invalid signature
information'
do
context
'with invalid tracking
information'
do
let
(
:tracking_data
)
do
let
(
:tracking_data
)
do
{
{
'type'
=>
'source'
,
'type'
=>
'source'
,
...
@@ -229,6 +234,34 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
...
@@ -229,6 +234,34 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
expect
(
finding
.
signatures
.
map
(
&
:algorithm_type
).
to_set
).
to
eq
(
Set
[
'hash'
,
'location'
])
expect
(
finding
.
signatures
.
map
(
&
:algorithm_type
).
to_set
).
to
eq
(
Set
[
'hash'
,
'location'
])
end
end
end
end
context
'with valid tracking information'
do
it
'creates signatures for each signature algorithm'
do
finding
=
report
.
findings
.
first
expect
(
finding
.
signatures
.
size
).
to
eq
(
3
)
expect
(
finding
.
signatures
.
map
(
&
:algorithm_type
)).
to
eq
(
%w[hash location scope_offset]
)
signatures
=
finding
.
signatures
.
index_by
(
&
:algorithm_type
)
expected_values
=
tracking_data
[
'items'
][
0
][
'signatures'
].
index_by
{
|
x
|
x
[
'algorithm'
]
}
expect
(
signatures
[
'hash'
].
signature_value
).
to
eq
(
expected_values
[
'hash'
][
'value'
])
expect
(
signatures
[
'location'
].
signature_value
).
to
eq
(
expected_values
[
'location'
][
'value'
])
expect
(
signatures
[
'scope_offset'
].
signature_value
).
to
eq
(
expected_values
[
'scope_offset'
][
'value'
])
end
it
'sets the uuid according to the higest priority signature'
do
finding
=
report
.
findings
.
first
highest_signature
=
finding
.
signatures
.
max_by
(
&
:priority
)
identifiers
=
if
vulnerability_finding_signatures_enabled
"
#{
finding
.
report_type
}
-
#{
finding
.
primary_identifier
.
fingerprint
}
-
#{
highest_signature
.
signature_hex
}
-
#{
report
.
project_id
}
"
else
"
#{
finding
.
report_type
}
-
#{
finding
.
primary_identifier
.
fingerprint
}
-
#{
finding
.
location
.
fingerprint
}
-
#{
report
.
project_id
}
"
end
expect
(
finding
.
uuid
).
to
eq
(
Gitlab
::
UUID
.
v5
(
identifiers
))
end
end
end
end
end
end
end
end
end
ee/spec/lib/gitlab/ci/reports/security/finding_signature_spec.rb
View file @
56fcaba4
...
@@ -18,15 +18,12 @@ RSpec.describe Gitlab::Ci::Reports::Security::FindingSignature do
...
@@ -18,15 +18,12 @@ RSpec.describe Gitlab::Ci::Reports::Security::FindingSignature do
expect
(
subject
.
algorithm_type
).
to
eq
(
params
[
:algorithm_type
])
expect
(
subject
.
algorithm_type
).
to
eq
(
params
[
:algorithm_type
])
expect
(
subject
.
signature_value
).
to
eq
(
params
[
:signature_value
])
expect
(
subject
.
signature_value
).
to
eq
(
params
[
:signature_value
])
end
end
describe
'#valid?'
do
it
'returns true'
do
expect
(
subject
.
valid?
).
to
eq
(
true
)
end
end
end
end
describe
'#to_h'
do
it
'returns a hash representation of the signature'
do
expect
(
subject
.
to_h
).
to
eq
(
algorithm_type:
params
[
:algorithm_type
],
signature_sha:
Digest
::
SHA1
.
digest
(
params
[
:signature_value
])
)
end
end
end
end
...
@@ -50,4 +47,13 @@ RSpec.describe Gitlab::Ci::Reports::Security::FindingSignature do
...
@@ -50,4 +47,13 @@ RSpec.describe Gitlab::Ci::Reports::Security::FindingSignature do
end
end
end
end
end
end
describe
'#to_hash'
do
it
'returns a hash representation of the signature'
do
expect
(
subject
.
to_hash
).
to
eq
(
algorithm_type:
params
[
:algorithm_type
],
signature_sha:
Digest
::
SHA1
.
digest
(
params
[
:signature_value
])
)
end
end
end
end
ee/spec/lib/gitlab/ci/reports/security/finding_spec.rb
View file @
56fcaba4
...
@@ -137,7 +137,8 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
...
@@ -137,7 +137,8 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
scan:
occurrence
.
scan
,
scan:
occurrence
.
scan
,
severity:
occurrence
.
severity
,
severity:
occurrence
.
severity
,
uuid:
occurrence
.
uuid
,
uuid:
occurrence
.
uuid
,
details:
occurrence
.
details
details:
occurrence
.
details
,
signatures:
[]
})
})
end
end
end
end
...
@@ -197,9 +198,11 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
...
@@ -197,9 +198,11 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
end
end
describe
'#eql?'
do
describe
'#eql?'
do
where
(
vulnerability_finding_signatures_enabled:
[
true
,
false
])
with_them
do
let
(
:identifier
)
{
build
(
:ci_reports_security_identifier
)
}
let
(
:identifier
)
{
build
(
:ci_reports_security_identifier
)
}
let
(
:location
)
{
build
(
:ci_reports_security_locations_sast
)
}
let
(
:location
)
{
build
(
:ci_reports_security_locations_sast
)
}
let
(
:finding
)
{
build
(
:ci_reports_security_finding
,
severity:
'low'
,
report_type: :sast
,
identifiers:
[
identifier
],
location:
location
)
}
let
(
:finding
)
{
build
(
:ci_reports_security_finding
,
severity:
'low'
,
report_type: :sast
,
identifiers:
[
identifier
],
location:
location
,
vulnerability_finding_signatures_enabled:
vulnerability_finding_signatures_enabled
)
}
let
(
:report_type
)
{
:secret_detection
}
let
(
:report_type
)
{
:secret_detection
}
let
(
:identifier_external_id
)
{
'foo'
}
let
(
:identifier_external_id
)
{
'foo'
}
...
@@ -211,9 +214,12 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
...
@@ -211,9 +214,12 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
severity:
'low'
,
severity:
'low'
,
report_type:
report_type
,
report_type:
report_type
,
identifiers:
[
other_identifier
],
identifiers:
[
other_identifier
],
location:
other_location
)
location:
other_location
,
vulnerability_finding_signatures_enabled:
vulnerability_finding_signatures_enabled
)
end
end
let
(
:signature
)
{
::
Gitlab
::
Ci
::
Reports
::
Security
::
FindingSignature
.
new
(
algorithm_type:
'location'
,
signature_value:
'value1'
)
}
subject
{
finding
.
eql?
(
other_finding
)
}
subject
{
finding
.
eql?
(
other_finding
)
}
context
'when the primary_identifier is nil'
do
context
'when the primary_identifier is nil'
do
...
@@ -230,25 +236,30 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
...
@@ -230,25 +236,30 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
context
'when the other finding has same primary identifier fingerprint'
do
context
'when the other finding has same primary identifier fingerprint'
do
let
(
:identifier_external_id
)
{
identifier
.
external_id
}
let
(
:identifier_external_id
)
{
identifier
.
external_id
}
context
'when the other finding has same location fingerprint'
do
context
'when the other finding has same location signature'
do
before
do
finding
.
signatures
<<
signature
other_finding
.
signatures
<<
signature
end
let
(
:location_start_line
)
{
location
.
start_line
}
let
(
:location_start_line
)
{
location
.
start_line
}
it
{
is_expected
.
to
be
(
true
)
}
it
{
is_expected
.
to
be
(
true
)
}
end
end
context
'when the other finding does not have same location fingerprint
'
do
context
'when the other finding does not have same location signature
'
do
it
{
is_expected
.
to
be
(
false
)
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
end
end
context
'when the other finding does not have same primary identifier fingerprint'
do
context
'when the other finding does not have same primary identifier fingerprint'
do
context
'when the other finding has same location fingerprint
'
do
context
'when the other finding has same location signature
'
do
let
(
:location_start_line
)
{
location
.
start_line
}
let
(
:location_start_line
)
{
location
.
start_line
}
it
{
is_expected
.
to
be
(
false
)
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
context
'when the other finding does not have same location fingerprint
'
do
context
'when the other finding does not have same location signature
'
do
it
{
is_expected
.
to
be
(
false
)
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
end
end
...
@@ -258,30 +269,31 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
...
@@ -258,30 +269,31 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
context
'when the other finding has same primary identifier fingerprint'
do
context
'when the other finding has same primary identifier fingerprint'
do
let
(
:identifier_external_id
)
{
identifier
.
external_id
}
let
(
:identifier_external_id
)
{
identifier
.
external_id
}
context
'when the other finding has same location fingerprint
'
do
context
'when the other finding has same location signature
'
do
let
(
:location_start_line
)
{
location
.
start_line
}
let
(
:location_start_line
)
{
location
.
start_line
}
it
{
is_expected
.
to
be
(
false
)
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
context
'when the other finding does not have same location fingerprint
'
do
context
'when the other finding does not have same location signature
'
do
it
{
is_expected
.
to
be
(
false
)
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
end
end
context
'when the other finding does not have same primary identifier fingerprint'
do
context
'when the other finding does not have same primary identifier fingerprint'
do
context
'when the other finding has same location fingerprint
'
do
context
'when the other finding has same location signature
'
do
let
(
:location_start_line
)
{
location
.
start_line
}
let
(
:location_start_line
)
{
location
.
start_line
}
it
{
is_expected
.
to
be
(
false
)
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
context
'when the other finding does not have same location fingerprint
'
do
context
'when the other finding does not have same location signature
'
do
it
{
is_expected
.
to
be
(
false
)
}
it
{
is_expected
.
to
be
(
false
)
}
end
end
end
end
end
end
end
end
end
describe
'#valid?'
do
describe
'#valid?'
do
let
(
:scanner
)
{
build
(
:ci_reports_security_scanner
)
}
let
(
:scanner
)
{
build
(
:ci_reports_security_scanner
)
}
...
@@ -345,4 +357,55 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
...
@@ -345,4 +357,55 @@ RSpec.describe Gitlab::Ci::Reports::Security::Finding do
it
{
is_expected
.
to
match_array
(
expected_keys
)
}
it
{
is_expected
.
to
match_array
(
expected_keys
)
}
end
end
describe
'#hash'
do
let
(
:scanner
)
{
build
(
:ci_reports_security_scanner
)
}
let
(
:identifiers
)
{
[
build
(
:ci_reports_security_identifier
)]
}
let
(
:location
)
{
build
(
:ci_reports_security_locations_sast
)
}
let
(
:uuid
)
{
SecureRandom
.
uuid
}
context
'with vulnerability_finding_signatures enabled'
do
let
(
:finding
)
do
build
(
:ci_reports_security_finding
,
scanner:
scanner
,
identifiers:
identifiers
,
location:
location
,
uuid:
uuid
,
compare_key:
''
,
vulnerability_finding_signatures_enabled:
true
)
end
let
(
:low_priority_signature
)
{
::
Gitlab
::
Ci
::
Reports
::
Security
::
FindingSignature
.
new
(
algorithm_type:
'location'
,
signature_value:
'value1'
)
}
let
(
:high_priority_signature
)
{
::
Gitlab
::
Ci
::
Reports
::
Security
::
FindingSignature
.
new
(
algorithm_type:
'scope_offset'
,
signature_value:
'value2'
)
}
it
'returns the expected hash with no signatures'
do
expect
(
finding
.
signatures
.
length
).
to
eq
(
0
)
expect
(
finding
.
hash
).
to
eq
(
finding
.
report_type
.
hash
^
finding
.
location
.
fingerprint
.
hash
^
finding
.
primary_identifier_fingerprint
.
hash
)
end
it
'returns the expected hash with signatures'
do
finding
.
signatures
<<
low_priority_signature
finding
.
signatures
<<
high_priority_signature
expect
(
finding
.
signatures
.
length
).
to
eq
(
2
)
expect
(
finding
.
hash
).
to
eq
(
finding
.
report_type
.
hash
^
high_priority_signature
.
signature_hex
.
hash
^
finding
.
primary_identifier_fingerprint
.
hash
)
end
end
context
'without vulnerability_finding_signatures enabled'
do
let
(
:finding
)
do
build
(
:ci_reports_security_finding
,
scanner:
scanner
,
identifiers:
identifiers
,
location:
location
,
uuid:
uuid
,
compare_key:
''
,
vulnerability_finding_signatures_enabled:
false
)
end
it
'returns the expected hash'
do
expect
(
finding
.
hash
).
to
eq
(
finding
.
report_type
.
hash
^
finding
.
location
.
fingerprint
.
hash
^
finding
.
primary_identifier_fingerprint
.
hash
)
end
end
end
end
end
ee/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
View file @
56fcaba4
...
@@ -8,16 +8,20 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
...
@@ -8,16 +8,20 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
let
(
:base_vulnerability
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
'123'
,
confidence:
::
Enums
::
Vulnerability
.
confidence_levels
[
:high
],
severity:
::
Enums
::
Vulnerability
.
severity_levels
[
:critical
])
}
let
(
:base_vulnerability
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
'123'
,
confidence:
::
Enums
::
Vulnerability
.
confidence_levels
[
:high
],
severity:
::
Enums
::
Vulnerability
.
severity_levels
[
:critical
])
}
let
(
:base_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
findings:
[
base_vulnerability
])}
let
(
:base_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
findings:
[
base_vulnerability
])}
let
(
:head_vulnerability
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
'123'
,
confidence:
::
Enums
::
Vulnerability
.
confidence_levels
[
:high
],
severity:
::
Enums
::
Vulnerability
.
severity_levels
[
:critical
]
)
}
let
(
:head_vulnerability
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
base_vulnerability
.
location_fingerprint
,
confidence:
::
Enums
::
Vulnerability
.
confidence_levels
[
:high
],
severity:
::
Enums
::
Vulnerability
.
severity_levels
[
:critical
],
uuid:
base_vulnerability
.
uuid
)
}
let
(
:head_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
findings:
[
head_vulnerability
])}
let
(
:head_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
findings:
[
head_vulnerability
])}
subject
{
described_class
.
new
(
base_report
,
head_report
)
}
where
(
vulnerability_finding_signatures_enabled:
[
true
,
false
])
with_them
do
before
do
before
do
allow
(
base_vulnerability
).
to
receive
(
:location
).
and_return
({})
allow
(
base_vulnerability
).
to
receive
(
:location
).
and_return
({})
allow
(
head_vulnerability
).
to
receive
(
:location
).
and_return
({})
allow
(
head_vulnerability
).
to
receive
(
:location
).
and_return
({})
stub_feature_flags
(
vulnerability_finding_signatures:
vulnerability_finding_signatures_enabled
)
end
end
subject
{
described_class
.
new
(
base_report
,
head_report
)
}
describe
'#base_report_out_of_date'
do
describe
'#base_report_out_of_date'
do
context
'no base report'
do
context
'no base report'
do
let
(
:base_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
reports:
[],
findings:
[])}
let
(
:base_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
reports:
[],
findings:
[])}
...
@@ -79,7 +83,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
...
@@ -79,7 +83,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
describe
'#fixed'
do
describe
'#fixed'
do
let
(
:vuln
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
'888'
)
}
let
(
:vuln
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
'888'
)
}
let
(
:medium_vuln
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
'888'
,
confidence:
::
Enums
::
Vulnerability
.
confidence_levels
[
:high
],
severity:
Enums
::
Vulnerability
.
severity_levels
[
:medium
]
)
}
let
(
:medium_vuln
)
{
build
(
:vulnerabilities_finding
,
report_type: :sast
,
identifiers:
[
identifier
],
location_fingerprint:
'888'
,
confidence:
::
Enums
::
Vulnerability
.
confidence_levels
[
:high
],
severity:
Enums
::
Vulnerability
.
severity_levels
[
:medium
],
uuid:
vuln
.
uuid
)
}
context
'with fixed vulnerability'
do
context
'with fixed vulnerability'
do
let
(
:base_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
findings:
[
base_vulnerability
,
vuln
])}
let
(
:base_report
)
{
build
(
:ci_reports_security_aggregated_reports
,
findings:
[
base_vulnerability
,
vuln
])}
...
@@ -132,4 +136,5 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
...
@@ -132,4 +136,5 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
expect
(
comparer
.
added
).
to
eq
([])
expect
(
comparer
.
added
).
to
eq
([])
end
end
end
end
end
end
end
ee/spec/models/concerns/vulnerability_finding_signature_helpers_spec.rb
0 → 100644
View file @
56fcaba4
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
VulnerabilityFindingSignatureHelpers
do
let
(
:cls
)
do
Class
.
new
do
include
VulnerabilityFindingSignatureHelpers
attr_accessor
:algorithm_type
def
initialize
(
algorithm_type
)
@algorithm_type
=
algorithm_type
end
end
end
describe
'#priority'
do
it
'returns numeric values of the priority string'
do
expect
(
cls
.
new
(
'scope_offset'
).
priority
).
to
eq
(
3
)
expect
(
cls
.
new
(
'location'
).
priority
).
to
eq
(
2
)
expect
(
cls
.
new
(
'hash'
).
priority
).
to
eq
(
1
)
end
end
describe
'#self.priority'
do
it
'returns the numeric value of the provided string'
do
expect
(
cls
.
priority
(
'scope_offset'
)).
to
eq
(
3
)
expect
(
cls
.
priority
(
'location'
)).
to
eq
(
2
)
expect
(
cls
.
priority
(
'hash'
)).
to
eq
(
1
)
end
end
end
ee/spec/models/vulnerabilities/finding_spec.rb
View file @
56fcaba4
...
@@ -7,6 +7,12 @@ RSpec.describe Vulnerabilities::Finding do
...
@@ -7,6 +7,12 @@ RSpec.describe Vulnerabilities::Finding do
it
{
is_expected
.
to
define_enum_for
(
:report_type
)
}
it
{
is_expected
.
to
define_enum_for
(
:report_type
)
}
it
{
is_expected
.
to
define_enum_for
(
:severity
)
}
it
{
is_expected
.
to
define_enum_for
(
:severity
)
}
where
(
vulnerability_finding_signatures_enabled:
[
true
,
false
])
with_them
do
before
do
stub_feature_flags
(
vulnerability_finding_signatures:
vulnerability_finding_signatures_enabled
)
end
describe
'associations'
do
describe
'associations'
do
it
{
is_expected
.
to
belong_to
(
:project
)
}
it
{
is_expected
.
to
belong_to
(
:project
)
}
it
{
is_expected
.
to
belong_to
(
:primary_identifier
).
class_name
(
'Vulnerabilities::Identifier'
)
}
it
{
is_expected
.
to
belong_to
(
:primary_identifier
).
class_name
(
'Vulnerabilities::Identifier'
)
}
...
@@ -63,7 +69,7 @@ RSpec.describe Vulnerabilities::Finding do
...
@@ -63,7 +69,7 @@ RSpec.describe Vulnerabilities::Finding do
context
'database uniqueness'
do
context
'database uniqueness'
do
let
(
:finding
)
{
create
(
:vulnerabilities_finding
)
}
let
(
:finding
)
{
create
(
:vulnerabilities_finding
)
}
let
(
:new_finding
)
{
finding
.
dup
.
tap
{
|
o
|
o
.
uuid
=
SecureRandom
.
uuid
}
}
let
(
:new_finding
)
{
finding
.
dup
.
tap
{
|
o
|
o
.
cve
=
SecureRandom
.
uuid
}
}
it
"when all index attributes are identical"
do
it
"when all index attributes are identical"
do
expect
{
new_finding
.
save!
}.
to
raise_error
(
ActiveRecord
::
RecordNotUnique
)
expect
{
new_finding
.
save!
}.
to
raise_error
(
ActiveRecord
::
RecordNotUnique
)
...
@@ -81,7 +87,7 @@ RSpec.describe Vulnerabilities::Finding do
...
@@ -81,7 +87,7 @@ RSpec.describe Vulnerabilities::Finding do
with_them
do
with_them
do
it
"is valid"
do
it
"is valid"
do
expect
{
new_finding
.
update!
({
key
=>
create
(
factory_name
)
})
}.
not_to
raise_error
expect
{
new_finding
.
update!
({
key
=>
create
(
factory_name
),
'uuid'
=>
SecureRandom
.
uuid
})
}.
not_to
raise_error
end
end
end
end
end
end
...
@@ -673,7 +679,6 @@ RSpec.describe Vulnerabilities::Finding do
...
@@ -673,7 +679,6 @@ RSpec.describe Vulnerabilities::Finding do
let
(
:confirmed_finding
)
{
create
(
:vulnerabilities_finding
,
:confirmed
)
}
let
(
:confirmed_finding
)
{
create
(
:vulnerabilities_finding
,
:confirmed
)
}
let
(
:resolved_finding
)
{
create
(
:vulnerabilities_finding
,
:resolved
)
}
let
(
:resolved_finding
)
{
create
(
:vulnerabilities_finding
,
:resolved
)
}
let
(
:dismissed_finding
)
{
create
(
:vulnerabilities_finding
,
:dismissed
)
}
let
(
:dismissed_finding
)
{
create
(
:vulnerabilities_finding
,
:dismissed
)
}
let
(
:detected_finding
)
{
create
(
:vulnerabilities_finding
,
:detected
)
}
let
(
:finding_with_issue
)
{
create
(
:vulnerabilities_finding
,
:with_issue_feedback
)
}
let
(
:finding_with_issue
)
{
create
(
:vulnerabilities_finding
,
:with_issue_feedback
)
}
it
'returns the expected state for a unresolved finding'
do
it
'returns the expected state for a unresolved finding'
do
...
@@ -692,10 +697,6 @@ RSpec.describe Vulnerabilities::Finding do
...
@@ -692,10 +697,6 @@ RSpec.describe Vulnerabilities::Finding do
expect
(
dismissed_finding
.
state
).
to
eq
'dismissed'
expect
(
dismissed_finding
.
state
).
to
eq
'dismissed'
end
end
it
'returns the expected state for a detected finding'
do
expect
(
detected_finding
.
state
).
to
eq
'detected'
end
context
'when a vulnerability present for a dismissed finding'
do
context
'when a vulnerability present for a dismissed finding'
do
before
do
before
do
create
(
:vulnerability
,
project:
dismissed_finding
.
project
,
findings:
[
dismissed_finding
])
create
(
:vulnerability
,
project:
dismissed_finding
.
project
,
findings:
[
dismissed_finding
])
...
@@ -1010,4 +1011,117 @@ RSpec.describe Vulnerabilities::Finding do
...
@@ -1010,4 +1011,117 @@ RSpec.describe Vulnerabilities::Finding do
end
end
end
end
end
end
describe
'#eql?'
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:report_type
)
{
:sast
}
let
(
:identifier_fingerprint
)
{
'fooo'
}
let
(
:identifier
)
{
build
(
:vulnerabilities_identifier
,
fingerprint:
identifier_fingerprint
)
}
let
(
:location_fingerprint1
)
{
'fingerprint1'
}
let
(
:location_fingerprint2
)
{
'fingerprint2'
}
let
(
:finding1
)
do
build
(
:vulnerabilities_finding
,
report_type
,
project:
project
,
primary_identifier:
identifier
,
location_fingerprint:
location_fingerprint1
)
end
let
(
:finding2
)
do
build
(
:vulnerabilities_finding
,
report_type
,
project:
project
,
primary_identifier:
identifier
,
location_fingerprint:
location_fingerprint2
)
end
it
'matches the finding based on enabled tracking methods (if feature flag enabled)'
do
signature1
=
create
(
:vulnerabilities_finding_signature
,
finding:
finding1
)
signature2
=
create
(
:vulnerabilities_finding_signature
,
finding:
finding2
,
signature_sha:
signature1
.
signature_sha
)
# verify that the signatures do exist and that they match
expect
(
finding1
.
signatures
.
size
).
to
eq
(
1
)
expect
(
finding2
.
signatures
.
size
).
to
eq
(
1
)
expect
(
signature1
.
eql?
(
signature2
)).
to
be
(
true
)
# now verify that the correct matching method was used for eql?
expect
(
finding1
.
eql?
(
finding2
)).
to
be
(
vulnerability_finding_signatures_enabled
)
end
context
'short circuits on the highest priority signature match'
do
using
RSpec
::
Parameterized
::
TableSyntax
let
(
:same_hash
)
{
false
}
let
(
:same_location
)
{
false
}
let
(
:create_scope_offset
)
{
false
}
let
(
:same_scope_offset
)
{
false
}
let
(
:create_signatures
)
do
signature1_hash
=
create
(
:vulnerabilities_finding_signature
,
algorithm_type:
'hash'
,
finding:
finding1
)
sha
=
same_hash
?
signature1_hash
.
signature_sha
:
::
Digest
::
SHA1
.
digest
(
SecureRandom
.
hex
(
50
))
create
(
:vulnerabilities_finding_signature
,
algorithm_type:
'hash'
,
finding:
finding2
,
signature_sha:
sha
)
signature1_location
=
create
(
:vulnerabilities_finding_signature
,
algorithm_type:
'location'
,
finding:
finding1
)
sha
=
same_location
?
signature1_location
.
signature_sha
:
::
Digest
::
SHA1
.
digest
(
SecureRandom
.
hex
(
50
))
create
(
:vulnerabilities_finding_signature
,
algorithm_type:
'location'
,
finding:
finding2
,
signature_sha:
sha
)
signature1_scope_offset
=
create
(
:vulnerabilities_finding_signature
,
algorithm_type:
'scope_offset'
,
finding:
finding1
)
if
create_scope_offset
sha
=
same_scope_offset
?
signature1_scope_offset
.
signature_sha
:
::
Digest
::
SHA1
.
digest
(
SecureRandom
.
hex
(
50
))
create
(
:vulnerabilities_finding_signature
,
algorithm_type:
'scope_offset'
,
finding:
finding2
,
signature_sha:
sha
)
end
end
where
(
:same_hash
,
:same_location
,
:create_scope_offset
,
:same_scope_offset
,
:should_match
)
do
true
|
true
|
true
|
true
|
true
# everything matches
false
|
false
|
true
|
false
|
false
# nothing matches
true
|
true
|
true
|
false
|
false
# highest priority matches alg/priority but not on value
false
|
false
|
true
|
true
|
true
# highest priority matches alg/priority and value
false
|
true
|
false
|
false
|
true
# highest priority is location, matches alg/priority and value
end
with_them
do
it
'matches correctly'
do
next
unless
vulnerability_finding_signatures_enabled
create_signatures
expect
(
finding1
.
eql?
(
finding2
)).
to
be
(
should_match
)
end
end
end
end
end
end
end
ee/spec/requests/api/vulnerability_findings_spec.rb
View file @
56fcaba4
...
@@ -33,7 +33,8 @@ RSpec.describe API::VulnerabilityFindings do
...
@@ -33,7 +33,8 @@ RSpec.describe API::VulnerabilityFindings do
project:
project
,
project:
project
,
pipeline:
pipeline
,
pipeline:
pipeline
,
project_fingerprint:
sast_report
.
findings
.
first
.
project_fingerprint
,
project_fingerprint:
sast_report
.
findings
.
first
.
project_fingerprint
,
vulnerability_data:
sast_report
.
findings
.
first
.
raw_metadata
vulnerability_data:
sast_report
.
findings
.
first
.
raw_metadata
,
finding_uuid:
sast_report
.
findings
.
first
.
uuid
)
)
end
end
...
...
ee/spec/serializers/vulnerabilities/feedback_entity_spec.rb
View file @
56fcaba4
...
@@ -164,7 +164,7 @@ RSpec.describe Vulnerabilities::FeedbackEntity do
...
@@ -164,7 +164,7 @@ RSpec.describe Vulnerabilities::FeedbackEntity do
end
end
context
'when finding_uuid is not present'
do
context
'when finding_uuid is not present'
do
let
(
:feedback
)
{
build_stubbed
(
:vulnerability_feedback
,
:issue
,
project:
project
)
}
let
(
:feedback
)
{
build_stubbed
(
:vulnerability_feedback
,
:issue
,
project:
project
,
finding_uuid:
nil
)
}
it
'has a nil finding_uuid'
do
it
'has a nil finding_uuid'
do
expect
(
subject
[
:finding_uuid
]).
to
be_nil
expect
(
subject
[
:finding_uuid
]).
to
be_nil
...
...
ee/spec/services/ci/compare_security_reports_service_spec.rb
View file @
56fcaba4
...
@@ -11,6 +11,12 @@ RSpec.describe Ci::CompareSecurityReportsService do
...
@@ -11,6 +11,12 @@ RSpec.describe Ci::CompareSecurityReportsService do
collection
.
map
{
|
t
|
t
[
'identifiers'
].
first
[
'external_id'
]
}
collection
.
map
{
|
t
|
t
[
'identifiers'
].
first
[
'external_id'
]
}
end
end
where
(
vulnerability_finding_signatures_enabled:
[
true
,
false
])
with_them
do
before
do
stub_feature_flags
(
vulnerability_finding_signatures:
vulnerability_finding_signatures_enabled
)
end
describe
'#execute DS'
do
describe
'#execute DS'
do
before
do
before
do
stub_licensed_features
(
dependency_scanning:
true
)
stub_licensed_features
(
dependency_scanning:
true
)
...
@@ -273,4 +279,5 @@ RSpec.describe Ci::CompareSecurityReportsService do
...
@@ -273,4 +279,5 @@ RSpec.describe Ci::CompareSecurityReportsService do
end
end
end
end
end
end
end
end
end
ee/spec/services/security/store_report_service_spec.rb
View file @
56fcaba4
...
@@ -3,6 +3,8 @@
...
@@ -3,6 +3,8 @@
require
'spec_helper'
require
'spec_helper'
RSpec
.
describe
Security
::
StoreReportService
,
'#execute'
do
RSpec
.
describe
Security
::
StoreReportService
,
'#execute'
do
using
RSpec
::
Parameterized
::
TableSyntax
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:user
)
{
create
(
:user
)
}
let
(
:artifact
)
{
create
(
:ee_ci_job_artifact
,
trait
)
}
let
(
:artifact
)
{
create
(
:ee_ci_job_artifact
,
trait
)
}
...
@@ -11,13 +13,16 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -11,13 +13,16 @@ RSpec.describe Security::StoreReportService, '#execute' do
let
(
:pipeline
)
{
artifact
.
job
.
pipeline
}
let
(
:pipeline
)
{
artifact
.
job
.
pipeline
}
let
(
:report
)
{
pipeline
.
security_reports
.
get_report
(
report_type
.
to_s
,
artifact
)
}
let
(
:report
)
{
pipeline
.
security_reports
.
get_report
(
report_type
.
to_s
,
artifact
)
}
subject
{
described_class
.
new
(
pipeline
,
report
).
execute
}
where
(
vulnerability_finding_signatures_enabled:
[
true
,
false
])
with_them
do
before
do
before
do
stub_feature_flags
(
vulnerability_finding_signatures:
vulnerability_finding_signatures_enabled
)
stub_licensed_features
(
sast:
true
,
dependency_scanning:
true
,
container_scanning:
true
,
security_dashboard:
true
)
stub_licensed_features
(
sast:
true
,
dependency_scanning:
true
,
container_scanning:
true
,
security_dashboard:
true
)
allow
(
Security
::
AutoFixWorker
).
to
receive
(
:perform_async
)
allow
(
Security
::
AutoFixWorker
).
to
receive
(
:perform_async
)
end
end
subject
{
described_class
.
new
(
pipeline
,
report
).
execute
}
context
'without existing data'
do
context
'without existing data'
do
before
(
:all
)
do
before
(
:all
)
do
checksum
=
'f00bc6261fa512f0960b7fc3bfcce7fb31997cf32b96fa647bed5668b2c77fee'
checksum
=
'f00bc6261fa512f0960b7fc3bfcce7fb31997cf32b96fa647bed5668b2c77fee'
...
@@ -30,24 +35,18 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -30,24 +35,18 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
end
context
'for different security reports'
do
context
'for different security reports'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
(
:case_name
,
:trait
,
:scanners
,
:identifiers
,
:findings
,
:finding_identifiers
,
:finding_pipelines
,
:remediations
,
:signatures
,
:optimize_sql_query_for_security_report_ff
)
do
'with SAST report'
|
:sast
|
1
|
6
|
5
|
7
|
5
|
0
|
2
|
false
'with exceeding identifiers'
|
:with_exceeding_identifiers
|
1
|
20
|
1
|
20
|
1
|
0
|
0
|
false
'with Dependency Scanning report'
|
:dependency_scanning_remediation
|
1
|
3
|
2
|
3
|
2
|
1
|
0
|
false
'with Container Scanning report'
|
:container_scanning
|
1
|
8
|
8
|
8
|
8
|
0
|
0
|
false
'with SAST report'
|
:sast
|
1
|
6
|
5
|
7
|
5
|
0
|
2
|
true
'with exceeding identifiers'
|
:with_exceeding_identifiers
|
1
|
20
|
1
|
20
|
1
|
0
|
0
|
true
'with Dependency Scanning report'
|
:dependency_scanning_remediation
|
1
|
3
|
2
|
3
|
2
|
1
|
0
|
true
'with Container Scanning report'
|
:container_scanning
|
1
|
8
|
8
|
8
|
8
|
0
|
0
|
true
end
with_them
do
with_them
do
before
do
before
do
stub_feature_flags
(
optimize_sql_query_for_security_report:
optimize_sql_query_for_security_report_ff
)
stub_feature_flags
(
optimize_sql_query_for_security_report:
optimize_sql_query_for_security_report_ff
)
end
end
where
(
:case_name
,
:trait
,
:scanners
,
:identifiers
,
:findings
,
:finding_identifiers
,
:finding_pipelines
,
:remediations
,
:signatures
)
do
'with SAST report'
|
:sast
|
1
|
6
|
5
|
7
|
5
|
0
|
2
'with exceeding identifiers'
|
:with_exceeding_identifiers
|
1
|
20
|
1
|
20
|
1
|
0
|
1
'with Dependency Scanning report'
|
:dependency_scanning_remediation
|
1
|
3
|
2
|
3
|
2
|
1
|
2
'with Container Scanning report'
|
:container_scanning
|
1
|
8
|
8
|
8
|
8
|
0
|
8
end
it
'inserts all scanners'
do
it
'inserts all scanners'
do
expect
{
subject
}.
to
change
{
Vulnerabilities
::
Scanner
.
count
}.
by
(
scanners
)
expect
{
subject
}.
to
change
{
Vulnerabilities
::
Scanner
.
count
}.
by
(
scanners
)
end
end
...
@@ -164,11 +163,6 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -164,11 +163,6 @@ RSpec.describe Security::StoreReportService, '#execute' do
let
(
:new_pipeline
)
{
create
(
:ci_pipeline
,
project:
project
)
}
let
(
:new_pipeline
)
{
create
(
:ci_pipeline
,
project:
project
)
}
let
(
:new_report
)
{
new_pipeline
.
security_reports
.
get_report
(
report_type
.
to_s
,
artifact
)
}
let
(
:new_report
)
{
new_pipeline
.
security_reports
.
get_report
(
report_type
.
to_s
,
artifact
)
}
let
(
:existing_signature
)
{
create
(
:vulnerabilities_finding_signature
,
finding:
finding
)
}
let
(
:existing_signature
)
{
create
(
:vulnerabilities_finding_signature
,
finding:
finding
)
}
let
(
:unsupported_signature
)
do
create
(
:vulnerabilities_finding_signature
,
finding:
finding
,
algorithm_type:
::
Vulnerabilities
::
FindingSignature
.
algorithm_types
[
:location
])
end
let
(
:trait
)
{
:sast
}
let
(
:trait
)
{
:sast
}
...
@@ -182,7 +176,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -182,7 +176,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
end
let!
(
:finding
)
do
let!
(
:finding
)
do
create
(
:vulnerabilities_finding
,
created_finding
=
create
(
:vulnerabilities_finding
,
pipelines:
[
pipeline
],
pipelines:
[
pipeline
],
identifiers:
[
identifier
],
identifiers:
[
identifier
],
primary_identifier:
identifier
,
primary_identifier:
identifier
,
...
@@ -190,6 +184,15 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -190,6 +184,15 @@ RSpec.describe Security::StoreReportService, '#execute' do
project:
project
,
project:
project
,
uuid:
"e5388f40-18f5-566d-95c6-d64c6f46a00a"
,
uuid:
"e5388f40-18f5-566d-95c6-d64c6f46a00a"
,
location_fingerprint:
finding_location_fingerprint
)
location_fingerprint:
finding_location_fingerprint
)
existing_finding
=
report
.
findings
.
find
{
|
f
|
f
.
location
.
fingerprint
==
created_finding
.
location_fingerprint
}
create
(
:vulnerabilities_finding_signature
,
finding:
created_finding
,
algorithm_type:
existing_finding
.
signatures
.
first
.
algorithm_type
,
signature_sha:
existing_finding
.
signatures
.
first
.
signature_sha
)
created_finding
end
end
let!
(
:vulnerability
)
{
create
(
:vulnerability
,
findings:
[
finding
],
project:
project
)
}
let!
(
:vulnerability
)
{
create
(
:vulnerability
,
findings:
[
finding
],
project:
project
)
}
...
@@ -227,6 +230,14 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -227,6 +230,14 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
end
it
'updates UUIDv4 to UUIDv5'
do
it
'updates UUIDv4 to UUIDv5'
do
finding
.
uuid
=
'00000000-1111-2222-3333-444444444444'
finding
.
save!
# this report_finding should be used to update the finding's uuid
report_finding
=
new_report
.
findings
.
find
{
|
f
|
f
.
location
.
fingerprint
==
'0e7d0291d912f56880e39d4fbd80d99dd5d327ba'
}
allow
(
report_finding
).
to
receive
(
:uuid
).
and_return
(
desired_uuid
)
report_finding
.
signatures
.
pop
subject
subject
expect
(
finding
.
reload
.
uuid
).
to
eq
(
desired_uuid
)
expect
(
finding
.
reload
.
uuid
).
to
eq
(
desired_uuid
)
...
@@ -259,28 +270,24 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -259,28 +270,24 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
end
it
'updates signatures to match new values'
do
it
'updates signatures to match new values'
do
existing_signature
next
unless
vulnerability_finding_signatures_enabled
unsupported_signature
expect
(
finding
.
signatures
.
count
).
to
eq
(
2
)
expect
(
finding
.
signatures
.
count
).
to
eq
(
1
)
signature_algs
=
finding
.
signatures
.
map
(
&
:algorithm_type
).
sort
expect
(
finding
.
signatures
.
first
.
algorithm_type
).
to
eq
(
'hash'
)
expect
(
signature_algs
).
to
eq
(
%w[hash location]
)
existing_signature
=
finding
.
signatures
.
first
subject
subject
finding
.
reload
finding
.
reload
existing_signature
.
reload
existing_signature
.
reload
# check that unsupported algorithm is not deleted
expect
(
finding
.
signatures
.
count
).
to
eq
(
2
)
expect
(
finding
.
signatures
.
count
).
to
eq
(
3
)
signature_algs
=
finding
.
signatures
.
sort_by
(
&
:priority
).
map
(
&
:algorithm_type
)
signature_algs
=
finding
.
signatures
.
sort
.
map
(
&
:algorithm_type
)
expect
(
signature_algs
).
to
eq
(
%w[hash scope_offset]
)
expect
(
signature_algs
).
to
eq
(
%w[hash location scope_offset]
)
# check that the existing hash signature was updated/reused
# check that the existing hash signature was updated/reused
expect
(
existing_signature
.
id
).
to
eq
(
finding
.
signatures
.
min
.
id
)
expect
(
existing_signature
.
id
).
to
eq
(
finding
.
signatures
.
find
(
&
:algorithm_hash?
).
id
)
# check that the unsupported signature was not deleted
expect
(
::
Vulnerabilities
::
FindingSignature
.
exists?
(
unsupported_signature
.
id
)).
to
eq
(
true
)
end
end
it
'updates existing vulnerability with new data'
do
it
'updates existing vulnerability with new data'
do
...
@@ -299,7 +306,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -299,7 +306,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
context
'when the existing resolved vulnerability is discovered again on the latest report'
do
context
'when the existing resolved vulnerability is discovered again on the latest report'
do
before
do
before
do
vulnerability
.
update!
(
resolved_on_default_branch:
true
)
vulnerability
.
update_column
(
:resolved_on_default_branch
,
true
)
end
end
it
'marks the vulnerability as not resolved on default branch'
do
it
'marks the vulnerability as not resolved on default branch'
do
...
@@ -331,7 +338,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -331,7 +338,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
end
context
'vulnerability issue link'
do
context
'vulnerability issue link'
do
context
'when there is no asso
ciated issue feedback with finding'
do
context
'when there is no assoi
ciated issue feedback with finding'
do
it
'does not insert issue links from the new pipeline'
do
it
'does not insert issue links from the new pipeline'
do
expect
{
subject
}.
to
change
{
Vulnerabilities
::
IssueLink
.
count
}.
by
(
0
)
expect
{
subject
}.
to
change
{
Vulnerabilities
::
IssueLink
.
count
}.
by
(
0
)
end
end
...
@@ -414,7 +421,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -414,7 +421,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
context
'when auto fix feature is disabled'
do
context
'when auto fix feature is disabled'
do
before
do
before
do
project
.
security_setting
.
update!
(
auto_fix_dependency_scanning:
false
)
project
.
security_setting
.
update_column
(
:auto_fix_dependency_scanning
,
false
)
end
end
it
'does not start auto fix worker'
do
it
'does not start auto fix worker'
do
...
@@ -464,4 +471,5 @@ RSpec.describe Security::StoreReportService, '#execute' do
...
@@ -464,4 +471,5 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
end
end
end
end
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