Skip to content

Commit

Permalink
Improved detection for standalone jar files by using pom.properties f…
Browse files Browse the repository at this point in the history
…ile if available (#724)

* parse pom properties files for stanadalone jars

use pom.properties if available
fallback to old logic if pom properties is not available
extract only META-INF from jar

Signed-off-by: Nikemare <[email protected]>

* Formatted utils.js

Signed-off-by: Nikemare <[email protected]>

* Fixed console.log

Signed-off-by: Nikemare <[email protected]>

* Better confidence info in extractJarArchive

Signed-off-by: Nikemare <[email protected]>

* Added repotest for standalone jar files

Signed-off-by: Nikemare <[email protected]>

* Fix repotest for standalone jar files

Missed to specify directory in repotest for standalone jar files

Signed-off-by: Nikemare <[email protected]>

---------

Signed-off-by: Nikemare <[email protected]>
  • Loading branch information
Nikemare authored Nov 19, 2023
1 parent b397bae commit b0105c9
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 54 deletions.
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
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]));
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

0 comments on commit b0105c9

Please sign in to comment.