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

Improved detection for standalone jar files by using pom.properties file if available #724

Merged
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
10 changes: 10 additions & 0 deletions .github/workflows/repotests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ jobs:
mv *.hpi jenkins
CDXGEN_DEBUG_MODE=debug bin/cdxgen.js -p -r -t jenkins jenkins -o bomresults/bom-jenkins.json --validate
shell: bash
- name: standalone jar files
run: |
mkdir -p standalone-jar-files
curl --output-dir standalone-jar-files -LO https://repo1.maven.org/maven2/org/jacoco/org.jacoco.report/0.8.8/org.jacoco.report-0.8.8.jar
curl --output-dir standalone-jar-files -LO https://repo1.maven.org/maven2/org/apache/ws/xmlschema/xmlschema-core/2.2.5/xmlschema-core-2.2.5.jar
curl --output-dir standalone-jar-files -LO https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.16.0/jackson-core-2.16.0.jar
curl --output-dir standalone-jar-files -LO https://repo1.maven.org/maven2/junit/junit/4.13.2/junit-4.13.2.jar
curl --output-dir standalone-jar-files -LO https://repo1.maven.org/maven2/wsdl4j/wsdl4j/1.6.3/wsdl4j-1.6.3.jar
FETCH_LICENSE=true bin/cdxgen.js -p standalone-jar-files -o bomresults/bom-standalone-jar-files.json --validate
Copy link
Collaborator

Choose a reason for hiding this comment

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

Well spotted!

shell: bash
- name: repotests 1.4
run: |
bin/cdxgen.js -p -r -t java repotests/shiftleft-java-example -o bomresults/bom-java.json --generate-key-and-sign --spec-version 1.4
Expand Down
163 changes: 109 additions & 54 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
readFileSync,
rmSync,
unlinkSync,
writeFileSync
writeFileSync,
readdirSync
} from "node:fs";
import got from "got";
import Arborist from "@npmcli/arborist";
Expand Down Expand Up @@ -6514,6 +6515,23 @@ export const parseJarManifest = function (jarMetadata) {
return metadata;
};

export const parsePomProperties = function (pomProperties) {
const properties = {};
if (!pomProperties) {
return properties;
}
pomProperties.split("\n").forEach((l) => {
l = l.replace("\r", "");
if (l.includes("=")) {
const tmpA = l.split("=");
if (tmpA && tmpA.length === 2) {
properties[tmpA[0]] = tmpA[1].replace("\r", "");
}
}
});
return properties;
};

