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 }}