fix: DecoderCollection discarding input from non-seekable Readers (#2878)

Signed-off-by: Russell Haering <russellhaering@gmail.com>
This commit is contained in:
Russell Haering 2024-05-16 12:17:11 -07:00 committed by GitHub
parent 15c9fe092a
commit 1bec1fc5d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 4 deletions

View File

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/format/internal/stream"
"github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/sbom"
) )
@ -20,10 +22,16 @@ func NewDecoderCollection(decoders ...sbom.FormatDecoder) sbom.FormatDecoder {
} }
// Decode takes a set of bytes and attempts to decode it into an SBOM relative to the decoders in the collection. // Decode takes a set of bytes and attempts to decode it into an SBOM relative to the decoders in the collection.
func (c *DecoderCollection) Decode(reader io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) { func (c *DecoderCollection) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) {
if reader == nil { if r == nil {
return nil, "", "", fmt.Errorf("no SBOM bytes provided") return nil, "", "", fmt.Errorf("no SBOM bytes provided")
} }
reader, err := stream.SeekableReader(r)
if err != nil {
return nil, "", "", fmt.Errorf("unable to create a seekable reader: %w", err)
}
var bestID sbom.FormatID var bestID sbom.FormatID
for _, d := range c.decoders { for _, d := range c.decoders {
id, version := d.Identify(reader) id, version := d.Identify(reader)
@ -45,10 +53,17 @@ func (c *DecoderCollection) Decode(reader io.Reader) (*sbom.SBOM, sbom.FormatID,
} }
// Identify takes a set of bytes and attempts to identify the format of the SBOM relative to the decoders in the collection. // Identify takes a set of bytes and attempts to identify the format of the SBOM relative to the decoders in the collection.
func (c *DecoderCollection) Identify(reader io.Reader) (sbom.FormatID, string) { func (c *DecoderCollection) Identify(r io.Reader) (sbom.FormatID, string) {
if reader == nil { if r == nil {
return "", "" return "", ""
} }
reader, err := stream.SeekableReader(r)
if err != nil {
log.Debugf("unable to create a seekable reader: %v", err)
return "", ""
}
for _, d := range c.decoders { for _, d := range c.decoders {
id, version := d.Identify(reader) id, version := d.Identify(reader)
if id != "" && version != "" { if id != "" && version != "" {

View File

@ -2,12 +2,14 @@ package format
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/format/spdxjson"
"github.com/anchore/syft/syft/format/syftjson" "github.com/anchore/syft/syft/format/syftjson"
"github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/sbom"
) )
@ -37,6 +39,17 @@ func TestIdentify(t *testing.T) {
} }
} }
func TestDecodeUnseekable(t *testing.T) {
reader, err := os.Open("spdxjson/test-fixtures/spdx/example7-go-module.spdx.json")
assert.NoError(t, err)
// io.NopCloser wraps the reader in a non-seekable type
unseekableReader := io.NopCloser(reader)
_, formatID, _, err := Decode(unseekableReader)
assert.NoError(t, err)
assert.Equal(t, spdxjson.ID, formatID)
}
func TestFormats_EmptyInput(t *testing.T) { func TestFormats_EmptyInput(t *testing.T) {
for _, format := range Decoders() { for _, format := range Decoders() {
name := strings.Split(fmt.Sprintf("%#v", format), "{")[0] name := strings.Split(fmt.Sprintf("%#v", format), "{")[0]