export const encodeForPurl = (s) => {
return s && !s.includes("%40")
? encodeURIComponent(s).replace(/%3A/g, ":").replace(/%2F/g, "/")
Expand Down Expand Up @@ -6601,13 +6619,14 @@ export const extractJarArchive = function (
}
const manifestDir = join(tempDir, "META-INF");
const manifestFile = join(manifestDir, "MANIFEST.MF");
const mavenDir = join(manifestDir, "maven");
let jarResult = {
status: 1
};
if (existsSync(pomname)) {
jarResult = { status: 0 };
} else {
jarResult = spawnSync("jar", ["-xf", jf], {
jarResult = spawnSync("jar", ["-xf", jf, "META-INF"], {
encoding: "utf-8",
cwd: tempDir,
shell: isWin,
Expand All @@ -6617,29 +6636,65 @@ export const extractJarArchive = function (
if (jarResult.status !== 0) {
console.error(jarResult.stdout, jarResult.stderr);
} else {
if (existsSync(manifestFile)) {
let group = "",
name = "",
version = "",
confidence = 1,
technique = "manifest-analysis";
// When maven descriptor is available take group, name and version from pom.properties
// META-INF/maven/${groupId}/${artifactId}/pom.properties
// see https://maven.apache.org/shared/maven-archiver/index.html
if (existsSync(mavenDir)) {
let groupDir = readdirSync(mavenDir);
if (groupDir && groupDir.length) {
let artifactDir = readdirSync(join(mavenDir, groupDir[0]));
Copy link
Collaborator

@prabhu prabhu Nov 20, 2023

Choose a reason for hiding this comment

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

@Nikemare, This line is failing while creating an SBOM for container images.

Error: ENOTDIR: not a directory, scandir '/tmp/jar-deps-pgCSWJ/META-INF/maven/extension.xml'
    at readdirSync (node:fs:1516:26)
    at extractJarArchive (file:///mnt/work/CycloneDX/cdxgen/utils.js:6650:31)
    at createJarBom (file:///mnt/work/CycloneDX/cdxgen/index.js:1084:21)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async createMultiXBom (file:///mnt/work/CycloneDX/cdxgen/index.js:4868:15)
    at async createBom (file:///mnt/work/CycloneDX/cdxgen/index.js:5388:21)
    at async file:///mnt/work/CycloneDX/cdxgen/bin/cdxgen.js:366:20 {
  errno: -20,

Below change at 6649 helps

if (
            groupDir &&
            groupDir.length &&
            lstatSync(join(mavenDir, groupDir[0])).isDirectory()
          ) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@prabhu I can provide a fix in the evening (CET), or will you fix it on your own?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The evening sounds good! Thank you so much!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See #727

if (artifactDir && artifactDir.length) {
let pomPropertiesFile = join(
mavenDir,
groupDir[0],
artifactDir[0],
"pom.properties"
);
if (existsSync(pomPropertiesFile)) {
const pomProperties = parsePomProperties(
readFileSync(pomPropertiesFile, {
encoding: "utf-8"
})
);
group = pomProperties["groupId"];
name = pomProperties["artifactId"];
version = pomProperties["version"];
}
}
}
}
if ((!group || !name || !version) && existsSync(manifestFile)) {
confidence = 0.8;
const jarMetadata = parseJarManifest(
readFileSync(manifestFile, {
encoding: "utf-8"
})
);
let group =
group =
group ||
jarMetadata["Extension-Name"] ||
jarMetadata["Implementation-Vendor-Id"] ||
jarMetadata["Bundle-SymbolicName"] ||
jarMetadata["Bundle-Vendor"] ||
jarMetadata["Automatic-Module-Name"] ||
"";
let version =
version =
version ||
jarMetadata["Bundle-Version"] ||
jarMetadata["Implementation-Version"] ||
jarMetadata["Specification-Version"];
if (version && version.includes(" ")) {
version = version.split(" ")[0];
}
let name = "";
// Prefer jar filename to construct name and version
if (!name || !version || name === "" || version === "") {
confidence = 0.5;
technique = "filename";
const tmpA = jarname.split("-");
if (tmpA && tmpA.length > 1) {
const lastPart = tmpA[tmpA.length - 1];
Expand Down Expand Up @@ -6688,56 +6743,56 @@ export const extractJarArchive = function (
break;
}
}
if (name && version) {
// if group is empty use name as group
group = encodeForPurl(group === "." ? name : group || name) || "";
let apkg = {
// if group is empty use name as group
group = group === "." ? name : group || name;
}
if (name && version) {
let apkg = {
group: group ? encodeForPurl(group) : "",
name: name ? encodeForPurl(name) : "",
version,
purl: new PackageURL(
"maven",
group,
name: name ? encodeForPurl(name) : "",
name,
version,
purl: new PackageURL(
"maven",
group,
name,
version,
{ type: "jar" },
null
).toString(),
evidence: {
identity: {
field: "purl",
confidence: 0.5,
methods: [
{
technique: "filename",
confidence: 0.5,
value: jarname
}
]
}
},
properties: [
{
name: "SrcFile",
value: jarname
}
]
};
if (
jarNSMapping &&
jarNSMapping[apkg.purl] &&
jarNSMapping[apkg.purl].namespaces
) {
apkg.properties.push({
name: "Namespaces",
value: jarNSMapping[apkg.purl].namespaces.join("\n")
});
}
pkgList.push(apkg);
} else {
if (DEBUG_MODE) {
console.log(`Ignored jar ${jarname}`, jarMetadata, name, version);
}
{ type: "jar" },
null
).toString(),
evidence: {
identity: {
field: "purl",
confidence: confidence,
methods: [
{
technique: technique,
confidence: confidence,
value: jarname
}
]
}
},
properties: [
{
name: "SrcFile",
value: jarname
}
]
};
if (
jarNSMapping &&
jarNSMapping[apkg.purl] &&
jarNSMapping[apkg.purl].namespaces
) {
apkg.properties.push({
name: "Namespaces",
value: jarNSMapping[apkg.purl].namespaces.join("\n")
});
}
pkgList.push(apkg);
} else {
if (DEBUG_MODE) {
console.log(`Ignored jar ${jarname}`, name, version);
}
}
try {
Expand Down
Loading