Snyk has native integrations to Slack, ServiceNow, Jira and others But no integration to MS Teams How can a team get Snyk notifications pushed to MS Teams?
You can make use of Snyk's outbound webhooks
Register any endpoint of yours that can consume JSON payload; sample payload is at https://snyk.docs.apiary.io/#introduction/consuming-webhooks
There is a sample Azure Function specific for MS Teams: https://github.com/harrykimpel/snyk-webhook-subscription/blob/main/azure-function-microsoft-teams.cs and additional info in this repo's README and blog post
Another script that does the same but for Azure Boards is this here, which is Node.js based: https://github.com/mathiasconradt/snyk-azure-boards-webhook
Below sample Azure function shows to to consume the incoming JSON payload from Snyk, then forward it further to another system, such as MS Teams:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json.Linq;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
//log.LogInformation("data: " + requestBody);
string count = data.newIssues.Count.ToString();
log.LogInformation("data.newIssues.Count: " + count);
string responseMessage = "No new issues found. Nothing to process!";
if (data.newIssues.Count > 0)
{
log.LogInformation("New issues found!");
name = name ?? data?.name;
string projectName = data.project.name;
string browseUrl = data.project.browseUrl;
int x = 0;
for (int i = 0; i < data.newIssues.Count; i++)
{
// send data to Azure Boards
StringBuilder sb = new StringBuilder();
//var item = (JObject)data.newIssues[i];
//do something with item
string id = data.newIssues[i].id.ToString();
//log.LogInformation("data.newIssues[i].id:" + id);
string descr = data.newIssues[i].issueData.description.ToString();
//log.LogInformation("data.newIssues[i].issueData.description:" + descr);
sb.Append("{");
sb.Append(" \"@context\": \"https://schema.org/extensions\",");
sb.Append(" \"@type\": \"MessageCard\",");
sb.Append(" \"themeColor\": \"0072C6\",");
sb.Append(" \"title\": \"" + projectName + "\",");
sb.Append(" \"text\": \"" + id + "<br><br>" + descr + "\",");
sb.Append(" \"potentialAction\": [ ");
sb.Append(" {");
sb.Append(" \"@type\": \"OpenUri\",");
sb.Append(" \"name\": \"Project Details\",");
sb.Append(" \"targets\": [");
sb.Append(" { \"os\": \"default\", \"uri\": \"" + browseUrl + "\" }");
sb.Append(" ]");
sb.Append(" }");
sb.Append(" ]");
sb.Append("}");
string payload = sb.ToString();
//log.LogInformation("content: " + payload);
var content = new StringContent(payload);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var MS_TEAMS_WEBHOOK = Environment.GetEnvironmentVariable("MS_TEAMS_WEBHOOK");
var url = MS_TEAMS_WEBHOOK;
using var client = new HttpClient();
var response = await client.PostAsync(url, content);
string result = response.Content.ReadAsStringAsync().Result;
//log.LogInformation("response.StatusCode: " + response.StatusCode);
if (response.StatusCode == HttpStatusCode.OK)
{
x++;
}
//log.LogInformation("result: " + result);
}
// write output as summary
string output = "Successfully processed " + x + " issues.";
log.LogInformation(output);
responseMessage = output;
}
return new OkObjectResult(responseMessage);
}
Another example based on Node.js but for Azure Boards. If you prefer Node.js, simply replace the outbound logic with MS Teams API.
// index.js
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const crypto = require('crypto');
const PORT = process.env.PORT || 5000;
const ISSUE_TEMPLATE = [
{
"op": "add",
"path": "/fields/System.Title",
"from": null,
"value": null
},
{
"op": "add",
"path": "/fields/System.Description",
"from": null,
"value": null
},
{
"op": "add",
"path": "/fields/System.WorkItemType",
"from": null,
"value": "Issue"
}
];
const app = express()
.use(bodyParser.urlencoded({ extended: true }))
.use(bodyParser.json())
.use(bodyParser.raw())
.get('/snyk', (req, res) => {
console.log('process.env.AZURE_DEVOPS_USER ' + process.env.AZURE_DEVOPS_USER);
res.sendStatus(200);
})
.post('/snyk', (req, res) => {
console.log('Got body:', req.body);
var verified = this.verifySignature(req);
console.log('verified: ', verified);
if (verified && req.body.newIssues) {
var newIssues = req.body.newIssues;
newIssues.forEach(issue => {
var it = JSON.parse(JSON.stringify(ISSUE_TEMPLATE));
it[0].value = issue.issueData.title + " [" + issue.issueData.id + "]";
it[1].value = issue.issueData.description;
this.createIssuePostman(it);
});
}
res.sendStatus(200);
})
.listen(PORT, () => console.log(`Listening on ${ PORT }`));
module.exports.verifySignature = function (request) {
const hmac = crypto.createHmac( 'sha256' , process.env.SNYK_WEBHOOKS_SECRET);
const buffer = JSON .stringify(request.body);
hmac.update(buffer, 'utf8' );
const signature = `sha256=${hmac.digest('hex')}` ;
return signature === request.headers[ 'x-hub-signature' ];
}
module.exports.createIssuePostman = function(issue) {
console.log('createIssuePostman: ' + issue[0].value);
var auth = 'Basic ' + Buffer.from(process.env.AZURE_DEVOPS_USER + ':' + process.env.AZURE_DEVOPS_ACCESS_TOKEN).toString('base64');
var config = {
method: 'post',
url: 'https://dev.azure.com/' + process.env.AZURE_DEVOPS_ORGANIZATION + '/' + process.env.AZURE_DEVOPS_PROJECT + '/_apis/wit/workitems/$Issue?validateOnly=false&api-version=6.0',
headers: {
'Authorization': auth,
'Content-Type': 'application/json-patch+json'
},
data : issue
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
}