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
49e788b9
Commit
49e788b9
authored
Sep 09, 2021
by
Balasankar 'Balu' C
Committed by
Mark Chao
Sep 09, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[RUN AS-IF-FOSS] Add option to send slack notifications when a new vulnerability is detected
parent
cc136978
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
397 additions
and
15 deletions
+397
-15
app/models/integrations/base_chat_notification.rb
app/models/integrations/base_chat_notification.rb
+2
-0
db/migrate/20210707163659_add_vulnerability_events_to_integrations.rb
...0210707163659_add_vulnerability_events_to_integrations.rb
+9
-0
db/schema_migrations/20210707163659
db/schema_migrations/20210707163659
+1
-0
db/structure.sql
db/structure.sql
+1
-0
doc/api/services.md
doc/api/services.md
+4
-0
doc/user/project/integrations/slack.md
doc/user/project/integrations/slack.md
+14
-13
ee/app/helpers/ee/integrations_helper.rb
ee/app/helpers/ee/integrations_helper.rb
+7
-0
ee/app/models/ee/integration.rb
ee/app/models/ee/integration.rb
+4
-0
ee/app/models/ee/integrations/base_chat_notification.rb
ee/app/models/ee/integrations/base_chat_notification.rb
+30
-0
ee/app/models/ee/vulnerability.rb
ee/app/models/ee/vulnerability.rb
+8
-0
ee/app/models/integrations/chat_message/vulnerability_message.rb
...models/integrations/chat_message/vulnerability_message.rb
+67
-0
ee/app/services/security/store_report_service.rb
ee/app/services/security/store_report_service.rb
+10
-2
ee/lib/ee/api/helpers/integrations_helpers.rb
ee/lib/ee/api/helpers/integrations_helpers.rb
+26
-0
ee/lib/gitlab/data_builder/vulnerability.rb
ee/lib/gitlab/data_builder/vulnerability.rb
+45
-0
ee/spec/lib/gitlab/data_builder/vulnerability_spec.rb
ee/spec/lib/gitlab/data_builder/vulnerability_spec.rb
+74
-0
ee/spec/models/ee/integration_spec.rb
ee/spec/models/ee/integration_spec.rb
+14
-0
ee/spec/models/integrations/chat_message/vulnerability_message_spec.rb
...s/integrations/chat_message/vulnerability_message_spec.rb
+70
-0
ee/spec/services/security/store_report_service_spec.rb
ee/spec/services/security/store_report_service_spec.rb
+8
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
No files found.
app/models/integrations/base_chat_notification.rb
View file @
49e788b9
...
...
@@ -253,3 +253,5 @@ module Integrations
end
end
end
Integrations
::
BaseChatNotification
.
prepend_mod_with
(
'Integrations::BaseChatNotification'
)
db/migrate/20210707163659_add_vulnerability_events_to_integrations.rb
0 → 100644
View file @
49e788b9
# frozen_string_literal: true
class
AddVulnerabilityEventsToIntegrations
<
ActiveRecord
::
Migration
[
6.1
]
include
Gitlab
::
Database
::
MigrationHelpers
def
change
add_column
:integrations
,
:vulnerability_events
,
:boolean
,
default:
false
,
null:
false
end
end
db/schema_migrations/20210707163659
0 → 100644
View file @
49e788b9
ac14aa49830a3af9a1445c0c7680f5660247a8104c8e4c1ae542c4b368f7c9bf
\ No newline at end of file
db/structure.sql
View file @
49e788b9
...
...
@@ -14968,6 +14968,7 @@ CREATE TABLE integrations (
alert_events boolean,
group_id bigint,
type_new text,
vulnerability_events boolean DEFAULT false NOT NULL,
CONSTRAINT check_a948a0aa7e CHECK ((char_length(type_new) <= 255))
);
doc/api/services.md
View file @
49e788b9
...
...
@@ -1153,6 +1153,8 @@ Parameters:
|
`tag_push_events`
| boolean | false | Enable notifications for tag push events |
|
`wiki_page_channel`
| string | false | The name of the channel to receive wiki page events notifications |
|
`wiki_page_events`
| boolean | false | Enable notifications for wiki page events |
|
`vulnerability_channel`
| string | false |
**(ULTIMATE)**
The name of the channel to receive vulnerability event notifications. |
|
`vulnerability_events`
| boolean | false |
**(ULTIMATE)**
Enable notifications for vulnerability events |
### Delete Slack service
...
...
@@ -1250,6 +1252,7 @@ Parameters:
|
`confidential_note_events`
| boolean | false | Enable notifications for confidential note events |
|
`pipeline_events`
| boolean | false | Enable notifications for pipeline events |
|
`wiki_page_events`
| boolean | false | Enable notifications for wiki page events |
|
`vulnerability_events`
| boolean | false |
**(ULTIMATE)**
Enable notifications for vulnerability events |
|
`push_channel`
| string | false | The name of the channel to receive push events notifications |
|
`issue_channel`
| string | false | The name of the channel to receive issues events notifications |
|
`confidential_issue_channel`
| string | false | The name of the channel to receive confidential issues events notifications |
...
...
@@ -1259,6 +1262,7 @@ Parameters:
|
`tag_push_channel`
| string | false | The name of the channel to receive tag push events notifications |
|
`pipeline_channel`
| string | false | The name of the channel to receive pipeline events notifications |
|
`wiki_page_channel`
| string | false | The name of the channel to receive wiki page events notifications |
|
`vulnerability_channel`
| string | false |
**(ULTIMATE)**
The name of the channel to receive vulnerability events notifications |
### Delete Mattermost notifications service
...
...
doc/user/project/integrations/slack.md
View file @
49e788b9
...
...
@@ -59,19 +59,20 @@ Your Slack team now starts receiving GitLab event notifications as configured.
The following triggers are available for Slack notifications:
| Trigger name | Trigger event |
|------------------------|------------------------------------------------------|
|
**Push**
| A push to the repository. |
|
**Issue**
| An issue is created, updated, or closed. |
|
**Confidential issue**
| A confidential issue is created, updated, or closed. |
|
**Merge request**
| A merge request is created, updated, or merged. |
|
**Note**
| A comment is added. |
|
**Confidential note**
| A confidential note is added. |
|
**Tag push**
| A new tag is pushed to the repository. |
|
**Pipeline**
| A pipeline status changed. |
|
**Wiki page**
| A wiki page is created or updated. |
|
**Deployment**
| A deployment starts or finishes. |
|
**Alert**
| A new, unique alert is recorded. |
| Trigger name | Trigger event |
| ------------------------ | ------------------------------------------------------ |
|
**Push**
| A push to the repository. |
|
**Issue**
| An issue is created, updated, or closed. |
|
**Confidential issue**
| A confidential issue is created, updated, or closed. |
|
**Merge request**
| A merge request is created, updated, or merged. |
|
**Note**
| A comment is added. |
|
**Confidential note**
| A confidential note is added. |
|
**Tag push**
| A new tag is pushed to the repository. |
|
**Pipeline**
| A pipeline status changed. |
|
**Wiki page**
| A wiki page is created or updated. |
|
**Deployment**
| A deployment starts or finishes. |
|
**Alert**
| A new, unique alert is recorded. |
|
**Vulnerability**
|
**(ULTIMATE)**
A new, unique vulnerability is recorded. |
## Troubleshooting
...
...
ee/app/helpers/ee/integrations_helper.rb
View file @
49e788b9
...
...
@@ -58,5 +58,12 @@ module EE
issues_list_path:
project_integrations_jira_issues_path
(
@project
)
}
end
override
:default_integration_event_description
def
default_integration_event_description
(
event
)
return
s_
(
"ProjectService|Trigger event when a new, unique vulnerability is recorded. (Note: This feature requires an Ultimate plan.)"
)
if
event
==
'vulnerability'
super
end
end
end
ee/app/models/ee/integration.rb
View file @
49e788b9
...
...
@@ -4,6 +4,10 @@ module EE
module
Integration
extend
ActiveSupport
::
Concern
prepended
do
scope
:vulnerability_hooks
,
->
{
where
(
vulnerability_events:
true
,
active:
true
)
}
end
EE_COM_PROJECT_SPECIFIC_INTEGRATION_NAMES
=
%w[
gitlab_slack_application
]
.
freeze
...
...
ee/app/models/ee/integrations/base_chat_notification.rb
0 → 100644
View file @
49e788b9
# frozen_string_literal: true
module
EE
module
Integrations
module
BaseChatNotification
extend
ActiveSupport
::
Concern
extend
::
Gitlab
::
Utils
::
Override
EE_SUPPORTED_EVENTS
=
%w[vulnerability]
.
freeze
::
Integration
.
prop_accessor
(
*
EE_SUPPORTED_EVENTS
.
map
{
|
event
|
"
#{
event
}
_channel"
})
override
:get_message
def
get_message
(
object_kind
,
data
)
return
::
Integrations
::
ChatMessage
::
VulnerabilityMessage
.
new
(
data
)
if
object_kind
==
'vulnerability'
super
end
class_methods
do
extend
::
Gitlab
::
Utils
::
Override
override
:supported_events
def
supported_events
super
+
EE_SUPPORTED_EVENTS
end
end
end
end
end
ee/app/models/ee/vulnerability.rb
View file @
49e788b9
...
...
@@ -172,8 +172,16 @@ module EE
::
Gitlab
::
Routing
.
url_helpers
.
project_blob_path
(
project
,
File
.
join
(
finding
.
pipeline_branch
,
finding_file
))
end
def
execute_hooks
project
.
execute_integrations
(
integration_data
,
:vulnerability_hooks
)
end
private
def
integration_data
@integration_data
||=
::
Gitlab
::
DataBuilder
::
Vulnerability
.
build
(
self
)
end
def
user_notes_count_service
@user_notes_count_service
||=
::
Vulnerabilities
::
UserNotesCountService
.
new
(
self
)
# rubocop: disable CodeReuse/ServiceClass
end
...
...
ee/app/models/integrations/chat_message/vulnerability_message.rb
0 → 100644
View file @
49e788b9
# frozen_string_literal: true
module
Integrations
module
ChatMessage
class
VulnerabilityMessage
<
::
Integrations
::
ChatMessage
::
BaseMessage
attr_reader
:title
attr_reader
:identifiers
attr_reader
:severity
attr_reader
:vulnerability_url
def
initialize
(
params
)
@project_name
=
params
[
:project_name
]
||
params
.
dig
(
:project
,
:path_with_namespace
)
@project_url
=
params
.
dig
(
:project
,
:web_url
)
||
params
[
:project_url
]
@title
=
params
.
dig
(
:object_attributes
,
:title
)
@identifiers
=
params
.
dig
(
:object_attributes
,
:identifiers
)
@severity
=
params
.
dig
(
:object_attributes
,
:severity
)
@vulnerability_url
=
params
.
dig
(
:object_attributes
,
:url
)
end
def
attachments
[{
title:
title
,
title_link:
vulnerability_url
,
color:
attachment_color
,
fields:
attachment_fields
}]
end
def
message
"Vulnerability detected in
#{
project_link
}
"
end
private
def
attachment_color
"#C95823"
end
def
attachment_fields
[
{
title:
"Severity"
,
value:
severity
.
to_s
.
humanize
,
short:
true
},
{
title:
"Identifiers"
,
value:
::
Slack
::
Messenger
::
Util
::
LinkFormatter
.
format
(
identifiers_links
),
short:
true
}
]
end
def
identifiers_links
@identifiers
.
map
{
|
i
|
identifier_link
(
i
)
}.
join
(
I18n
.
t
(
:'support.array.words_connector'
))
end
def
identifier_link
(
identifier
)
link
(
identifier
[
:name
],
identifier
[
:url
])
end
def
project_link
link
(
project_name
,
project_url
)
end
end
end
end
ee/app/services/security/store_report_service.rb
View file @
49e788b9
...
...
@@ -6,7 +6,7 @@ module Security
class
StoreReportService
<
::
BaseService
include
Gitlab
::
Utils
::
StrongMemoize
attr_reader
:pipeline
,
:report
,
:project
,
:vulnerability_finding_to_finding_map
attr_reader
:pipeline
,
:report
,
:project
,
:vulnerability_finding_to_finding_map
,
:new_vulnerabilities
BATCH_SIZE
=
1000
...
...
@@ -15,6 +15,7 @@ module Security
@report
=
report
@project
=
@pipeline
.
project
@vulnerability_finding_to_finding_map
=
{}
@new_vulnerabilities
=
[]
end
def
execute
...
...
@@ -25,6 +26,7 @@ module Security
vulnerability_ids
=
create_all_vulnerabilities!
mark_as_resolved_except
(
vulnerability_ids
)
execute_new_vulnerabilities_hooks
start_auto_fix
...
...
@@ -66,6 +68,10 @@ module Security
vulnerability_ids
end
def
execute_new_vulnerabilities_hooks
new_vulnerabilities
.
each
{
|
v
|
v
.
execute_hooks
}
end
def
mark_as_resolved_except
(
vulnerability_ids
)
project
.
vulnerabilities
.
with_report_types
(
report
.
type
)
...
...
@@ -400,7 +406,9 @@ module Security
vulnerability
=
if
vulnerability_finding
.
vulnerability_id
Vulnerabilities
::
UpdateService
.
new
(
vulnerability_finding
.
project
,
pipeline
.
user
,
finding:
vulnerability_finding
,
resolved_on_default_branch:
false
).
execute
else
Vulnerabilities
::
CreateService
.
new
(
vulnerability_finding
.
project
,
pipeline
.
user
,
finding_id:
vulnerability_finding
.
id
).
execute
Vulnerabilities
::
CreateService
.
new
(
vulnerability_finding
.
project
,
pipeline
.
user
,
finding_id:
vulnerability_finding
.
id
).
execute
.
tap
do
|
vuln
|
new_vulnerabilities
<<
vuln
end
end
create_vulnerability_issue_link
(
vulnerability
)
...
...
ee/lib/ee/api/helpers/integrations_helpers.rb
View file @
49e788b9
...
...
@@ -42,6 +42,32 @@ module EE
*
super
]
end
override
:chat_notification_channels
def
chat_notification_channels
[
*
super
,
{
required:
false
,
name: :vulnerability_channel
,
type:
String
,
desc:
'The name of the channel to receive vulnerability_events notifications'
}
].
freeze
end
override
:chat_notification_events
def
chat_notification_events
[
*
super
,
{
required:
false
,
name: :vulnerability_events
,
type:
::
API
::
Services
::
Boolean
,
desc:
'Enable notifications for vulnerability_events'
}
].
freeze
end
end
end
end
...
...
ee/lib/gitlab/data_builder/vulnerability.rb
0 → 100644
View file @
49e788b9
# frozen_string_literal: true
module
Gitlab
module
DataBuilder
module
Vulnerability
extend
self
def
build
(
vulnerability
)
{
object_kind:
'vulnerability'
,
object_attributes:
hook_attrs
(
vulnerability
)
}
end
def
hook_attrs
(
vulnerability
)
{
url:
::
Gitlab
::
Routing
.
url_helpers
.
project_security_vulnerability_url
(
vulnerability
.
project
,
vulnerability
),
title:
vulnerability
.
title
,
state:
vulnerability
.
state
,
severity:
vulnerability
.
severity
,
severity_overridden:
vulnerability
.
severity_overridden
,
identifiers:
identifiers_hook_attrs
(
vulnerability
.
identifiers
),
report_type:
vulnerability
.
report_type
,
confidence:
vulnerability
.
confidence
,
confidence_overridden:
vulnerability
.
confidence_overridden
,
dismissed_at:
vulnerability
.
dismissed_at
,
dismissed_by_id:
vulnerability
.
dismissed_by_id
}
end
def
identifiers_hook_attrs
(
identifiers
)
return
[]
unless
identifiers
identifiers
.
map
do
|
identifier
|
{
name:
identifier
.
name
,
external_id:
identifier
.
external_id
,
external_type:
identifier
.
external_type
,
url:
identifier
.
url
}
end
end
end
end
end
ee/spec/lib/gitlab/data_builder/vulnerability_spec.rb
0 → 100644
View file @
49e788b9
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
DataBuilder
::
Vulnerability
do
let
(
:trait
)
{
:sast
}
let
(
:artifact
)
{
create
(
:ee_ci_job_artifact
,
trait
)
}
let
(
:report_type
)
{
artifact
.
file_type
}
let
(
:project
)
{
artifact
.
project
}
let
(
:pipeline
)
{
artifact
.
job
.
pipeline
}
let
(
:report
)
{
pipeline
.
security_reports
.
get_report
(
report_type
.
to_s
,
artifact
)
}
let
(
:finding_identifier_fingerprint
)
do
build
(
:ci_reports_security_identifier
,
external_id:
"CIPHER_INTEGRITY"
).
fingerprint
end
let
(
:scanner
)
{
build
(
:vulnerabilities_scanner
,
project:
project
,
external_id:
'find_sec_bugs'
,
name:
'Find Security Bugs'
)
}
let
(
:identifier
)
{
build
(
:vulnerabilities_identifier
,
project:
project
,
fingerprint:
finding_identifier_fingerprint
)
}
let
(
:finding_location_fingerprint
)
do
build
(
:ci_reports_security_locations_sast
,
file_path:
"groovy/src/main/java/com/gitlab/security_products/tests/App.groovy"
,
start_line:
"29"
,
end_line:
"29"
).
fingerprint
end
let
(
:finding
)
do
build
(
:vulnerabilities_finding
,
pipelines:
[
pipeline
],
identifiers:
[
identifier
],
primary_identifier:
identifier
,
scanner:
scanner
,
project:
project
,
uuid:
"e5388f40-18f5-566d-95c6-d64c6f46a00a"
,
location_fingerprint:
finding_location_fingerprint
)
end
let
(
:vulnerability
)
{
create
(
:vulnerability
,
findings:
[
finding
],
project:
project
)
}
describe
'.build'
do
let
(
:data
)
{
described_class
.
build
(
vulnerability
)
}
it
{
expect
(
data
).
to
be_a
(
Hash
)
}
it
{
expect
(
data
[
:object_kind
]).
to
eq
(
'vulnerability'
)
}
it
'contains the correct object attributes'
,
:aggregate_failures
do
object_attributes
=
data
[
:object_attributes
]
expected_attributes
=
{
url:
::
Gitlab
::
Routing
.
url_helpers
.
project_security_vulnerability_url
(
vulnerability
.
project
,
vulnerability
),
title:
vulnerability
.
title
,
state:
vulnerability
.
state
,
severity:
vulnerability
.
severity
,
severity_overridden:
vulnerability
.
severity_overridden
,
report_type:
vulnerability
.
report_type
,
confidence:
vulnerability
.
confidence
,
confidence_overridden:
vulnerability
.
confidence_overridden
,
dismissed_at:
vulnerability
.
dismissed_at
,
dismissed_by_id:
vulnerability
.
dismissed_by_id
,
identifiers:
[
{
name:
identifier
.
name
,
external_id:
identifier
.
external_id
,
external_type:
identifier
.
external_type
,
url:
identifier
.
url
}
]
}
expect
(
object_attributes
).
to
eq
(
expected_attributes
)
end
end
end
ee/spec/models/ee/integration_spec.rb
View file @
49e788b9
...
...
@@ -30,4 +30,18 @@ RSpec.describe Integration do
end
end
end
describe
'.vulnerability_hooks'
do
it
'includes services where vulnerability_events is true'
do
create
(
:service
,
active:
true
,
vulnerability_events:
true
)
expect
(
described_class
.
vulnerability_hooks
.
count
).
to
eq
1
end
it
'excludes services where vulnerability_events is false'
do
create
(
:service
,
active:
true
,
vulnerability_events:
false
)
expect
(
described_class
.
vulnerability_hooks
.
count
).
to
eq
0
end
end
end
ee/spec/models/integrations/chat_message/vulnerability_message_spec.rb
0 → 100644
View file @
49e788b9
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Integrations
::
ChatMessage
::
VulnerabilityMessage
do
subject
{
described_class
.
new
(
args
)
}
let
(
:args
)
do
{
project_name:
'Foobar Project'
,
project_url:
'https://git.example.com/random/foobar'
,
object_attributes:
{
url:
'https://git.example.com/random/foobar/-/security/vulnerabilities/1'
,
title:
'Foo Vulnerability'
,
identifiers:
[
{
name:
'CVE-2021-1234'
,
external_id:
'CVE-2021-1234'
,
external_type:
'cve'
,
url:
'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-1234'
},
{
name:
'CVE-2021-5678'
,
external_id:
'CVE-2021-5678'
,
external_type:
'cve'
,
url:
'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-5678'
}
]
}
}
end
describe
'#message'
do
it
'returns the correct message'
do
expect
(
subject
.
message
).
to
eq
(
"Vulnerability detected in [Foobar Project](https://git.example.com/random/foobar)"
)
end
end
describe
'#attachments'
do
it
'returns an array of one'
do
expect
(
subject
.
attachments
).
to
be_a
(
Array
)
expect
(
subject
.
attachments
.
size
).
to
eq
(
1
)
end
it
'contains the correct attributes'
do
attachments_item
=
subject
.
attachments
.
first
expect
(
attachments_item
).
to
have_key
(
:title
)
expect
(
attachments_item
).
to
have_key
(
:title_link
)
expect
(
attachments_item
).
to
have_key
(
:color
)
expect
(
attachments_item
).
to
have_key
(
:fields
)
end
it
'returns the correct color'
do
expect
(
subject
.
attachments
.
first
[
:color
]).
to
eq
(
"#C95823"
)
end
it
'returns the correct attachment fields'
do
attachments_item
=
subject
.
attachments
.
first
fields
=
attachments_item
[
:fields
].
map
{
|
h
|
h
[
:title
]
}
expect
(
fields
).
to
match_array
(
%w[Severity Identifiers]
)
end
it
'returns list of identifiers in correct form'
do
identifiers_item
=
subject
.
attachments
.
first
[
:fields
].
detect
{
|
i
|
i
[
:title
]
==
'Identifiers'
}
expect
(
identifiers_item
[
:value
]).
to
eq
(
'<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-1234|CVE-2021-1234>, <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-5678|CVE-2021-5678>'
)
end
end
end
ee/spec/services/security/store_report_service_spec.rb
View file @
49e788b9
...
...
@@ -428,6 +428,14 @@ RSpec.describe Security::StoreReportService, '#execute', :snowplow do
expect
{
subject
}.
to
change
{
Vulnerability
.
count
}.
by
(
4
)
end
it
'triggers project hooks on new vulnerabilities'
do
expect_next_instances_of
(
Vulnerability
,
4
)
do
|
vulnerability
|
expect
(
vulnerability
).
to
receive
(
:execute_hooks
)
end
subject
end
it
'updates existing findings with new data'
do
subject
...
...
locale/gitlab.pot
View file @
49e788b9
...
...
@@ -26258,6 +26258,9 @@ msgstr ""
msgid "ProjectService|Trigger event when a new, unique alert is recorded."
msgstr ""
msgid "ProjectService|Trigger event when a new, unique vulnerability is recorded. (Note: This feature requires an Ultimate plan.)"
msgstr ""
msgid "ProjectService|Trigger event when a pipeline status changes."
msgstr ""
...
...
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