Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Snyk push notifications to MS Teams?

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?

like image 430
Jonathan Gruber Avatar asked Oct 15 '25 16:10

Jonathan Gruber


1 Answers

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);
  });
}
like image 70
Mathias Conradt Avatar answered Oct 18 '25 07:10

Mathias Conradt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!