Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 2 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
],
"rules": {
"curly": "warn",
"eqeqeq": "warn",
"eqeqeq": ["warn", "always", {"null": "never"}],
"no-throw-literal": "warn",
"sort-imports": "warn"
},
"ignorePatterns": [
"integration"
]
}
}
11 changes: 11 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Setup syft
uses: jaxxstorm/[email protected]
with:
repo: anchore/syft
platform: linux
arch: amd64
# tag: the latest one, so we can catch changes

- name: Setup skopeo
run: sudo apt update && sudo apt-get -y install skopeo

- name: Install project modules
run: npm ci

Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ jobs:
- name: Prepare PNPM
run: corepack prepare pnpm@latest --activate


- name: Setup Java 17
uses: actions/setup-java@v4
with:
Expand All @@ -76,6 +75,17 @@ jobs:
- name: Setup Gradle
uses: gradle/gradle-build-action@v3

- name: Setup syft
uses: jaxxstorm/[email protected]
with:
repo: anchore/syft
platform: linux
arch: amd64
# tag: the latest one, so we can catch changes

- name: Setup skopeo
run: sudo apt update && sudo apt-get -y install skopeo

- name: Configure git
run: |
git config user.name "${{ github.actor }}"
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 52 additions & 3 deletions src/analysis.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import fs from "node:fs";
import path from "node:path";
import {EOL} from "os";
import {RegexNotToBeLogged, getCustom} from "./tools.js";
import { EOL } from "os";

Check warning on line 3 in src/analysis.js

View workflow job for this annotation

GitHub Actions / Lint and test project (18)

Imports should be sorted alphabetically
import { RegexNotToBeLogged, getCustom } from "./tools.js";

Check warning on line 4 in src/analysis.js

View workflow job for this annotation

GitHub Actions / Lint and test project (18)

Expected 'multiple' syntax before 'single' syntax
import { HttpsProxyAgent } from "https-proxy-agent";
import { generateImageSBOM, parseImageRef } from "./oci_image/utils.js";

Check warning on line 6 in src/analysis.js

View workflow job for this annotation

GitHub Actions / Lint and test project (18)

Expected 'multiple' syntax before 'single' syntax

export default { requestComponent, requestStack, validateToken }
export default { requestComponent, requestStack, requestImages, validateToken }

const rhdaTokenHeader = "rhda-token";
const rhdaSourceHeader = "rhda-source"
Expand Down Expand Up @@ -133,6 +134,54 @@
return Promise.resolve(result)
}

/**
*
* @param {Array<string>} imageRefs
* @param {string} url
* @param {{}} [opts={}] - optional various options to pass along the application
* @returns {Promise<string|import('../generated/backend/AnalysisReport').AnalysisReport>}
*/
async function requestImages(imageRefs, url, html = false, opts = {}) {
const imageSboms = {}
for (const image of imageRefs) {
const parsedImageRef = parseImageRef(image)
imageSboms[parsedImageRef.getPackageURL().toString()] = generateImageSBOM(parsedImageRef)
}

const resp = await fetch(`${url}/api/v4/batch-analysis`, {
method: 'POST',
headers: {
'Accept': html ? 'text/html' : 'application/json',
'Content-Type': 'application/vnd.cyclonedx+json',
...getTokenHeaders(opts)
},
body: JSON.stringify(imageSboms),
})

console.error(JSON.stringify(imageSboms, '', '\t'))

if(resp.status === 200) {
let result;
if (!html) {
result = await resp.json()
} else {
result = await resp.text()
}
if (process.env["EXHORT_DEBUG"] === "true") {
let exRequestId = resp.headers.get("ex-request-id");
if (exRequestId) {
console.log("Unique Identifier associated with this request - ex-request-id=" + exRequestId)
}
console.log("Response body received from exhort server : " + EOL + EOL)
console.log(JSON.stringify(result, null, 4))
console.log("Ending time of sending component analysis request to exhort server= " + new Date())
}
return result
} else {
throw new Error(`Got error response from exhort backend - http return code : ${resp.status}, ex-request-id: ${resp.headers.get("ex-request-id")} error message => ${await resp.text()}`)
}
}

/**
*
* @param url the backend url to send the request to
Expand Down
35 changes: 34 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getCustom } from "./tools.js";
import.meta.dirname
import * as url from 'url';

export default { componentAnalysis, stackAnalysis, validateToken }
export default { componentAnalysis, stackAnalysis, imageAnalysis, validateToken }

export const exhortDevDefaultUrl = 'https://exhort.stage.devshift.net';

Expand Down Expand Up @@ -148,6 +148,39 @@ async function componentAnalysis(manifest, opts = {}) {
return await analysis.requestComponent(provider, manifest, theUrl, opts) // throws error request sending failed
}

/**
* @overload
* @param {Array<string>} imageRefs
* @param {true} html
* @param {object} [opts={}]
* @returns {Promise<string>}
* @throws {Error}
*/

/**
* @overload
* @param {Array<string>} imageRefs
* @param {false} html
* @param {object} [opts={}]
* @returns {Promise<import('@trustification/exhort-api-spec/model/v4/AnalysisReport.js').AnalysisReport}
* @throws {Error}
*/

/**
* Get image analysis report for a set of OCI image references.
* @overload
* @param {Array<string>} imageRefs - OCI image references
* @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
* @param {{}} [opts={}] - optional various options to pass along the application
* @returns {Promise<string|import('@trustification/exhort-api-spec/model/v4/AnalysisReport.js').AnalysisReport}
* @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
* or backend request failed
*/
async function imageAnalysis(imageRefs, html = false, opts = {}) {
theUrl = selectExhortBackend(opts)
return await analysis.requestImages(imageRefs, theUrl, opts)
}

/**
* Validates the Exhort token.
* @param {object} [opts={}] - Optional parameters, potentially including token override.
Expand Down
Loading
Loading