How To: Issue Triage via Phylum Extensions
Tags: security, javascript, tutorial, productivity
Published: 2022-09-07 23:48:54

# Extension Development
With the release of the Phylum community edition, we have added support for Software Supply Chain Extensions - scriptable extensions to the product which enable a layer of automation and customizability on top of what Phylum provides. 

## Motivation
Better automation is critically important to maintaining development velocity and ensuring that major problems aren't missed - in short, it is the entire cornerstone of DevOps. Without strong automation (and tooling to support it), CI/CD is no longer "continuous". With this in mind, we developed an automation framework to enable the development of tooling to help facilitate orchestration, findings management, and triage automation (among other things).  As such, we strive to be "automation first," and provide better tooling to help design toward more proactive solutions.

Several guiding principles were applied in selecting the correct tools to enable this vision: the mechanism for managing extensions must be flexible, it should provide security controls (as a software supply chain security company, we want to ensure that the tooling from community is _also_ adequately secure) and sandboxing, and should be simple to utilize. 

## Tooling
With this in mind we chose to leverage Deno as the base framework for our automation tooling. It runs in a sandbox with a robust permission management system, comes with a feature-rich standard library, and supports many languages (through its ability to run Web Assembly). Additionally, it provides first-class integrations with Rust (as the Deno embeddable sandbox itself ships as a crate), and is easily extensible. 

## Extensions In Motion
Given that the API itself [has relatively robust documentation](https://docs.phylum.io/docs/extension_overview), and is not extraordinarily complex, we won't focus too heavily on that _specifically_ for this exercise - rather, we'll spend more time focused on what we can _actually do_ with the extension framework. 

To that end, we will work through the exercise of building an alerting tool - the end result being an extension that can filter issues, and push _critical_ alerts into Jira via REST API. This is also [quite well documented](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-events-get), and should serve as a good example to showcase where the extension framework can be helpful.

### Getting Started
As we start working on this problem, we will first add a manifest to outline what resources our extension will need to operate properly:

```TOML
name = "jira"
description = "blindly put critical issues into jira!"
entry_point = "main.ts"
[permissions]
  env = ["JIRA_USER_EMAIL", "JIRA_USER_TOKEN"]
  net = ["example-domain.atlassian.net"]
```

Where `example-domain` should be replaced with whatever subdomain is appropriate. 

Next, we will begin to build out our actual extension - starting in `main.ts`, which we identified as our extension's entry point (above).  Here we will start by:

1. Parsing the lockfile provided by the user.
2. Performing an analysis on the loaded packages.
3. Collecting the returned issues that are of `critical` severity.

```Typescript

import { PhylumApi } from "phylum";


// First, we will check to ensure that we have the appropriate arguments -
// for this example, we will assume that it takes the form:
// phylum jira <ecosystem> <lockfile-path>
// NOTE: the phylum CLI itself utilizes the first two of those arguments before
//       calling into Deno, so `Deno.args[0]` will be the value provided for 
//       <ecosystem>, and `Deno.args[1]` will contain <lockfile>.
if(Deno.args.length < 2) {
  console.error("Usage: phylum jira <ecosystem> <lockfile>");
  Deno.exit(1);
}

const ecosystem = Deno.args[0];
const lockfile = Deno.args[1];
// Parse the provided lockfile
const lockData = await PhylumApi.parseLockfile(lockfile, ecosystem);

// Perform analysis
const jobId = await PhylumApi.analyze(ecosystem, lockData.packages);
const status = await PhylumApi.getJobStatus(jobId);

let criticalIssues = {};
// Now, we will loop through the packages and check the issues that came back:
for(const package of status.packages) {
  // Issues are as follows:
  // { 
  //   tag: "<unique issue ID>", 
  //   title: "issue title", 
  //   description: "issue description",
  //   severity: "low | medium | high | critical",
  //   domain: "malicious_code | vulnerability | author | license | engineering" 
  // }
  const issues = package.issues.filter(issue => issue.severity === 'critical');
  if(issues.length)
    criticalIssues[`${package.name}:${package.version}`] = issues;
}


```

Now we can refer back to the [docs for the Jira API we will be hitting](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-bulk-post) in order to generate Jira issues for each critical issue returned by the Phylum API. In practice, we would likely want to pull existing issues and de-duplicate (if the issue we are about to create already exists for an existing package/version), and would want to paginate for situations where 50 or more packages with critical issues are identified, but for the sake of keeping this example simple, we will just attempt bulk creation.

```Typescript

import { encode } from "https://deno.land/std/encoding/base64.ts";

// ...

let bodyData = {
  issueUpdates: []
};

// First we will loop through our packages with critical issues, and generate
// an entry for each:
for(const packageId of Object.keys(criticalIssues)) {
   // Populate whatever fields are appropriate given the package/version
   // tuple provided as the key, and the issue information as the value
}

const email = Deno.env.get("JIRA_USER_EMAIL");
const token = Deno.env.get("JIRA_USER_TOKEN");

if(!email || !token) {
  console.error("Please ensure that you provide the user email and token via env!")
  Deno.exit(2);
}

const encodedToken = encode(`${email}:${token}`);

const result = await fetch("https://example-domain.atlassian.net", {
  method: "POST",
  headers: {
    'Authorization': `Basic ${encodedToken}`,
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(bodyData)
});

if(200 !== result.status) {
  console.error(`Request to create issues failed! Got a ${result.status} back.`);
  Deno.exit(3);
}

const data = result.json();

if(data.errors.length > 0) {
  // Handle errors
}

```


Followed by a `phylum extension install ./jira-extension`  and now, execution!

You can also view this article (and many more) [on our site](https://blog.phylum.io/insights-and-resources/build-your-own-software-supply-chain-extensions) - where you can also access [the free, community version](https://app.phylum.io/register) of our product.