Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add SBOM-based dependency diff workflow #14

Merged
merged 5 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Continuous integration (node-js code)

on: [push]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x'
- run: npm ci
working-directory: common
- run: npm run build
working-directory: common
- run: npm test
working-directory: common
79 changes: 79 additions & 0 deletions .github/workflows/java-dependency-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Compare the dependencies of a pull request to a Maven multi-module project with the base branch. Check according to our Stop & Go list.

on:
workflow_call:
inputs:
ref:
description: 'The reference of the github commit of the reusable workflow. Must match the specified branch/tag/commit where the workflow is called. Workaround to https://github.com/actions/runner/issues/2417. Should never be untrusted content.'
default: 'main'
type: string
secrets:
VAULT_ADDR:
required: true
VAULT_ROLE_ID:
required: true
VAULT_SECRET_ID:
required: true

jobs:
java-dependency-check:
runs-on: ubuntu-latest
name: Java Dependency Check
# todo: filter by label if diff should be performed
steps:
- name: Import Secrets
id: secrets
uses: hashicorp/[email protected]
with:
url: ${{ secrets.VAULT_ADDR }}
method: approle
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}
secrets: |
secret/data/github.com/organizations/camunda NEXUS_USR;
secret/data/github.com/organizations/camunda NEXUS_PSW;
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Checkout repository
uses: actions/checkout@v3
with:
path: repo-to-check
ref: ${{ github.event.pull_request.head.sha }}
- name: Checkout reusable workflow dir
uses: actions/checkout@v3
with:
repository: camunda/automation-platform-github-actions
token: ${{ secrets.GITHUB_TOKEN }}
path: automation-platform-github-actions
ref: ${{ inputs.ref }} # github.job_workflow_sha seems to be the documented parameter to resolve this, but doesn't work in practice
- name: Generate SBOMs
id: generate-sboms
run: bash ${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/generate-sboms.sh ${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/maven-settings.xml
working-directory: ${{ github.workspace }}/repo-to-check
- name: Diff SBOMs
id: diff-sboms
uses: ./automation-platform-github-actions/java-dependency-check
with:
base-sbom: ${{ github.workspace }}/repo-to-check/target/diff/base.json
head-sbom: ${{ github.workspace }}/repo-to-check/target/diff/head.json
primary-party-group-matcher: "^org\\.camunda"
license-list: ${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/licenses.json
github-comment-template: ${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/diff.hbs
partials: |
componentDetails:${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/component-details.hbs
componentDiff:${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/component-diff.hbs
componentTree:${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/component-tree.hbs
componentVersion:${{ github.workspace }}/automation-platform-github-actions/java-dependency-check/component-version.hbs
output-path: ${{ github.workspace }}/dependency-diff.html
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload full diff as workflow artifact
uses: actions/upload-artifact@v3
with:
name: artifacts.zip
path: |
${{ github.workspace }}/dependency-diff.html
${{ github.workspace }}/repo-to-check/target/diff/base.json
${{ github.workspace }}/repo-to-check/target/diff/head.json
retention-days: 30
1 change: 1 addition & 0 deletions common/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbom-workspace
12 changes: 12 additions & 0 deletions common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@ git push ...
```

Make sure to commit and push the changes to the `dist` directory to the repository.

# How to test

Run `npm run test` to run the unit tests

# How to try SBOM diffing

1. Generate two SBOMs that you want to compare
1. For example, use `mvn org.cyclonedx:cyclonedx-maven-plugin:2.7.9:makeAggregateBom` to generate an SBOM for a maven (multi-module) project
1. Run `npm run diff-sboms <path to base SBOM> <path to comparing SBOM> <output file path>` to generate an SBOM diff
1. Hint: The `sbom-workspace` subdirectory is in `.gitignore`, so you can put files there
1. In Visual Studio Code, you can run the script from the Javascript Debugger Console to attach a debugger and put breakpoints in the business logic
50 changes: 50 additions & 0 deletions common/diff-sboms-standalone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const diffSBOMs = require('./src/sbom-diff/differ.js');
const formatTemplate = require('./src/sbom-diff/format-handlebars-template.js');
const fs = require('fs');

const readFile = function(path) {
return fs.readFileSync(path, 'utf8')
}

const writeFile = function(path, content) {
fs.writeFileSync(path, content); // default encoding is utf8
}

var args = process.argv.slice(2); // first two arguments are the executable and the JS file

if (args.length !== 3) {
throw new Error('Requires three arguments: <path to base SBOM> <path to comparing SBOM> <path to output file>');
}

const baseSbomPath = args[0];
const headSbomPath = args[1];
const outPath = args[2];

const baseSbom = readFile(baseSbomPath);
const headSbom = readFile(headSbomPath);

const licenseList = readFile('../java-dependency-check/licenses.json');

const commentTemplate = readFile('../java-dependency-check/diff.hbs');

const partialPaths = [
'componentDetails:../java-dependency-check/component-details.hbs',
'componentDiff:../java-dependency-check/component-diff.hbs',
'componentTree:../java-dependency-check/component-tree.hbs',
'componentVersion:../java-dependency-check/component-version.hbs'
];

const partials = partialPaths.reduce(
(result, input) => {
[ partialId, partialPath ] = input.split(':');
result[partialId.trim()] = readFile(partialPath.trim());
return result;
},
{}
);
Comment on lines +30 to +44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Interesting way of doing this :) But it does the job.
I guess it's because it uses the (almost) same format as the workflow parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if there's a more elegant way to convert these parameters into a map. I have rather basic Javascript standard library knowledge (and google skills).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's okay!
I later realized that it uses the same format as the workflow input parameter (which is just a string not a JSON object or map), so it makes sense. 👍


diffSBOMs(baseSbom, headSbom, '^org\\.camunda', licenseList)
.then(rootComponentDiff => formatTemplate(rootComponentDiff, commentTemplate, partials)
.then(diff => writeFile(outPath, diff.fullDiff)));


Loading