mirror of
https://github.com/anchore/syft.git
synced 2026-10-11 21:57:22 +02:00
* fix: scan a Java resource adapter (.rar) given as a file source The file source picks an extractor for the scan target from its file name alone. A Java resource adapter archive (JCA) uses the .rar extension but is a zip file, so it was handed to the RAR extractor. With rardecode v2.2.0 the RAR extractor does not return on non-RAR input (an infinite loop in its signature search), so `syft scan --from file adapter.rar` never finished. The same bytes named .jar scan in under a second. When the name says RAR but the content is a zip archive, leave the file unextracted, the same way .jar, .war and .ear scan targets are handled. The java cataloger already reads .rar files (anchore/syft#4136) and now finds the resource adapter and its nested jars. Real RAR archives are still extracted. Signed-off-by: Ismael Donmez <ismael.donmez@chainguard.dev> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore(deps): bump github.com/nwaples/rardecode/v2 from 2.2.0 to 2.4.1 rardecode v2.2.0 loops forever in its RAR signature search when the input does not start with a RAR signature (fixed upstream in nwaples/rardecode f501ac8, "fix infinite loop in findSig", released in v2.2.2). syft reaches that search for any file source whose name contains ".rar", so a scan of a self-extracting RAR, or of any other file with such a name, never finished. With v2.4.1 a self-extracting RAR is extracted, and a file that is not a RAR archive fails fast with "rardecode: RAR signature not found", the same way a file named .zip that is not a zip archive fails today. github.com/mholt/archives v0.1.5 (the latest release) still requires v2.2.0, so the newer version is selected here. Signed-off-by: Ismael Donmez <ismael.donmez@chainguard.dev> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: clarify the .rar zip log line and cover self-extracting RAR sources - the debug line no longer calls every `.rar`-named zip a resource adapter - a self-extracting RAR (stub before the signature) given as a file source is still extracted Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Ismael Donmez <ismael.donmez@chainguard.dev> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"archive/tar"
|
|
"archive/zip"
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestArchiveScan(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
archiveFixture string
|
|
env map[string]string
|
|
assertions []traitAssertion
|
|
}{
|
|
{
|
|
name: "scan an archive within the temp dir",
|
|
args: []string{
|
|
"scan",
|
|
"-o",
|
|
"json",
|
|
"file:" + createArchive(t, "testdata/archive", t.TempDir()),
|
|
},
|
|
assertions: []traitAssertion{
|
|
assertSuccessfulReturnCode,
|
|
assertJsonReport,
|
|
assertPackageCount(1),
|
|
},
|
|
},
|
|
{
|
|
// a Java resource adapter archive is a zip file with a .rar extension
|
|
name: "scan a java resource adapter archive",
|
|
args: []string{
|
|
"scan",
|
|
"-o",
|
|
"json",
|
|
"--from",
|
|
"file",
|
|
createResourceAdapterArchive(t, t.TempDir()),
|
|
},
|
|
assertions: []traitAssertion{
|
|
assertSuccessfulReturnCode,
|
|
assertJsonReport,
|
|
assertInOutput("pkg:maven/org.example/example-ra@1.0.0"),
|
|
assertInOutput("pkg:maven/org.example/example-lib@1.0.0"),
|
|
assertPackageCount(2),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
cmd, stdout, stderr := runSyft(t, test.env, test.args...)
|
|
for _, traitAssertionFn := range test.assertions {
|
|
traitAssertionFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
|
}
|
|
logOutputOnFailure(t, cmd, stdout, stderr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func createArchive(t *testing.T, path string, destDir string) string {
|
|
// create a tarball of the test fixtures (not by shelling out)
|
|
archivePath := filepath.Join(destDir, "test.tar")
|
|
|
|
fh, err := os.Create(archivePath)
|
|
require.NoError(t, err)
|
|
defer fh.Close()
|
|
|
|
writer := tar.NewWriter(fh)
|
|
require.NoError(t, writer.AddFS(os.DirFS(path)))
|
|
require.NoError(t, writer.Close())
|
|
|
|
return archivePath
|
|
}
|
|
|
|
// createResourceAdapterArchive writes a minimal Java resource adapter archive (a zip file with a .rar extension that
|
|
// holds a deployment descriptor and a nested jar) into destDir and returns its path.
|
|
func createResourceAdapterArchive(t *testing.T, destDir string) string {
|
|
lib := zipBytes(t, [][2]string{
|
|
{"META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n"},
|
|
{"META-INF/maven/org.example/example-lib/pom.properties", "groupId=org.example\nartifactId=example-lib\nversion=1.0.0\n"},
|
|
})
|
|
|
|
ra := zipBytes(t, [][2]string{
|
|
{"META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n"},
|
|
{"META-INF/ra.xml", "<connector/>\n"},
|
|
{"example-lib-1.0.0.jar", string(lib)},
|
|
{"META-INF/maven/org.example/example-ra/pom.properties", "groupId=org.example\nartifactId=example-ra\nversion=1.0.0\n"},
|
|
})
|
|
|
|
archivePath := filepath.Join(destDir, "example-ra-1.0.0.rar")
|
|
require.NoError(t, os.WriteFile(archivePath, ra, 0o600))
|
|
return archivePath
|
|
}
|
|
|
|
// zipBytes returns a zip archive holding the given (name, contents) entries, in order.
|
|
func zipBytes(t *testing.T, entries [][2]string) []byte {
|
|
var buf bytes.Buffer
|
|
w := zip.NewWriter(&buf)
|
|
for _, e := range entries {
|
|
f, err := w.Create(e[0])
|
|
require.NoError(t, err)
|
|
_, err = f.Write([]byte(e[1]))
|
|
require.NoError(t, err)
|
|
}
|
|
require.NoError(t, w.Close())
|
|
return buf.Bytes()
|
|
}
|