Snyk
Our Snyk integration allows you to import targets
, projects
and issues
from your Snyk account into Port, according to your mapping and definitions.
Common use casesโ
- Map
targets
,projects
andissues
in your Snyk organization environment. - Watch for object changes (create/update/delete) in real-time, and automatically apply the changes to your entities in Port.
- Create/delete Snyk objects using self-service actions.
Prerequisitesโ
To install the integration, you need a Kubernetes cluster that the integration's container chart will be deployed to.
Please make sure that you have kubectl
and helm
installed on your machine, and that your kubectl
CLI is connected to the Kubernetes cluster where you plan to install the integration.
installationโ
Choose one of the following installation methods:
- Real Time & Always On
- Scheduled
Using this installation option means that the integration will be able to update Port in real time using webhooks.
This table summarizes the available parameters for the installation. Set them as you wish in the script below, then copy it and run it in your terminal:
Parameter | Description | Required |
---|---|---|
port.clientId | Your port client id | โ |
port.clientSecret | Your port client secret | โ |
port.baseUrl | Your port base url, relevant only if not using the default port app | โ |
integration.identifier | Change the identifier to describe your integration | โ |
integration.type | The integration type | โ |
integration.eventListener.type | The event listener type | โ |
integration.secrets.token | The Snyk API token | โ |
integration.config.organizationId | The Snyk organization ID | โ |
integration.config.apiUrl | The Snyk API URL. If not specified, the default will be https://api.snyk.io | โ |
integration.config.appHost | The host of the Port Ocean app. Used to set up the integration endpoint as the target for Webhooks created in Snyk | โ |
integration.secret.webhookSecret | This is a password you create, that Snyk uses to sign webhook events to Port | โ |
scheduledResyncInterval | The number of minutes between each resync | โ |
initializePortResources | Default true, When set to true the integration will create default blueprints and the port App config Mapping | โ |
helm repo add --force-update port-labs https://port-labs.github.io/helm-charts
helm upgrade --install my-snyk-integration port-labs/port-ocean \
--set port.clientId="PORT_CLIENT_ID" \
--set port.clientSecret="PORT_CLIENT_SECRET" \
--set port.baseUrl="https://api.getport.io" \
--set initializePortResources=true \
--set scheduledResyncInterval=120 \
--set integration.identifier="my-snyk-integration" \
--set integration.type="snyk" \
--set integration.eventListener.type="POLLING" \
--set integration.secrets.token="SNYK_TOKEN" \
--set integration.config.organizationId="ORG_ID"
- GitHub
- Jenkins
This workflow will run the Snyk integration once and then exit, this is useful for scheduled ingestion of data.
If you want the integration to update Port in real time using webhooks you should use the Real Time & Always On installation option
Make sure to configure the following Github Secrets:
Parameter | Description | Required |
---|---|---|
OCEAN__INTEGRATION__CONFIG__TOKEN | The Snyk API token | โ |
OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID | The Snyk organization ID | โ |
OCEAN__INTEGRATION__CONFIG__API_URL | The Snyk API URL. If not specified, the default will be https://api.snyk.io | โ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to false the integration will not create default blueprints and the port App config Mapping | โ |
OCEAN__INTEGRATION__IDENTIFIER | Change the identifier to describe your integration, if not set will use the default one | โ |
OCEAN__PORT__CLIENT_ID | Your port client id | โ |
OCEAN__PORT__CLIENT_SECRET | Your port client secret | โ |
OCEAN__PORT__BASE_URL | Your port base url, relevant only if not using the default port app | โ |
Here is an example for snyk-integration.yml
workflow file:
name: Snyk Exporter Workflow
# This workflow responsible for running Snyk exporter.
on:
workflow_dispatch:
jobs:
run-integration:
runs-on: ubuntu-latest
steps:
- name: Run Snyk Integration
run: |
# Set Docker image and run the container
integration_type="snyk"
version="latest"
image_name="ghcr.io/port-labs/port-ocean-$integration_type:$version"
docker run -i --rm --platform=linux/amd64 \
-e OCEAN__EVENT_LISTENER='{"type":"ONCE"}' \
-e OCEAN__INITIALIZE_PORT_RESOURCES=true \
-e OCEAN__INTEGRATION__CONFIG__TOKEN=${{ secrets.OCEAN__INTEGRATION__CONFIG__TOKEN }} \
-e OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID=${{ secrets.OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID }} \
-e OCEAN__PORT__CLIENT_ID=${{ secrets.OCEAN__PORT__CLIENT_ID }} \
-e OCEAN__PORT__CLIENT_SECRET=${{ secrets.OCEAN__PORT__CLIENT_SECRET }} \
$image_name
exit $?
This pipeline will run the Snyk integration once and then exit, this is useful for scheduled ingestion of data.
Your Jenkins agent should be able to run docker commands.
If you want the integration to update Port in real time using webhooks you should use the Real Time & Always On installation option.
Make sure to configure the following Jenkins Credentials
of Secret Text
type:
Parameter | Description | Required |
---|---|---|
OCEAN__INTEGRATION__CONFIG__TOKEN | The Snyk API token | โ |
OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID | The Snyk organization ID | โ |
OCEAN__INTEGRATION__CONFIG__API_URL | The Snyk API URL. If not specified, the default will be https://api.snyk.io | โ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to false the integration will not create default blueprints and the port App config Mapping | โ |
OCEAN__INTEGRATION__IDENTIFIER | Change the identifier to describe your integration, if not set will use the default one | โ |
OCEAN__PORT__CLIENT_ID | Your port client id (How to get the credentials) | โ |
OCEAN__PORT__CLIENT_SECRET | Your port client (How to get the credentials) secret | โ |
OCEAN__PORT__BASE_URL | Your port base url, relevant only if not using the default port app | โ |
Here is an example for Jenkinsfile
groovy pipeline file:
pipeline {
agent any
stages {
stage('Run Snyk Integration') {
steps {
script {
withCredentials([
string(credentialsId: 'OCEAN__INTEGRATION__CONFIG__TOKEN', variable: 'OCEAN__INTEGRATION__CONFIG__TOKEN'),
string(credentialsId: 'OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID', variable: 'OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID'),
string(credentialsId: 'OCEAN__PORT__CLIENT_ID', variable: 'OCEAN__PORT__CLIENT_ID'),
string(credentialsId: 'OCEAN__PORT__CLIENT_SECRET', variable: 'OCEAN__PORT__CLIENT_SECRET'),
]) {
sh('''
#Set Docker image and run the container
integration_type="snyk"
version="latest"
image_name="ghcr.io/port-labs/port-ocean-${integration_type}:${version}"
docker run -i --rm --platform=linux/amd64 \
-e OCEAN__EVENT_LISTENER='{"type":"ONCE"}' \
-e OCEAN__INITIALIZE_PORT_RESOURCES=true \
-e OCEAN__INTEGRATION__CONFIG__TOKEN=$OCEAN__INTEGRATION__CONFIG__TOKEN \
-e OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID=$OCEAN__INTEGRATION__CONFIG__ORGANIZATION_ID \
-e OCEAN__PORT__CLIENT_ID=$OCEAN__PORT__CLIENT_ID \
-e OCEAN__PORT__CLIENT_SECRET=$OCEAN__PORT__CLIENT_SECRET \
$image_name
exit $?
''')
}
}
}
}
}
}
Ingesting Snyk objectsโ
The Snyk integration uses a YAML configuration to describe the process of loading data into the developer portal.
Here is an example snippet from the config which demonstrates the process for getting project
data from Snyk:
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: project
selector:
query: "true"
port:
entity:
mappings:
identifier: .id
title: .attributes.name
blueprint: '"snykProject"'
properties:
url: ("https://app.snyk.io/org/" + .relationships.organization.data.id + "/project/" + .id | tostring)
owner: .__owner.email
businessCriticality: .attributes.business_criticality
environment: .attributes.environment
lifeCycle: .attributes.lifecycle
highOpenVulnerabilities: .meta.latest_issue_counts.high
mediumOpenVulnerabilities: .meta.latest_issue_counts.medium
lowOpenVulnerabilities: .meta.latest_issue_counts.low
criticalOpenVulnerabilities: .meta.latest_issue_counts.critical
importedBy: .__importer.email
tags: .attributes.tags
The integration makes use of the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from Snyk's API events.
Configuration structureโ
The integration configuration determines which resources will be queried from Snyk, and which entities and properties will be created in Port.
-
The root key of the integration configuration is the
resources
key:resources:
- kind: project
selector:
... -
The
kind
key is a specifier for a Snyk object:resources:
- kind: project
selector:
... -
The
selector
and thequery
keys allow you to filter which objects of the specifiedkind
will be ingested into your software catalog:resources:
- kind: project
selector:
query: "true" # JQ boolean expression. If evaluated to false - this object will be skipped.
port: -
The
port
,entity
and themappings
keys are used to map the Snyk object fields to Port entities. To create multiple mappings of the same kind, you can add another item in theresources
array;resources:
- kind: project
selector:
query: "true"
port:
entity:
mappings: # Mappings between one Snyk object to a Port entity. Each value is a JQ query.
identifier: .id
title: .attributes.name
blueprint: '"snykProject"'
properties:
url: ("https://app.snyk.io/org/" + .relationships.organization.data.id + "/project/" + .id | tostring)
owner: .__owner.email
businessCriticality: .attributes.business_criticality
environment: .attributes.environment
lifeCycle: .attributes.lifecycle
highOpenVulnerabilities: .meta.latest_issue_counts.high
mediumOpenVulnerabilities: .meta.latest_issue_counts.medium
lowOpenVulnerabilities: .meta.latest_issue_counts.low
criticalOpenVulnerabilities: .meta.latest_issue_counts.critical
importedBy: .__importer.email
tags: .attributes.tags
- kind: project # In this instance project is mapped again with a different filter
selector:
query: '.name == "MyProjectName"'
port:
entity:
mappings: ...:::tip Blueprint key Note the value of the
blueprint
key - if you want to use a hardcoded string, you need to encapsulate it in 2 sets of quotes, for example use a pair of single-quotes ('
) and then another pair of double-quotes ("
) :::
Ingest data into Portโ
To ingest Snyk objects using the integration configuration, you can follow the steps below:
- Go to the DevPortal Builder page.
- Select a blueprint you want to ingest using Snyk.
- Choose the Ingest Data option from the menu.
- Select Snyk under the Code quality & security providers category.
- Modify the configuration according to your needs.
- Click
Resync
.
Examplesโ
Examples of blueprints and the relevant integration configurations:
Targetโ
Target blueprint
{
"identifier": "snykTarget",
"title": "Snyk Target",
"icon": "Snyk",
"schema": {
"properties": {
"criticalOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open Critical Vulnerabilities"
},
"highOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open High Vulnerabilities"
},
"mediumOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open Medium Vulnerabilities"
},
"lowOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open Low Vulnerabilities"
},
"origin": {
"title": "Target Origin",
"type": "string",
"enum": [
"artifactory-cr",
"aws-config",
"aws-lambda",
"azure-functions",
"azure-repos",
"bitbucket-cloud",
"bitbucket-server",
"cli",
"cloud-foundry",
"digitalocean-cr",
"docker-hub",
"ecr",
"gcr",
"github",
"github-cr",
"github-enterprise",
"gitlab",
"gitlab-cr",
"google-artifact-cr",
"harbor-cr",
"heroku",
"ibm-cloud",
"kubernetes",
"nexus-cr",
"pivotal",
"quay-cr",
"terraform-cloud"
]
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
Integration configuration
- kind: target
selector:
query: "true"
port:
entity:
mappings:
identifier: .attributes.displayName
title: .attributes.displayName
blueprint: '"snykTarget"'
properties:
origin: .attributes.origin
highOpenVulnerabilities: "[.__projects[].meta.latest_issue_counts.high] | add"
mediumOpenVulnerabilities: "[.__projects[].meta.latest_issue_counts.medium] | add"
lowOpenVulnerabilities: "[.__projects[].meta.latest_issue_counts.low] | add"
criticalOpenVulnerabilities: "[.__projects[].meta.latest_issue_counts.critical] | add"
Projectโ
Project blueprint
{
"identifier": "snykProject",
"description": "This blueprint represents a snyk project in our software catalog",
"title": "Snyk Project",
"icon": "Snyk",
"schema": {
"properties": {
"url": {
"type": "string",
"title": "URL",
"format": "url",
"icon": "Snyk"
},
"owner": {
"type": "string",
"title": "Owner",
"format": "user",
"icon": "TwoUsers"
},
"businessCriticality": {
"title": "Business Criticality",
"type": "array",
"items": {
"type": "string",
"enum": ["critical", "high", "medium", "low"]
},
"icon": "DefaultProperty"
},
"environment": {
"items": {
"type": "string",
"enum": [
"frontend",
"backend",
"internal",
"external",
"mobile",
"saas",
"onprem",
"hosted",
"distributed"
]
},
"icon": "Environment",
"title": "Environment",
"type": "array"
},
"lifeCycle": {
"title": "Life Cycle",
"type": "array",
"items": {
"type": "string",
"enum": ["development", "sandbox", "production"]
},
"icon": "DefaultProperty"
},
"highOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open High Vulnerabilities"
},
"mediumOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open Medium Vulnerabilities"
},
"lowOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open Low Vulnerabilities"
},
"criticalOpenVulnerabilities": {
"icon": "Vulnerability",
"type": "number",
"title": "Open Low Vulnerabilities"
},
"importedBy": {
"icon": "TwoUsers",
"type": "string",
"title": "Imported By",
"format": "user"
},
"tags": {
"type": "array",
"title": "Tags",
"icon": "DefaultProperty"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
Integration configuration
resources:
- kind: project
selector:
query: "true"
port:
entity:
mappings:
identifier: .id
title: .attributes.name
blueprint: '"snykProject"'
properties:
url: ("https://app.snyk.io/org/" + .relationships.organization.data.id + "/project/" + .id | tostring)
owner: .__owner.email
businessCriticality: .attributes.business_criticality
environment: .attributes.environment
lifeCycle: .attributes.lifecycle
highOpenVulnerabilities: .meta.latest_issue_counts.high
mediumOpenVulnerabilities: .meta.latest_issue_counts.medium
lowOpenVulnerabilities: .meta.latest_issue_counts.low
criticalOpenVulnerabilities: .meta.latest_issue_counts.critical
importedBy: .__importer.email
tags: .attributes.tags
Issueโ
Issue blueprint
{
"identifier": "snykVulnerability",
"description": "This blueprint represents a Snyk vulnerability in our software catalog",
"title": "Snyk Vulnerability",
"icon": "Snyk",
"schema":
{
"properties":
{
"score": { "icon": "Star", "type": "number", "title": "Score" },
"packageName":
{
"type": "string",
"title": "Package Name",
"icon": "DefaultProperty",
},
"packageVersions":
{ "icon": "Package", "title": "Package Versions", "type": "array" },
"type":
{
"type": "string",
"title": "Type",
"enum": ["vuln", "license", "configuration"],
"icon": "DefaultProperty",
},
"severity":
{
"icon": "Alert",
"title": "Issue Severity",
"type": "string",
"enum": ["low", "medium", "high", "critical"],
"enumColors":
{
"low": "green",
"medium": "yellow",
"high": "red",
"critical": "red",
},
},
"url":
{
"icon": "Link",
"type": "string",
"title": "Issue URL",
"format": "url",
},
"language":
{
"type": "string",
"title": "Language",
"icon": "DefaultProperty",
},
"publicationTime":
{
"type": "string",
"format": "date-time",
"title": "Publication Time",
"icon": "DefaultProperty",
},
"isPatched":
{
"type": "boolean",
"title": "Is Patched",
"icon": "DefaultProperty",
},
},
"required": [],
},
"mirrorProperties": {},
"calculationProperties": {},
"relations":
{
"snykProject":
{
"title": "Project",
"target": "snykProject",
"required": false,
"many": false,
},
},
}
Integration configuration
resources:
- kind: vulnerability
selector:
query: '.issueType == "vuln"'
port:
entity:
mappings:
identifier: .issueData.id
title: .issueData.title
blueprint: '"snykVulnerability"'
properties:
score: .priorityScore
packageName: .pkgName
packageVersions: .pkgVersions
type: .issueType
severity: .issueData.severity
url: .issueData.url
language: .issueData.language // .issueType
publicationTime: .issueData.publicationTime
isPatched: .isPatched
relations:
snykProject: .__project.id