diff --git a/java/jar/jar.go b/java/jar/jar.go index 4480b20c9..1d817091f 100644 --- a/java/jar/jar.go +++ b/java/jar/jar.go @@ -90,6 +90,8 @@ func Parse(ctx context.Context, name string, z *zip.Reader) ([]Info, error) { goto Finish case errors.Is(err, errUnpopulated): case strings.HasPrefix(base, "javax") && errors.Is(err, ErrNotAJar): + case errors.Is(err, ErrNotAJar): + return nil, err default: return nil, mkErr(name, err) } @@ -103,6 +105,8 @@ func Parse(ctx context.Context, name string, z *zip.Reader) ([]Info, error) { goto Finish case errors.Is(err, errUnpopulated) || errors.Is(err, errInsaneManifest): case strings.HasPrefix(base, "javax") && errors.Is(err, ErrNotAJar): + case errors.Is(err, ErrNotAJar): + return nil, err default: return nil, mkErr(name, err) } @@ -146,7 +150,7 @@ func extractManifest(ctx context.Context, name string, z *zip.Reader) (Info, err mf, err := z.Open(manifestPath) switch { case errors.Is(err, nil): - case errors.Is(err, fs.ErrNotExist): + case errors.Is(err, fs.ErrNotExist), errors.Is(err, zip.ErrFormat): return Info{}, mkErr("manifest", notAJar(name, err)) default: return Info{}, err @@ -165,10 +169,13 @@ func extractManifest(ctx context.Context, name string, z *zip.Reader) (Info, err func extractProperties(ctx context.Context, name string, z *zip.Reader) ([]Info, error) { const filename = "pom.properties" mf, err := z.Open(`META-INF`) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - return nil, mkErr("properties", notAJar(name, err)) - } + switch { + case errors.Is(err, nil): + case errors.Is(err, fs.ErrNotExist), + errors.Is(err, zip.ErrFormat), + errors.Is(err, zip.ErrChecksum): + return nil, mkErr("properties", notAJar(name, err)) + default: return nil, mkErr("properties", err) } mf.Close() @@ -193,7 +200,11 @@ func extractProperties(ctx context.Context, name string, z *zip.Reader) ([]Info, ret := make([]Info, len(pf)) for i, p := range pf { f, err := z.Open(p) - if err != nil { + switch { + case errors.Is(err, nil): + case errors.Is(err, zip.ErrFormat), errors.Is(err, zip.ErrChecksum): + return nil, mkErr("properties", notAJar(name, err)) + default: return nil, err } err = ret[i].parseProperties(ctx, f) diff --git a/java/jar/jar_test.go b/java/jar/jar_test.go index e5964cfd1..160656128 100644 --- a/java/jar/jar_test.go +++ b/java/jar/jar_test.go @@ -259,6 +259,100 @@ func TestJARBadManifest(t *testing.T) { } } +// TestMalformed creates a malformed zip, then makes sure the package handles it +// gracefully. +func TestMalformed(t *testing.T) { + const ( + jarName = `malformed_zip.jar` + manifest = `testdata/malformed_zip.MF` + ) + t.Parallel() + ctx := zlog.Test(context.Background(), t) + dir := integration.PackageCacheDir(t) + fn := filepath.Join(dir, jarName) +Open: + f, err := os.Open(fn) + switch { + case errors.Is(err, nil): + case errors.Is(err, os.ErrNotExist): + // Create the jar-like. + mk, err := os.Create(fn) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := mk.Close(); err != nil { + t.Logf("non-failing error: %v", err) + } + if t.Failed() { + if err := os.Remove(fn); err != nil { + t.Error(err) + } + } + }() + w := zip.NewWriter(mk) + if _, err := w.Create(`META-INF/`); err != nil { + t.Fatal(err) + } + fw, err := w.Create(`META-INF/MANIFEST.MF`) + if err != nil { + t.Fatal(err) + } + mf, err := os.ReadFile(manifest) + if err != nil { + t.Fatal(err) + } + if _, err := io.Copy(fw, bytes.NewReader(mf)); err != nil { + t.Fatal(err) + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + + // Then, corrupt it. + // Seek to the central directory footer: + pos, err := mk.Seek(-0x16+0x10 /* sizeof(footer) + offset(dir_offset)*/, io.SeekEnd) + if err != nil { + t.Fatal(err) + } + b := make([]byte, 4) + if _, err := io.ReadFull(mk, b); err != nil { + t.Fatal(err) + } + // Offset everything so the reader slowly descends into madness. + b[0] -= 7 + if _, err := mk.WriteAt(b, pos); err != nil { + t.Fatal(err) + } + + if err := mk.Sync(); err != nil { + t.Error(err) + } + goto Open + default: + t.Fatal(err) + } + defer f.Close() + fi, err := f.Stat() + if err != nil { + t.Fatal(err) + } + z, err := zip.NewReader(f, fi.Size()) + if err != nil { + t.Fatal(err) + } + infos, err := Parse(ctx, jarName, z) + t.Logf("returned error: %v", err) + switch { + case errors.Is(err, ErrNotAJar): + default: + t.Fail() + } + if len(infos) != 0 { + t.Errorf("returned infos: %#v", infos) + } +} + func TestManifestSectionReader(t *testing.T) { var ms []string d := os.DirFS("testdata") diff --git a/java/jar/testdata/malformed_zip.MF b/java/jar/testdata/malformed_zip.MF new file mode 100644 index 000000000..616c252ef --- /dev/null +++ b/java/jar/testdata/malformed_zip.MF @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Created-By: 666 (claircore testing) + +Name: foo +Other-Key: blah