Skip to content
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
3 changes: 3 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ jobs:
with:
go-version: '1.20.1'

- name: Setup Gradle
uses: gradle/gradle-build-action@v3

- name: Install project modules
run: npm ci

Expand Down
19 changes: 19 additions & 0 deletions .idea/codeStyles/Project.xml

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

5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

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

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

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

8 changes: 7 additions & 1 deletion package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"dependencies": {
"@babel/core": "^7.23.2",
"@cyclonedx/cyclonedx-library": "^4.0.0",
"fast-toml": "^0.5.4",
"fast-xml-parser": "^4.2.4",
"help": "^3.0.2",
"packageurl-js": "^1.0.2",
Expand Down
2 changes: 1 addition & 1 deletion src/analysis.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const rhdaOperationTypeHeader = "rhda-operation-type"

/**
* Send a stack analysis request and get the report as 'text/html' or 'application/json'.
* @param {import('./provider').Provider} provider - the provided data for constructing the request
* @param {import('./provider').Provider | import('./providers/base_java.js').default } provider - the provided data for constructing the request
* @param {string} manifest - path for the manifest
* @param {string} url - the backend url to send the request to
* @param {boolean} [html=false] - true will return 'text/html', false will return 'application/json'
Expand Down
5 changes: 3 additions & 2 deletions src/provider.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import javaMvnProvider from './providers/java_maven.js'
import javascriptNpmProvider from './providers/javascript_npm.js'
import golangGomodulesProvider from './providers/golang_gomodules.js'
import pythonPipProvider from './providers/python_pip.js'
import path from 'node:path'
import Java_maven from "./providers/java_maven.js";
import Java_gradle from "./providers/java_gradle.js";

/** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */
/** @typedef {{isSupported: function(string): boolean, provideComponent: function(string, {}): Provided, provideStack: function(string, {}): Provided}} Provider */
Expand All @@ -11,7 +12,7 @@ import path from 'node:path'
* MUST include all providers here.
* @type {[Provider]}
*/
export const availableProviders = [javaMvnProvider,javascriptNpmProvider,golangGomodulesProvider,pythonPipProvider]
export const availableProviders = [new Java_maven(),new Java_gradle(),javascriptNpmProvider,golangGomodulesProvider,pythonPipProvider]

/**
* Match a provider from a list or providers based on file type.
Expand Down
142 changes: 142 additions & 0 deletions src/providers/base_java.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {execSync} from "node:child_process"
import {PackageURL} from 'packageurl-js'


/** @typedef {import('../provider').Provider} */

/** @typedef {import('../provider').Provided} Provided */

/** @typedef {{name: string, version: string}} Package */

/** @typedef {{groupId: string, artifactId: string, version: string, scope: string, ignore: boolean}} Dependency */

/**
* @type {string} ecosystem for java maven packages.
* @private
*/
export const ecosystem_maven = 'maven'
export const ecosystem_gradle = 'gradle'
export default class Base_Java {
constructor() {
}



DEP_REGEX = /(([-a-zA-Z0-9._]{2,})|[0-9])/g
CONFLICT_REGEX = /.*omitted for conflict with (\S+)\)/

/**
* Recursively populates the SBOM instance with the parsed graph
* @param {string} src - Source dependency to start the calculations from
* @param {number} srcDepth - Current depth in the graph for the given source
* @param {Array} lines - Array containing the text files being parsed
* @param {Sbom} sbom - The SBOM where the dependencies are being added
*/
parseDependencyTree(src, srcDepth, lines, sbom) {
if (lines.length === 0) {
return;
}
if ((lines.length === 1 && lines[0].trim() === "")) {
return;
}
let index = 0;
let target = lines[index];
let targetDepth = this.#getDepth(target);
while (targetDepth > srcDepth && index < lines.length) {
if (targetDepth === srcDepth + 1) {
let from = this.parseDep(src);
let to = this.parseDep(target);
let matchedScope = target.match(/:compile|:provided|:runtime|:test|:system/g)
let matchedScopeSrc = src.match(/:compile|:provided|:runtime|:test|:system/g)
// only add dependency to sbom if it's not with test scope or if it's root
if ((matchedScope && matchedScope[0] !== ":test" && (matchedScopeSrc && matchedScopeSrc[0] !== ":test")) || (srcDepth == 0 && matchedScope && matchedScope[0] !== ":test")) {
sbom.addDependency(sbom.purlToComponent(from), to)
}
} else {
this.parseDependencyTree(lines[index - 1], this.#getDepth(lines[index - 1]), lines.slice(index), sbom)
}
target = lines[++index];
targetDepth = this.#getDepth(target);
}
}

/**
* Calculates how deep in the graph is the given line
* @param {string} line - line to calculate the depth from
* @returns {number} The calculated depth
* @private
*/
#getDepth(line) {
if (line === undefined) {
return -1;
}
return ((line.indexOf('-') - 1) / 3) + 1;
}

/**
* Create a PackageURL from any line in a Text Graph dependency tree for a manifest path.
* @param {string} line - line to parse from a dependencies.txt file
* @returns {PackageURL} The parsed packageURL
*/
parseDep(line) {

let match = line.match(this.DEP_REGEX);
if (!match) {
throw new Error(`Unable generate SBOM from dependency tree. Line: ${line} cannot be parsed into a PackageURL`);
}
let version
if (match.length >= 5 && ['compile', 'provided', 'runtime'].includes(match[5])) {
version = `${match[4]}-${match[3]}`
} else {
version = match[3]
}
let override = line.match(this.CONFLICT_REGEX);
if (override) {
version = override[1];
}
return this.toPurl(match[0], match[1], version);
}

/**
* Returns a PackageUrl For Java maven dependencies
* @param group
* @param artifact
* @param version
* @return {PackageURL}
*/
toPurl(group, artifact, version) {
if (typeof version === "number") {
version = version.toString()
}
return new PackageURL('maven', group, artifact, version, undefined, undefined);
}

/** this method invokes command string in a process in a synchronous way.
* @param cmdString - the command to be invoked
* @param errorMessage - the message to print to an exception thrown to client in case of error
* @protected
*/
_invokeCommand(cmdString, errorMessage) {
execSync(cmdString, err => {
if (err) {
throw new Error(`${errorMessage}`)
}
})
}

/** this method invokes command string in a process in a synchronous way.
* @param cmdString - the command to be invoked
* @param workingDir - the directory in which the command will be invoked
* @return the output of the command
* @protected
*/
_invokeCommandGetOutput(cmdString, workingDir) {
let opts = {}
if(workingDir) {
opts.cwd = workingDir
}
return execSync(cmdString, opts)
}


}
Loading