From 09c4ad36e61a324c89f3287ccee7ac8c71b4bf6b Mon Sep 17 00:00:00 2001
From: Ruben Romero Montes
Date: Tue, 18 Nov 2025 14:46:06 +0100
Subject: [PATCH 1/2] feat: support image scan in cli
Signed-off-by: Ruben Romero Montes
---
README.md | 50 +++++++++++++++++++++++++++++++++++++++++++-
src/cli.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 7ee4915..abd94f4 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,14 @@ let stackAnalysis = await exhort.stackAnalysis('/path/to/pom.xml')
let stackAnalysisHtml = await exhort.stackAnalysis('/path/to/pom.xml', true)
// Get component analysis in JSON format
let componentAnalysis = await exhort.componentAnalysis('/path/to/pom.xml')
+// Get image analysis in JSON format
+let imageAnalysis = await exhort.imageAnalysis(['docker.io/library/node:18'])
+// Get image analysis in HTML format (string)
+let imageAnalysisHtml = await exhort.imageAnalysis(['docker.io/library/node:18'], true)
+// Analyze multiple images
+let multipleImagesAnalysis = await exhort.imageAnalysis(['docker.io/library/node:18', 'docker.io/library/python:3.9'])
+// Specify architecture using ^^ notation (e.g., httpd:2.4.49^^amd64)
+let imageAnalysisWithArch = await exhort.imageAnalysis(['httpd:2.4.49^^amd64'])
```
@@ -68,11 +76,12 @@ Use as CLI Script
```shell
$ npx @trustify-da/trustify-da-javascript-client help
-Usage: trustify-da-javascript-client {component|stack}
+Usage: trustify-da-javascript-client {component|stack|image|validate-token}
Commands:
trustify-da-javascript-client stack [--html|--summary] produce stack report for manifest path
trustify-da-javascript-client component [--summary] produce component report for a manifest type and content
+ trustify-da-javascript-client image [--html|--summary] produce image analysis report for OCI image references
Options:
--help Show help [boolean]
@@ -91,6 +100,22 @@ $ npx @trustify-da/trustify-da-javascript-client stack /path/to/pom.xml --html
# get component analysis
$ npx @trustify-da/trustify-da-javascript-client component /path/to/pom.xml
+
+# get image analysis in json format
+$ npx @trustify-da/trustify-da-javascript-client image docker.io/library/node:18
+
+# get image analysis in json format (summary only)
+# Note: summary returns an object with imageRef as key
+$ npx @trustify-da/trustify-da-javascript-client image docker.io/library/node:18 --summary
+
+# get image analysis in html format
+$ npx @trustify-da/trustify-da-javascript-client image docker.io/library/node:18 --html
+
+# analyze multiple images
+$ npx @trustify-da/trustify-da-javascript-client image docker.io/library/node:18 docker.io/library/python:3.9
+
+# specify architecture using ^^ notation (e.g., httpd:2.4.49^^amd64)
+$ npx @trustify-da/trustify-da-javascript-client image httpd:2.4.49^^amd64
```
@@ -113,6 +138,22 @@ $ trustify-da-javascript-client stack /path/to/pom.xml --html
# get component analysis
$ trustify-da-javascript-client component /path/to/pom.xml
+
+# get image analysis in json format
+$ trustify-da-javascript-client image docker.io/library/node:18
+
+# get image analysis in json format (summary only)
+# Note: summary returns an object with imageRef as key
+$ trustify-da-javascript-client image docker.io/library/node:18 --summary
+
+# get image analysis in html format
+$ trustify-da-javascript-client image docker.io/library/node:18 --html
+
+# analyze multiple images
+$ trustify-da-javascript-client image docker.io/library/node:18 docker.io/library/python:3.9
+
+# specify architecture using ^^ notation (e.g., httpd:2.4.49^^amd64)
+$ trustify-da-javascript-client image httpd:2.4.49^^amd64
```
@@ -288,6 +329,13 @@ let stackAnalysisHtml = await exhort.stackAnalysis('/path/to/pom.xml', true, opt
// Get component analysis in JSON format
let componentAnalysis = await exhort.componentAnalysis('/path/to/pom.xml', options)
+
+// Get image analysis in JSON format
+let imageAnalysis = await exhort.imageAnalysis(['docker.io/library/node:18'], false, options)
+// Get image analysis in HTML format in string
+let imageAnalysisHtml = await exhort.imageAnalysis(['docker.io/library/node:18'], true, options)
+// Specify architecture using ^^ notation (e.g., httpd:2.4.49^^amd64)
+let imageAnalysisWithArch = await exhort.imageAnalysis(['httpd:2.4.49^^amd64'], false, options)
```
**_Environment variables takes precedence._**
diff --git a/src/cli.js b/src/cli.js
index c24797c..a453bf4 100644
--- a/src/cli.js
+++ b/src/cli.js
@@ -55,6 +55,64 @@ const validateToken = {
}
}
+// command for image analysis takes OCI image references
+const image = {
+ command: 'image ',
+ desc: 'produce image analysis report for OCI image references',
+ builder: yargs => yargs.positional(
+ 'image-refs',
+ {
+ desc: 'OCI image references to analyze (one or more)',
+ type: 'string',
+ array: true,
+ }
+ ).options({
+ html: {
+ alias: 'r',
+ desc: 'Get the report as HTML instead of JSON',
+ type: 'boolean',
+ conflicts: 'summary'
+ },
+ summary: {
+ alias: 's',
+ desc: 'For JSON report, get only the \'summary\'',
+ type: 'boolean',
+ conflicts: 'html'
+ }
+ }),
+ handler: async args => {
+ let imageRefs = args['image-refs']
+ if (!Array.isArray(imageRefs)) {
+ imageRefs = [imageRefs]
+ }
+ let html = args['html']
+ let summary = args['summary']
+ let res = await exhort.imageAnalysis(imageRefs, html)
+ if(summary && !html) {
+ let summaries = {}
+ for (let [imageRef, report] of Object.entries(res)) {
+ for (let provider in report.providers) {
+ if (report.providers[provider].sources !== undefined) {
+ for (let source in report.providers[provider].sources) {
+ if (report.providers[provider].sources[source].summary) {
+ if (!summaries[imageRef]) {
+ summaries[imageRef] = {};
+ }
+ if (!summaries[imageRef][provider]) {
+ summaries[imageRef][provider] = {};
+ }
+ summaries[imageRef][provider][source] = report.providers[provider].sources[source].summary
+ }
+ }
+ }
+ }
+ }
+ res = summaries
+ }
+ console.log(html ? res : JSON.stringify(res, null, 2))
+ }
+}
+
// command for stack analysis takes a manifest path
const stack = {
command: 'stack [--html|--summary]',
@@ -112,9 +170,10 @@ const stack = {
// parse and invoke the command
yargs(hideBin(process.argv))
- .usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|validate-token}`)
+ .usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|image|validate-token}`)
.command(stack)
.command(component)
+ .command(image)
.command(validateToken)
.scriptName('')
.version(false)
From 6a097460e9fa42bbcf10dafba5bdfb9f7d1f9753 Mon Sep 17 00:00:00 2001
From: Ruben Romero Montes
Date: Tue, 18 Nov 2025 14:50:58 +0100
Subject: [PATCH 2/2] fix: trigger IT only after Tests or manually
Signed-off-by: Ruben Romero Montes
---
.github/workflows/integration.yml | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index 50ec85b..3afcb18 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -8,9 +8,6 @@ on:
types:
- completed
workflow_dispatch:
- pull_request:
- branches:
- - main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -18,13 +15,12 @@ concurrency:
jobs:
call-shared:
- # Only run if the test workflow succeeded, or if triggered directly
+ # Only run if the test workflow succeeded, or if triggered manually
if: |
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') ||
- github.event_name == 'workflow_dispatch' ||
- github.event_name == 'pull_request'
+ github.event_name == 'workflow_dispatch'
uses: trustification/exhort-integration-tests/.github/workflows/integration.yml@main
with:
language: javascript
- repo-url: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
- commit-sha: ${{ github.event.pull_request.head.sha || github.sha }}
+ repo-url: ${{ github.repository }}
+ commit-sha: ${{ github.event.workflow_run && github.event.workflow_run.head_sha || github.sha }}