Quay.io vulnerability notification to Microsoft Teams
Quay supports custom notifications based on different criteria. With the webhook notifications, you can tailor the notifications to integrate with almost any service.
Here I’ll show you how to set up Quay -> Microsoft Teams notifications, such as when vulnerabilities are detected.
1. Set up “Incoming Webhook” connector in Teams
I will not go into detail here. Setting this up is pretty straight forward and just clicking through Teams interface.
Documentation can be found here: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/what-are-webhooks-and-connectors
As a result you will get a webhook URL. Make sure to copy this and save it someplace, as we’ll use it later on in this guide.
2. Construct webhook JSON POST body
Quay grants us the ability to construct our JSON body however we want. When it comes to the Microsoft Teams “Incoming Webhook” connector, this grants us the ability to stylize our message however we want. A good place to start is to look at Microsoft’s examples: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#example-connector-message
What we then can do is populate it with data from the Quay notification itself. The data itself can be seen here: https://docs.quay.io/guides/notifications.html For “Vulnerability Detected” notifications, they contain the following data:
{
"repository": "mynamespace/repository",
"namespace": "mynamespace",
"name": "repository",
"docker_url": "quay.io/mynamespace/repository",
"homepage": "https://quay.io/repository/mynamespace/repository",
"tags": ["latest", "othertag"],
"vulnerability": {
"id": "CVE-1234-5678",
"description": "This is a bad vulnerability",
"link": "http://url/to/vuln/info",
"priority": "Critical",
"has_fix": true
}
}
When constructing our JSON body, what we can do is use the variable syntax ${someJsonPath}
inside any property value and have it replaced by the values from the notification struct shown above.
Here’s an example message body, used in the screenshot up at the top of this post:
{
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "d71e00",
"summary": "Vulnerability detected in ${name}, first tag: ${tags[0]}",
"sections": [{
"activityTitle": "Vulnerability Detected",
"activitySubtitle": "In repository ${name}",
"activityImage": "https://github.com/quay/quay/raw/master/static/img/quay-icon-stripe.png",
"facts": [{
"name": "Repository",
"value": "${repository}"
}, {
"name": "First 5 tags",
"value": "${tags[0]}, ${tags[1]}, ${tags[2]}, ${tags[3]}, ${tags[4]}"
}, {
"name": "Vulnerability ID",
"value": "${vulnerability.id}"
}, {
"name": "Priority",
"value": "${vulnerability.priority}"
}, {
"name": "Description",
"value": "${vulnerability.description}"
}, {
"name": "Has fix?",
"value": "${vulnerability.has_fix}"
}],
"markdown": false
}],
"potentialAction": [{
"@type": "OpenUri",
"name": "Show Quay.io repository",
"targets": [{
"os": "default",
"uri": "${homepage}"
}]
}, {
"@type": "OpenUri",
"name": "Show ${vulnerability.id}",
"targets": [{
"os": "default",
"uri": "${vulnerability.link}"
}]
}]
}
Behind the scenes Quay is using jsonpath-rw which allows quite extensive JSONPath usage. Recommended to visit the documentation of jsonpath-rw to find out more.
⚠️ NOTE: Selecting multiple values, such as
${tags[*]}
, does not work due to a scripting bug in Quay. Quay is supposed to take this list of matches and concatenate them with,
as separator, but it fails.To get around this, you can target each element in the array separately instead. For example as shown in the JSON snippet above where I chose to do
${tags[0]}, ${tags[1]}, ${tags[2]}, ${tags[3]}, ${tags[4]}
instead.Do note values not found results in the string
(none)
. For example, if there only are 2 tags, let’s say"tags": ["latest", "v1.0.0"]
, then the above string would result inlatest, v1.0.0, (none), (none), (none)
. It’s a compromise, but it’s still readable.You can follow a pull request trying to tackle this issue over here: https://github.com/quay/quay/pull/800
📄 For source-code hunters who really want to know how this templating works, I recommend the following files:
- JSON templating/substitution: https://github.com/quay/quay/blob/v3.5.2/util/jsontemplate.py
- Sample data that is sent on “Test Notification”: https://github.com/quay/quay/blob/v3.5.2/notifications/notificationevent.py#L192-L207
- Actual data that is sent on vulnerability scans: https://github.com/quay/quay/blob/v3.5.2/workers/securityscanningnotificationworker.py#L104-L117
3. Create notification in Quay
These settings are set in the settings panel of a repository, i.e.:
https://quay.io/repository/myNamespace/myRepo?tab=settings
For this example, I’ll hook up a “Package Vulnerability Found” notification. So in the newly opened “Create repository notification” form you are granted with, enter the following:
- “When this event occurs”
- “Event” (dropdown):
Package Vulnerability Found
- “With minimum severity level” (dropdown):
Medium
- “Event” (dropdown):
- “Then issue a notification”
- “Notification method” (dropdown):
Webhook POST
- “Webhook URL” (text field): /paste your webhook URL from step 1/
- “POST body template (optional)” (text area): /paste your webhook JSON from step 2/
- “Notification method” (dropdown):
- “With extra configuration”
- “Notification title” (text field):
Microsoft Teams
- “Notification title” (text field):
And click “Create Notification”, and you’re done!
4. Test your notification
Click the cogwheel and then “Test Notification”
Troubleshooting your notification
If no message shows up in your Teams channel where you added your connection, then here’s some things you should check:
-
You should not be using range selection JSONPath syntax, ex:
${tags}
or${tags[*]}
(see PR 800) -
Your webhook is not missing a character. It’s so long that there’s a good chance when you copied it you missed a trailing character.
-
Your webhook body is valid JSON. Try populate it with some dummy data and send it to the webhook manually as a sanity check, like so:
# first save myWebhookBody.json as a file before running this
curl -d @myWebhookBody.json "https://mytenant.webhook.office.com/webhookb2/blablabla/IncomingWebhook/blablablablablu"
- For self-hosted Quay instance, make sure you have your firewalls opened. Try
curl
’ing from the Quay server over to that webhook to see if it can reach it.