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
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# FOSSA CLI Changelog

## 3.10.11

- container scanning: fix unzipping JARs that symlink to other layers #1555 ([#1555](https://github.com/fossas/fossa-cli/pull/1555))

## 3.10.10

- go: support the `tool` directive introduced in go Feb 2025 ([#1553](https://github.com/fossas/fossa-cli/pull/1553))
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ lint-cargo:
@cargo clippy -V
@cargo clippy

# Build cargo deps needed b y the CLI and move them into place for cabal.
# Build cargo deps needed by the CLI and move them into place for cabal.
build-embedded-rust-bins: target/release/berkeleydb target/release/millhone
cargo build --release --bin millhone --bin berkeleydb

Expand Down
77 changes: 48 additions & 29 deletions extlib/millhone/src/cmd/analyze_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,15 @@ fn recursive_jars_in_jars(
return Ok(vec![]);
}
let mut discoveries = Vec::new();
let mut archive =
zip::ZipArchive::new(std::io::Cursor::new(jar_contents)).context("unzipping jar")?;
// If the jar is a symlink we find empty contents and run into an error when trying to unzip it.
// Due to this, we have decided to warn instead of error and skip the jar.
let mut archive = match zip::ZipArchive::new(std::io::Cursor::new(jar_contents)) {
Ok(archive) => archive,
Err(e) => {
warn!("failed to unzip jar: {e:?}");
return Ok(vec![]);
}
};
for path in archive.clone().file_names() {
debug!("file_name: {path}");
if !path.ends_with(".jar") {
Expand Down Expand Up @@ -317,70 +324,82 @@ mod tests {
r#"
{{
"discovered_jars": {{
"blobs/sha256/3af1c7e331a4b6791c25101e0c862125a597d8d75d786aead62de19f78a5a992": [
"88a896b18358cbccbf66cc1c3dcd0d2d61504c5bf41284c551e47f230675534f/layer.tar": [
{{
"kind": "v1.discover.binary.jar",
"path": "jars/deepest.jar",
"path": "jars/middle.jar",
"fingerprints": {{
"sha_256": "LsXfP24XYFIZnkS3Z7RaNim1o8/TtGnueThkZv9hCok=",
"v1.mavencentral.jar": "zpVxCUcFUE/F+WFYr7bUWEyA0Go=",
"v1.raw.jar": "b/9aNwWFlSqOwk+nZaG9zot8q+PENy/tn8Y0Xe/y3XY=",
"sha_256": "5Vh9z8oEKud8flIitzCE/rLMfbFlszhGkQbVxfHgg8U=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
}}
}},
{{
"kind": "v1.discover.binary.jar",
"path": "jars/middle.jar{separator}deepest.jar",
"fingerprints": {{
"sha_256": "vpyhj/ImCwcAx4HZTP23XV9yTdNdRF5NQFj8eV5NOHM=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"v1.mavencentral.jar": "1+4xPh5QS5IW0H6lfbxamjtVVdk=",
"v1.mavencentral.jar": "yPAF7stJN1sIjsM4/hSWcGilyz8=",
"v1.raw.jar": "UMQ1yS7xM6tF4YMvAWz8UP6+qAIRq3JauBoiTlVUNkM="
}}
}}
],
"blobs/sha256/5ee98bff2cf0e70d115677fc37f734d26848435eef5fe52e905229ff7a7d87fb": [
"2cd0dec90e9f3f920397ed6bf0ba740493620a99bb20b79d2c4c8159948439e4/layer.tar": [],
"5a99cb0cd20c916ca7444b625ff06e3afe6a1b4349c44c3ba11eb054daf5fda4/layer.tar": [],
"9fea496e8349f2c33fe177df27e4369f08cff62ad40168183493d9de3d6832e5/layer.tar": [
{{
"kind": "v1.discover.binary.jar",
"path": "jars/middle.jar",
"path": "jars/deepest.jar",
"fingerprints": {{
"sha_256": "nKFXVngFtkHIv4FC/rr5o4k+v/KSKzWJ0B9uBuRb+4k=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"v1.mavencentral.jar": "2XA3GFJJkvvpEbAM9nLnAypojEo=",
"v1.raw.jar": "36i3JNvrLMWCMfjB2c9bjQt4Vhmvfq29cb+Hqrb6XeI="
"sha_256": "vpyhj/ImCwcAx4HZTP23XV9yTdNdRF5NQFj8eV5NOHM=",
"v1.mavencentral.jar": "yPAF7stJN1sIjsM4/hSWcGilyz8=",
"v1.raw.jar": "UMQ1yS7xM6tF4YMvAWz8UP6+qAIRq3JauBoiTlVUNkM="
}}
}},
}}
],
"c65b9197c11847b3f36c822b9c57f417af6f721ae6719f8eb3bd334c3516796e/layer.tar": [],
"792ccfdf114be140500ea1d6b99ae7ff0cae6a19b247f886328d4b8a01b8869c/layer.tar": [
{{
"kind": "v1.discover.binary.jar",
"path": "jars/middle.jar{separator}deepest.jar",
"path": "sym-link-test/sym.jar",
"fingerprints": {{
"v1.mavencentral.jar": "1+4xPh5QS5IW0H6lfbxamjtVVdk=",
"sha_256": "LsXfP24XYFIZnkS3Z7RaNim1o8/TtGnueThkZv9hCok=",
"v1.raw.jar": "UMQ1yS7xM6tF4YMvAWz8UP6+qAIRq3JauBoiTlVUNkM=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
"comment_stripped:sha_256": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"sha_256": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"v1.mavencentral.jar": "2jmj7l5rSw0yVb/vlWAYkK/YBwk="
}}
}}
],
"blobs/sha256/6979b741102e5c5c787f94ad8bfdebeee561b1b89f21139d38489e1b3d6f9096": [],
"blobs/sha256/931c525b52485e01ab5e2926a4b3c884f1c7325782dca13bd11e345f46cc34c3": [],
"blobs/sha256/10bb0e91eb016af401369ecaadccfea9f4768776e54d46ad4e9a0309c82f1d7f": [
"d8abb0d1e8708fb1b5b79bcdd098898becfe22019d6b70781974743a90415724/layer.tar": [
{{
"kind": "v1.discover.binary.jar",
"path": "jars/top.jar",
"fingerprints": {{
"v1.raw.jar": "TNW7ezd3fqw3MULVTrexg68Q1x2PTDGk2DkltAqUefk=",
"v1.mavencentral.jar": "TtwsgEXwLd/8UFTohsFhJqYMJ74=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"sha_256": "l9XTA5PwWJhnFlz9t0SWKvr2cHDmcytIVvPsr6vqFis="
"v1.raw.jar": "Qvy1Y7ZCnHpGfy12XszUCq8zyzsTeZ2f0HiMkZKRkzY=",
"sha_256": "RXo50J5YsgsHl56Vfcc3Ee9epeJ2XXmO56Zi/4DAQZM=",
"v1.mavencentral.jar": "NWCmsGqwY5JV85qtij8Nf/IQidw=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
}}
}},
{{
"kind": "v1.discover.binary.jar",
"path": "jars/top.jar{separator}middle.jar",
"fingerprints": {{
"v1.mavencentral.jar": "2XA3GFJJkvvpEbAM9nLnAypojEo=",
"sha_256": "5Vh9z8oEKud8flIitzCE/rLMfbFlszhGkQbVxfHgg8U=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"v1.raw.jar": "36i3JNvrLMWCMfjB2c9bjQt4Vhmvfq29cb+Hqrb6XeI=",
"sha_256": "nKFXVngFtkHIv4FC/rr5o4k+v/KSKzWJ0B9uBuRb+4k="
"v1.mavencentral.jar": "zpVxCUcFUE/F+WFYr7bUWEyA0Go=",
"v1.raw.jar": "b/9aNwWFlSqOwk+nZaG9zot8q+PENy/tn8Y0Xe/y3XY="
}}
}},
{{
"kind": "v1.discover.binary.jar",
"path": "jars/top.jar{separator}middle.jar{separator}deepest.jar",
"fingerprints": {{
"v1.mavencentral.jar": "yPAF7stJN1sIjsM4/hSWcGilyz8=",
"sha_256": "vpyhj/ImCwcAx4HZTP23XV9yTdNdRF5NQFj8eV5NOHM=",
"v1.raw.jar": "UMQ1yS7xM6tF4YMvAWz8UP6+qAIRq3JauBoiTlVUNkM=",
"sha_256": "LsXfP24XYFIZnkS3Z7RaNim1o8/TtGnueThkZv9hCok=",
"v1.mavencentral.jar": "1+4xPh5QS5IW0H6lfbxamjtVVdk=",
"v1.class.jar": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
}}
}}
Expand Down
4 changes: 2 additions & 2 deletions test/App/Fossa/Container/AnalyzeNativeSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ nestedJarsInContainerSpec :: Spec
nestedJarsInContainerSpec = describe "Nested Jars in Containers" $ do
currDir <- runIO getCurrentDir
let imageArchivePath = currDir </> nestedJarsInContainerImage
baseLayerId = "sha256:3af1c7e331a4b6791c25101e0c862125a597d8d75d786aead62de19f78a5a992"
baseLayerId = "sha256:e525e941002b68382c246b973b465308be906b5051f4de84d0a047c3d24e6e73"
otherLayerId = "sha256:6979b741102e5c5c787f94ad8bfdebeee561b1b89f21139d38489e1b3d6f9096"

it' "Reads and merges the layers correctly" $ do
Expand All @@ -271,5 +271,5 @@ nestedJarsInContainerSpec = describe "Nested Jars in Containers" $ do
-- It also directly includes middle.jar and deepest.jar
-- So we should find 6 total jars: three from top.jar and its nested jars, two from middle.jar and its nested jar and then deepest.jar
-- See test/App/Fossa/Container/testdata/nested-jar/README.md for info on how nested_jars.tar was made
(length <$> Map.lookup baseLayerId observationsMap) `shouldBe'` Just 6
(length <$> Map.lookup baseLayerId observationsMap) `shouldBe'` Just 7
(length <$> Map.lookup otherLayerId observationsMap) `shouldBe'` Just 0
3 changes: 2 additions & 1 deletion test/App/Fossa/Container/testdata/nested-jar/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ RUN mkdir /jars
COPY top.jar /jars
COPY middle.jar /jars
COPY deepest.jar /jars

RUN mkdir /sym-link-test
RUN ln -s /jars/deepest.jar /sym-link-test/sym.jar
29 changes: 24 additions & 5 deletions test/App/Fossa/Container/testdata/nested-jar/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
# Making test/Container/testdata/nested-jar.tar
# Using this test suite

**If you make any changes to this suite ensure that you manually create the tar file. The test suite does not automatically create the image for testing.**

This test suite tests the CLI's capability to recursively unpack archived files in a container image for the purpose of JAR analysis.

## Building the tar file

Running the following commands in order will create `nested_jars.tar` for the purpose of testing.

```
./make-nested-jars
docker build -t nested_jars .
docker save nested_jars > ../nested_jars.tar
```

After this is run, ensure that you commit `nested_jars.tar` and any other files in this directory that you used to build it.

The existing assertions in the test files will likely need to be updated as well.

## Notes on the tar file

You will end up with a container with a jar called top.jar in it
top.jar contains middle.jar
middle.jar contains deepest.jar
Running the make command results in a container with:
- jar called top.jar in it
- top.jar contains middle.jar
- middle.jar contains deepest.jar
- sym.jar symlinks to deepest.jar (for the sake of testing symlinks between layers)

These are not actual jar files. They're just zip files with a single text file in them.
These are not actual jar files. They're just zip files with a single text file in them.
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/opt/homebrew/bin/bash
# This script creates a nested jar in the current directory for testing purposes.
# Refer to README.md for usage instructions.

rm -rf nested-jar
mkdir nested-jar
cd nested-jar || exit
Expand Down
4 changes: 2 additions & 2 deletions test/App/Fossa/Container/testdata/nested_jars.tar
Git LFS file not shown
Loading