chore: centralize temp files and prefer streaming IO (#4668)

* chore: centralize temp files and prefer streaming IO

Catalogers that create temp files ad-hoc can easily forget cleanup,
leaking files on disk. Similarly, io.ReadAll is convenient but risks
OOM on large or malicious inputs.

Introduce internal/tmpdir to manage all cataloger temp storage under
a single root directory with automatic cleanup. Prefer streaming
parsers (bufio.Scanner, json/yaml.NewDecoder, io.LimitReader) over
buffering entire inputs into memory. Add ruleguard rules to enforce
both practices going forward.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* chore: go back to old release parsing

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* simplify to limit reader in version check

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* chore: regex change postponed

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* simplify supplement release to limitreader

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

---------

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
This commit is contained in:
Will Murphy
2026-03-18 10:53:51 -04:00
committed by GitHub
parent a3dacf5ecd
commit e38851143e
56 changed files with 704 additions and 214 deletions
+8 -6
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
@@ -15,6 +14,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -24,17 +24,19 @@ import (
// parseGGUFModel parses a GGUF model file and returns the discovered package.
// This implementation only reads the header portion of the file, not the entire model.
func parseGGUFModel(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
func parseGGUFModel(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
defer internal.CloseAndLogError(reader, reader.Path())
// Create a temporary file for the library to parse
// The library requires a file path, so we create a temp file
tempFile, err := os.CreateTemp("", "syft-gguf-*.gguf")
td := tmpdir.FromContext(ctx)
if td == nil {
return nil, nil, fmt.Errorf("no temp dir factory in context")
}
tempFile, cleanup, err := td.NewFile("syft-gguf-*.gguf")
if err != nil {
return nil, nil, fmt.Errorf("failed to create temp file: %w", err)
}
defer cleanup()
tempPath := tempFile.Name()
defer os.Remove(tempPath)
// Copy and validate the GGUF file header using LimitedReader to prevent OOM
// We use LimitedReader to cap reads at maxHeaderSize (50MB)
+1 -1
View File
@@ -171,7 +171,7 @@ func findReleases(resolver file.Resolver, dbPath string) []linux.Release {
func parseReleasesFromAPKRepository(reader file.LocationReadCloser) []linux.Release {
var releases []linux.Release
reposB, err := io.ReadAll(reader)
reposB, err := io.ReadAll(reader) //nolint:gocritic // regex matching requires full buffer
if err != nil {
log.Tracef("unable to read APK repositories file %q: %+v", reader.RealPath, err)
return nil
+1 -1
View File
@@ -47,7 +47,7 @@ func node(value interface{}) erlangNode {
// parseErlang basic parser for erlang, used by rebar.lock
func parseErlang(reader io.Reader) (erlangNode, error) {
data, err := io.ReadAll(reader)
data, err := io.ReadAll(reader) //nolint:gocritic // custom parser requires []byte
if err != nil {
return node(nil), err
}
@@ -3,7 +3,6 @@ package githubactions
import (
"context"
"fmt"
"io"
"go.yaml.in/yaml/v3"
@@ -25,13 +24,9 @@ type compositeActionRunsDef struct {
}
func parseCompositeActionForActionUsage(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
contents, errs := io.ReadAll(reader)
if errs != nil {
return nil, nil, fmt.Errorf("unable to read yaml composite action file: %w", errs)
}
var ca compositeActionDef
if errs = yaml.Unmarshal(contents, &ca); errs != nil {
var errs error
if errs = yaml.NewDecoder(reader).Decode(&ca); errs != nil {
return nil, nil, fmt.Errorf("unable to parse yaml composite action file: %w", errs)
}
@@ -3,7 +3,6 @@ package githubactions
import (
"context"
"fmt"
"io"
"regexp"
"go.yaml.in/yaml/v3"
@@ -41,14 +40,10 @@ type stepDef struct {
}
func parseWorkflowForWorkflowUsage(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
contents, errs := io.ReadAll(reader)
if errs != nil {
return nil, nil, fmt.Errorf("unable to read yaml workflow file: %w", errs)
}
// parse the yaml file into a generic node to preserve comments
var node yaml.Node
if errs = yaml.Unmarshal(contents, &node); errs != nil {
var errs error
if errs = yaml.NewDecoder(reader).Decode(&node); errs != nil {
return nil, nil, fmt.Errorf("unable to parse yaml workflow file: %w", errs)
}
@@ -79,14 +74,10 @@ func parseWorkflowForWorkflowUsage(_ context.Context, _ file.Resolver, _ *generi
}
func parseWorkflowForActionUsage(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
contents, errs := io.ReadAll(reader)
if errs != nil {
return nil, nil, fmt.Errorf("unable to read yaml workflow file: %w", errs)
}
// parse the yaml file into a generic node to preserve comments
var node yaml.Node
if errs = yaml.Unmarshal(contents, &node); errs != nil {
var errs error
if errs = yaml.NewDecoder(reader).Decode(&node); errs != nil {
return nil, nil, fmt.Errorf("unable to parse yaml workflow file: %w", errs)
}
+52 -12
View File
@@ -23,6 +23,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/cache"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/licenses"
@@ -163,7 +164,10 @@ func (c *goLicenseResolver) getLicensesFromRemote(ctx context.Context, moduleNam
return c.licenseCache.Resolve(fmt.Sprintf("%s/%s", moduleName, moduleVersion), func() ([]pkg.License, error) {
proxies := remotesForModule(c.opts.Proxies, c.opts.NoProxy, moduleName)
urlPrefix, fsys, err := getModule(proxies, moduleName, moduleVersion)
urlPrefix, fsys, cleanup, err := getModule(ctx, proxies, moduleName, moduleVersion)
if cleanup != nil {
defer cleanup()
}
if err != nil {
return nil, err
}
@@ -258,7 +262,7 @@ func processCaps(s string) string {
})
}
func getModule(proxies []string, moduleName, moduleVersion string) (urlPrefix string, fsys fs.FS, err error) {
func getModule(ctx context.Context, proxies []string, moduleName, moduleVersion string) (urlPrefix string, fsys fs.FS, cleanup func(), err error) {
for _, proxy := range proxies {
u, _ := url.Parse(proxy)
if proxy == "direct" {
@@ -267,7 +271,7 @@ func getModule(proxies []string, moduleName, moduleVersion string) (urlPrefix st
}
switch u.Scheme {
case "https", "http":
urlPrefix, fsys, err = getModuleProxy(proxy, moduleName, moduleVersion)
urlPrefix, fsys, cleanup, err = getModuleProxy(ctx, proxy, moduleName, moduleVersion)
case "file":
p := filepath.Join(u.Path, moduleName, "@v", moduleVersion)
urlPrefix = path.Join("file://", p) + "/"
@@ -281,42 +285,78 @@ func getModule(proxies []string, moduleName, moduleVersion string) (urlPrefix st
return
}
func getModuleProxy(proxy string, moduleName string, moduleVersion string) (moduleURL string, out fs.FS, _ error) {
// getModuleProxy downloads a Go module zip from the given proxy and returns a filesystem view of its contents.
// The returned cleanup function closes the underlying temp file and removes it; callers must not use the
// returned fs.FS after calling cleanup.
func getModuleProxy(ctx context.Context, proxy string, moduleName string, moduleVersion string) (moduleURL string, out fs.FS, cleanup func(), _ error) {
u := fmt.Sprintf("%s/%s/@v/%s.zip", proxy, moduleName, moduleVersion)
// get the module zip
log.WithFields("url", u).Info("downloading go module from proxy")
resp, err := http.Get(u) //nolint:gosec
if err != nil {
return "", nil, err
return "", nil, nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
// close the first response before retrying with a lowercased module name
_ = resp.Body.Close()
u = fmt.Sprintf("%s/%s/@v/%s.zip", proxy, strings.ToLower(moduleName), moduleVersion)
// try lowercasing it; some packages have mixed casing that really messes up the proxy
resp, err = http.Get(u) //nolint:gosec
if err != nil {
return "", nil, err
return "", nil, nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return "", nil, fmt.Errorf("failed to get module zip: %s", resp.Status)
return "", nil, nil, fmt.Errorf("failed to get module zip: %s", resp.Status)
}
}
// read the zip
b, err := io.ReadAll(resp.Body)
// stream the zip to a temp file to avoid unbounded memory usage
td := tmpdir.FromContext(ctx)
if td == nil {
return "", nil, nil, fmt.Errorf("no temp dir factory in context")
}
tmpFile, cleanupFile, err := td.NewFile("gomodule-*.zip") //nolint:gocritic // cleanup is returned to caller, not deferred here
if err != nil {
return "", nil, err
return "", nil, nil, fmt.Errorf("failed to create temp file for module zip: %w", err)
}
out, err = zip.NewReader(bytes.NewReader(b), resp.ContentLength)
cleanup = func() {
_ = tmpFile.Close()
cleanupFile()
}
// cap downloads at 500MB to prevent disk exhaustion from malicious proxies
const maxModuleZipSize = 500 * 1024 * 1024
size, err := io.Copy(tmpFile, io.LimitReader(resp.Body, maxModuleZipSize))
if err != nil {
cleanup()
return "", nil, nil, fmt.Errorf("failed to download module zip: %w", err)
}
if size >= maxModuleZipSize {
cleanup()
return "", nil, nil, fmt.Errorf("module zip exceeds %d byte size limit", maxModuleZipSize)
}
if _, err := tmpFile.Seek(0, io.SeekStart); err != nil {
cleanup()
return "", nil, nil, fmt.Errorf("failed to seek module zip: %w", err)
}
out, err = zip.NewReader(tmpFile, size)
if err != nil {
cleanup()
return "", nil, nil, fmt.Errorf("failed to read module zip: %w", err)
}
versionPath := findVersionPath(out, ".")
out = getSubFS(out, versionPath)
return u + "#" + versionPath + "/", out, err
return u + "#" + versionPath + "/", out, cleanup, err
}
func findVersionPath(f fs.FS, dir string) string {
+2 -2
View File
@@ -24,7 +24,7 @@ import (
)
func Test_LicenseSearch(t *testing.T) {
ctx := pkgtest.Context()
ctx := pkgtest.Context(t)
loc1 := file.NewLocation("github.com/someorg/somename@v0.3.2/LICENSE")
loc2 := file.NewLocation("github.com/!cap!o!r!g/!cap!project@v4.111.5/LICENSE.txt")
@@ -321,7 +321,7 @@ func Test_noLocalGoModDir(t *testing.T) {
validTmp := t.TempDir()
require.NoError(t, os.MkdirAll(filepath.Join(validTmp, "mod@ver"), 0700|os.ModeDir))
ctx := pkgtest.Context()
ctx := pkgtest.Context(t)
tests := []struct {
name string
dir string
+1 -1
View File
@@ -324,7 +324,7 @@ func buildModuleRelationships(
}
func (c *goModCataloger) parseModFileContents(reader file.LocationReadCloser) (*modfile.File, error) {
contents, err := io.ReadAll(reader)
contents, err := io.ReadAll(reader) //nolint:gocritic // modfile.Parse requires []byte
if err != nil {
return nil, fmt.Errorf("failed to read go module: %w", err)
}
@@ -3,7 +3,6 @@ package haskell
import (
"context"
"fmt"
"io"
"strings"
"go.yaml.in/yaml/v3"
@@ -42,16 +41,11 @@ type completedSnapshot struct {
// parseStackLock is a parser function for stack.yaml.lock contents, returning all packages discovered.
func parseStackLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
bytes, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to load stack.yaml.lock file: %w", err)
}
var lockFile stackLock
if err := yaml.Unmarshal(bytes, &lockFile); err != nil {
if err := yaml.NewDecoder(reader).Decode(&lockFile); err != nil {
log.WithFields("error", err, "path", reader.RealPath).Trace("failed to parse stack.yaml.lock")
return nil, nil, fmt.Errorf("failed to parse stack.yaml.lock file")
return nil, nil, fmt.Errorf("failed to parse stack.yaml.lock file: %w", err)
}
var (
@@ -3,7 +3,6 @@ package haskell
import (
"context"
"fmt"
"io"
"go.yaml.in/yaml/v3"
@@ -23,16 +22,11 @@ type stackYaml struct {
// parseStackYaml is a parser function for stack.yaml contents, returning all packages discovered.
func parseStackYaml(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
bytes, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to load stack.yaml file: %w", err)
}
var stackFile stackYaml
if err := yaml.Unmarshal(bytes, &stackFile); err != nil {
if err := yaml.NewDecoder(reader).Decode(&stackFile); err != nil {
log.WithFields("error", err, "path", reader.RealPath).Trace("failed to parse stack.yaml")
return nil, nil, fmt.Errorf("failed to parse stack.yaml file")
return nil, nil, fmt.Errorf("failed to parse stack.yaml file: %w", err)
}
var pkgs []pkg.Package
@@ -270,7 +270,7 @@ func (c *NVDAPIClient) fetchPage(ctx context.Context, startIndex int, start, end
// handleRateLimit handles HTTP 429 responses by parsing Retry-After and waiting
func (c *NVDAPIClient) handleRateLimit(ctx context.Context, httpResp *http.Response, attempt int) error {
body, _ := io.ReadAll(httpResp.Body)
body, _ := io.ReadAll(httpResp.Body) //nolint:gocritic // offline code generator
httpResp.Body.Close()
// parse Retry-After header
@@ -298,7 +298,7 @@ func checkHTTPStatus(httpResp *http.Response) error {
if httpResp.StatusCode == http.StatusOK {
return nil
}
body, _ := io.ReadAll(httpResp.Body)
body, _ := io.ReadAll(httpResp.Body) //nolint:gocritic // offline code generator
httpResp.Body.Close()
return fmt.Errorf("NVD API error (status %d): %s", httpResp.StatusCode, string(body))
}
@@ -24,6 +24,7 @@ import (
"github.com/anchore/syft/internal/cmptest"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/relationship"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/fileresolver"
@@ -62,7 +63,7 @@ type CatalogTester struct {
skipTestObservations bool
}
func Context() context.Context {
func initLicenseScanner() {
once.Do(func() {
// most of the time in testing is initializing the scanner. Let's do that just once
sc := &licenses.ScannerConfig{Scanner: licensecheck.Scan, CoverageThreshold: 75}
@@ -72,13 +73,20 @@ func Context() context.Context {
}
licenseScanner = &scanner
})
}
return licenses.SetContextLicenseScanner(context.Background(), *licenseScanner)
// Context returns a context with a shared license scanner and a TempDir backed by t.TempDir(),
// so cleanup is handled automatically when the test finishes.
func Context(t *testing.T) context.Context {
t.Helper()
initLicenseScanner()
td := tmpdir.FromPath(t.TempDir())
ctx := tmpdir.WithValue(context.Background(), td)
return licenses.SetContextLicenseScanner(ctx, *licenseScanner)
}
func NewCatalogTester() *CatalogTester {
return &CatalogTester{
context: Context(),
locationComparer: cmptest.DefaultLocationComparer,
licenseComparer: cmptest.DefaultLicenseComparer,
packageStringer: stringPackage,
@@ -272,9 +280,18 @@ func (p *CatalogTester) WithoutTestObserver() *CatalogTester {
return p
}
func (p *CatalogTester) ensureContext(t *testing.T) context.Context {
t.Helper()
if p.context != nil {
return p.context
}
return Context(t)
}
func (p *CatalogTester) TestParser(t *testing.T, parser generic.Parser) {
t.Helper()
pkgs, relationships, err := parser(p.context, p.resolver, p.env, p.reader)
ctx := p.ensureContext(t)
pkgs, relationships, err := parser(ctx, p.resolver, p.env, p.reader)
// only test for errors if explicitly requested
if p.wantErr != nil {
@@ -289,10 +306,11 @@ func (p *CatalogTester) TestParser(t *testing.T, parser generic.Parser) {
func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) {
t.Helper()
ctx := p.ensureContext(t)
resolver := NewObservingResolver(p.resolver)
pkgs, relationships, err := cataloger.Catalog(p.context, resolver)
pkgs, relationships, err := cataloger.Catalog(ctx, resolver)
// this is a minimum set, the resolver may return more that just this list
for _, path := range p.expectedPathResponses {
+6 -1
View File
@@ -18,6 +18,7 @@ import (
"github.com/anchore/syft/internal"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -104,7 +105,11 @@ func newJavaArchiveParser(ctx context.Context, reader file.LocationReadCloser, d
virtualElements := strings.Split(reader.Path(), ":")
currentFilepath := virtualElements[len(virtualElements)-1]
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(currentFilepath, reader)
td := tmpdir.FromContext(ctx)
if td == nil {
return nil, func() {}, fmt.Errorf("no temp dir factory in context")
}
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(td, currentFilepath, reader)
if err != nil {
return nil, cleanupFn, fmt.Errorf("unable to process java archive: %w", err)
}
+13 -14
View File
@@ -2,7 +2,6 @@ package java
import (
"bufio"
"context"
"fmt"
"io"
"os"
@@ -30,7 +29,7 @@ import (
func TestSearchMavenForLicenses(t *testing.T) {
url := maventest.MockRepo(t, "internal/maven/testdata/maven-repo")
ctx := pkgtest.Context()
ctx := pkgtest.Context(t)
tests := []struct {
name string
@@ -72,7 +71,7 @@ func TestSearchMavenForLicenses(t *testing.T) {
require.NoError(t, err)
// setup parser
ap, cleanupFn, err := newJavaArchiveParser(context.Background(),
ap, cleanupFn, err := newJavaArchiveParser(pkgtest.Context(t),
file.LocationReadCloser{
Location: file.NewLocation(fixture.Name()),
ReadCloser: fixture,
@@ -81,17 +80,17 @@ func TestSearchMavenForLicenses(t *testing.T) {
require.NoError(t, err)
// assert licenses are discovered from upstream
_, _, _, parsedPom := ap.discoverMainPackageFromPomInfo(context.Background())
_, _, _, parsedPom := ap.discoverMainPackageFromPomInfo(pkgtest.Context(t))
require.NotNil(t, parsedPom, "expected to find pom information in the fixture")
require.NotNil(t, parsedPom.project, "expected parsedPom to have a project")
resolvedLicenses, _ := ap.maven.ResolveLicenses(context.Background(), parsedPom.project)
resolvedLicenses, _ := ap.maven.ResolveLicenses(pkgtest.Context(t), parsedPom.project)
assert.Equal(t, tc.expectedLicenses, toPkgLicenses(ctx, nil, resolvedLicenses))
})
}
}
func TestParseJar(t *testing.T) {
ctx := pkgtest.Context()
ctx := pkgtest.Context(t)
tests := []struct {
name string
fixture string
@@ -372,7 +371,7 @@ func TestParseJar(t *testing.T) {
UseNetwork: false,
UseMavenLocalRepository: false,
}
parser, cleanupFn, err := newJavaArchiveParser(context.Background(),
parser, cleanupFn, err := newJavaArchiveParser(pkgtest.Context(t),
file.LocationReadCloser{
Location: file.NewLocation(fixture.Name()),
ReadCloser: fixture,
@@ -662,7 +661,7 @@ func TestParseNestedJar(t *testing.T) {
require.NoError(t, err)
gap := newGenericArchiveParserAdapter(ArchiveCatalogerConfig{})
actual, _, err := gap.processJavaArchive(context.Background(), file.LocationReadCloser{
actual, _, err := gap.processJavaArchive(pkgtest.Context(t), file.LocationReadCloser{
Location: file.NewLocation(fixture.Name()),
ReadCloser: fixture,
}, nil)
@@ -1080,7 +1079,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
test.expectedParent.Locations = locations
r := maven.NewResolver(nil, maven.DefaultConfig())
actualPackage := newPackageFromMavenData(context.Background(), r, test.props, test.project, test.parent, file.NewLocation(virtualPath))
actualPackage := newPackageFromMavenData(pkgtest.Context(t), r, test.props, test.project, test.parent, file.NewLocation(virtualPath))
if test.expectedPackage == nil {
require.Nil(t, actualPackage)
} else {
@@ -1120,7 +1119,7 @@ func Test_artifactIDMatchesFilename(t *testing.T) {
}
func Test_parseJavaArchive_regressions(t *testing.T) {
ctx := context.TODO()
ctx := pkgtest.Context(t)
apiAll := pkg.Package{
Name: "api-all",
Version: "2.0.0",
@@ -1499,7 +1498,7 @@ func Test_deterministicMatchingPomProperties(t *testing.T) {
fixture, err := os.Open(fixturePath)
require.NoError(t, err)
parser, cleanupFn, err := newJavaArchiveParser(context.Background(),
parser, cleanupFn, err := newJavaArchiveParser(pkgtest.Context(t),
file.LocationReadCloser{
Location: file.NewLocation(fixture.Name()),
ReadCloser: fixture,
@@ -1507,7 +1506,7 @@ func Test_deterministicMatchingPomProperties(t *testing.T) {
defer cleanupFn()
require.NoError(t, err)
groupID, artifactID, version, _ := parser.discoverMainPackageFromPomInfo(context.TODO())
groupID, artifactID, version, _ := parser.discoverMainPackageFromPomInfo(pkgtest.Context(t))
require.Equal(t, test.expected, maven.NewID(groupID, artifactID, version))
}()
}
@@ -1634,9 +1633,9 @@ func Test_jarPomPropertyResolutionDoesNotPanic(t *testing.T) {
fixture, err := os.Open(jarName)
require.NoError(t, err)
ctx := context.TODO()
ctx := pkgtest.Context(t)
// setup parser
ap, cleanupFn, err := newJavaArchiveParser(context.Background(),
ap, cleanupFn, err := newJavaArchiveParser(pkgtest.Context(t),
file.LocationReadCloser{
Location: file.NewLocation(fixture.Name()),
ReadCloser: fixture,
@@ -40,7 +40,7 @@ func ParsePomXML(content io.Reader) (project *Project, err error) {
}
func getUtf8Reader(content io.Reader) (io.Reader, error) {
pomContents, err := io.ReadAll(content)
pomContents, err := io.ReadAll(content) //nolint:gocritic // charset detection requires full buffer
if err != nil {
return nil, err
}
@@ -463,7 +463,7 @@ func (r *Resolver) cacheResolveReader(key string, resolve func() (io.ReadCloser,
defer internal.CloseAndLogError(contentReader, key)
// store the contents to return a new reader with the same content
contents, err := io.ReadAll(contentReader)
contents, err := io.ReadAll(contentReader) //nolint:gocritic // caching requires full buffer
if err != nil {
return nil, err
}
@@ -1,7 +1,6 @@
package java
import (
"context"
"os"
"testing"
@@ -194,7 +193,7 @@ func Test_parseCommonsTextPomXMLProject(t *testing.T) {
func Test_parsePomXMLProject(t *testing.T) {
// TODO: ideally we would have the path to the contained pom.xml, not the jar
jarLocation := file.NewLocation("path/to/archive.jar")
ctx := context.TODO()
ctx := pkgtest.Context(t)
tests := []struct {
name string
project *pkg.JavaPomProject
@@ -265,11 +264,11 @@ func Test_parsePomXMLProject(t *testing.T) {
pom, err := maven.ParsePomXML(fixture)
require.NoError(t, err)
actual := newPomProject(context.Background(), r, fixture.Name(), pom)
actual := newPomProject(pkgtest.Context(t), r, fixture.Name(), pom)
assert.NoError(t, err)
assert.Equal(t, test.project, actual)
licenses, err := r.ResolveLicenses(context.Background(), pom)
licenses, err := r.ResolveLicenses(pkgtest.Context(t), pom)
//assert.NoError(t, err)
assert.Equal(t, test.licenses, toPkgLicenses(ctx, &jarLocation, licenses))
})
@@ -331,7 +330,7 @@ func Test_pomParent(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
r := maven.NewResolver(nil, maven.DefaultConfig())
assert.Equal(t, test.expected, pomParent(context.Background(), r, &maven.Project{Parent: test.input}))
assert.Equal(t, test.expected, pomParent(pkgtest.Context(t), r, &maven.Project{Parent: test.input}))
})
}
}
@@ -433,7 +432,7 @@ func Test_resolveLicenses(t *testing.T) {
fr, err := ds.FileResolver(source.AllLayersScope)
require.NoError(t, err)
ctx := context.TODO()
ctx := pkgtest.Context(t)
pkgs, _, err := cat.Catalog(ctx, fr)
require.NoError(t, err)
+6 -10
View File
@@ -6,23 +6,16 @@ import (
"os"
"path/filepath"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/tmpdir"
)
func saveArchiveToTmp(archiveVirtualPath string, reader io.Reader) (string, string, func(), error) {
func saveArchiveToTmp(td *tmpdir.TempDir, archiveVirtualPath string, reader io.Reader) (string, string, func(), error) {
name := filepath.Base(archiveVirtualPath)
tempDir, err := os.MkdirTemp("", "syft-archive-contents-")
tempDir, cleanupFn, err := td.NewChild("archive-contents") //nolint:gocritic // cleanup is returned to caller, not deferred here
if err != nil {
return "", "", func() {}, fmt.Errorf("unable to create tempdir for archive processing: %w", err)
}
cleanupFn := func() {
err = os.RemoveAll(tempDir)
if err != nil {
log.Errorf("unable to cleanup archive tempdir: %+v", err)
}
}
archivePath := filepath.Join(tempDir, "archive-"+name)
contentDir := filepath.Join(tempDir, "contents")
@@ -37,6 +30,9 @@ func saveArchiveToTmp(archiveVirtualPath string, reader io.Reader) (string, stri
}
defer archiveFile.Close()
// note: no size limit here — the reader comes from a file already enumerated by the
// resolver, not from an untrusted network source. The file size is bounded by the
// source image/directory being scanned.
_, err = io.Copy(archiveFile, reader)
if err != nil {
return contentDir, archivePath, cleanupFn, fmt.Errorf("unable to copy archive: %w", err)
@@ -5,6 +5,7 @@ import (
"fmt"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -58,7 +59,11 @@ func newGenericTarWrappedJavaArchiveParser(cfg ArchiveCatalogerConfig) genericTa
}
func (gtp genericTarWrappedJavaArchiveParser) parseTarWrappedJavaArchive(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(reader.Path(), reader)
td := tmpdir.FromContext(ctx)
if td == nil {
return nil, nil, fmt.Errorf("no temp dir factory in context")
}
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(td, reader.Path(), reader)
// note: even on error, we should always run cleanup functions
defer cleanupFn()
if err != nil {
@@ -1,7 +1,6 @@
package java
import (
"context"
"os"
"path"
"testing"
@@ -50,7 +49,7 @@ func Test_parseTarWrappedJavaArchive(t *testing.T) {
}
gtp := newGenericTarWrappedJavaArchiveParser(ArchiveCatalogerConfig{})
actualPkgs, _, err := gtp.parseTarWrappedJavaArchive(context.Background(), nil, nil, file.LocationReadCloser{
actualPkgs, _, err := gtp.parseTarWrappedJavaArchive(pkgtest.Context(t), nil, nil, file.LocationReadCloser{
Location: file.NewLocation(test.fixture),
ReadCloser: fixture,
})
@@ -5,6 +5,7 @@ import (
"fmt"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -30,7 +31,11 @@ func newGenericZipWrappedJavaArchiveParser(cfg ArchiveCatalogerConfig) genericZi
}
func (gzp genericZipWrappedJavaArchiveParser) parseZipWrappedJavaArchive(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(reader.Path(), reader)
td := tmpdir.FromContext(ctx)
if td == nil {
return nil, nil, fmt.Errorf("no temp dir factory in context")
}
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(td, reader.Path(), reader)
// note: even on error, we should always run cleanup functions
defer cleanupFn()
if err != nil {
@@ -1,7 +1,6 @@
package java
import (
"context"
"os"
"path"
"testing"
@@ -10,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func Test_parseZipWrappedJavaArchive(t *testing.T) {
@@ -36,7 +36,7 @@ func Test_parseZipWrappedJavaArchive(t *testing.T) {
gzp := newGenericZipWrappedJavaArchiveParser(ArchiveCatalogerConfig{})
actualPkgs, _, err := gzp.parseZipWrappedJavaArchive(context.Background(), nil, nil, file.LocationReadCloser{
actualPkgs, _, err := gzp.parseZipWrappedJavaArchive(pkgtest.Context(t), nil, nil, file.LocationReadCloser{
Location: file.NewLocation(test.fixture),
ReadCloser: fixture,
})
+2 -16
View File
@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
@@ -257,19 +256,12 @@ func getLicenseFromNpmRegistry(baseURL, packageName, version string) (string, er
}
}()
bytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("unable to parse package from npm registry: %w", err)
}
dec := json.NewDecoder(strings.NewReader(string(bytes)))
// Read "license" from the response
var license struct {
License string `json:"license"`
}
if err := dec.Decode(&license); err != nil {
if err := json.NewDecoder(resp.Body).Decode(&license); err != nil {
return "", fmt.Errorf("unable to parse license from npm registry: %w", err)
}
@@ -324,14 +316,8 @@ func parseLicensesFromLocation(l file.Location, resolver file.Resolver, pkgFile
}
defer internal.CloseAndLogError(contentReader, l.RealPath)
contents, err := io.ReadAll(contentReader)
if err != nil {
log.Debugf("error reading file contents for %s: %v", pkgFile, err)
return nil, err
}
var pkgJSON packageJSON
err = json.Unmarshal(contents, &pkgJSON)
err = json.NewDecoder(contentReader).Decode(&pkgJSON)
if err != nil {
log.Debugf("error parsing %s: %v", pkgFile, err)
return nil, err
@@ -179,7 +179,7 @@ func newPnpmLockfileParser(version float64) pnpmLockfileParser {
// parsePnpmLock is the main parser function for pnpm-lock.yaml files.
func (a genericPnpmLockAdapter) parsePnpmLock(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
data, err := io.ReadAll(reader)
data, err := io.ReadAll(reader) //nolint:gocritic // multi-pass parse requires []byte
if err != nil {
return nil, nil, fmt.Errorf("failed to load pnpm-lock.yaml file: %w", err)
}
@@ -166,7 +166,8 @@ func findDevOnlyPkgs(yarnPkgs []yarnPackage, prodDeps, devDeps map[string]string
}
func parseYarnV1LockFile(reader io.ReadCloser) ([]yarnPackage, error) {
content, err := io.ReadAll(reader)
// TODO: refactor to use bufio.Scanner for streaming line-by-line parsing instead of reading the entire file
content, err := io.ReadAll(reader) //nolint:gocritic // stateful multi-line parser; candidate for streaming refactor
if err != nil {
return nil, fmt.Errorf("failed to read yarn.lock file: %w", err)
}
@@ -265,7 +266,8 @@ func (a genericYarnLockAdapter) parseYarnLock(ctx context.Context, resolver file
return nil, nil, nil
}
data, err := io.ReadAll(reader)
// TODO: refactor to detect version from the first line via bufio.Scanner, then dispatch to a streaming parser
data, err := io.ReadAll(reader) //nolint:gocritic // two-pass parse: version detection then format-specific parsing
if err != nil {
return nil, nil, fmt.Errorf("failed to load yarn.lock file: %w", err)
}
+1 -1
View File
@@ -39,7 +39,7 @@ var noReturn = rockspec{
// parseRockspec basic parser for rockspec
func parseRockspecData(reader io.Reader) (rockspec, error) {
data, err := io.ReadAll(reader)
data, err := io.ReadAll(reader) //nolint:gocritic // custom parser requires []byte
if err != nil {
return noReturn, err
}
+1 -1
View File
@@ -46,7 +46,7 @@ func (c cataloger) Name() string {
func (c cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
// always try the DB cataloger first (based off of information recorded by actions taken by nix tooling)
pkgs, rels, err := c.dbParser.catalog(resolver)
pkgs, rels, err := c.dbParser.catalog(ctx, resolver)
if err != nil {
return nil, nil, fmt.Errorf("failed to catalog nix packages from database: %w", err)
}
+8 -7
View File
@@ -1,8 +1,9 @@
package nix
import (
"bufio"
"context"
"fmt"
"io"
"path"
"strconv"
"strings"
@@ -17,7 +18,7 @@ import (
const defaultSchema = 10
type dbProcessor func(config Config, dbLocation file.Location, resolver file.Resolver, catalogerName string) ([]pkg.Package, []artifact.Relationship, error)
type dbProcessor func(ctx context.Context, config Config, dbLocation file.Location, resolver file.Resolver, catalogerName string) ([]pkg.Package, []artifact.Relationship, error)
type dbCataloger struct {
config Config
@@ -45,7 +46,7 @@ type dbPackageEntry struct {
Files []string
}
func (c dbCataloger) catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
func (c dbCataloger) catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
dbLocs, err := resolver.FilesByGlob("**/nix/var/nix/db/db.sqlite")
if err != nil {
return nil, nil, fmt.Errorf("failed to find Nix database: %w", err)
@@ -65,7 +66,7 @@ func (c dbCataloger) catalog(resolver file.Resolver) ([]pkg.Package, []artifact.
continue
}
newPkgs, newRelationships, err := parser(c.config, dbLoc, resolver, c.catalogerName)
newPkgs, newRelationships, err := parser(ctx, c.config, dbLoc, resolver, c.catalogerName)
if err != nil {
errs = unknown.Append(errs, dbLoc.Coordinates, err)
continue
@@ -92,13 +93,13 @@ func (c dbCataloger) selectDBParser(dbLocation file.Location, resolver file.Reso
return c.schemaProcessor[defaultSchema], 0
}
contents, err := io.ReadAll(schemaContents)
if err != nil {
scanner := bufio.NewScanner(schemaContents)
if !scanner.Scan() {
log.WithFields("path", loc.RealPath).Tracef("failed to read Nix database schema file, assuming %d", defaultSchema)
return c.schemaProcessor[defaultSchema], 0
}
schema, err := strconv.Atoi(strings.TrimSpace(string(contents)))
schema, err := strconv.Atoi(strings.TrimSpace(scanner.Text()))
if err != nil {
log.WithFields("path", loc.RealPath).Tracef("failed to parse Nix database schema file, assuming %d", defaultSchema)
return c.schemaProcessor[defaultSchema], 0
+22 -11
View File
@@ -1,6 +1,7 @@
package nix
import (
"context"
"database/sql"
"fmt"
"io"
@@ -8,6 +9,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -15,26 +17,33 @@ import (
var _ dbProcessor = processV10DB
func processV10DB(config Config, dbLocation file.Location, resolver file.Resolver, catalogerName string) ([]pkg.Package, []artifact.Relationship, error) {
func processV10DB(ctx context.Context, config Config, dbLocation file.Location, resolver file.Resolver, catalogerName string) ([]pkg.Package, []artifact.Relationship, error) {
dbContents, err := resolver.FileContentsByLocation(dbLocation)
defer internal.CloseAndLogError(dbContents, dbLocation.RealPath)
if err != nil {
return nil, nil, fmt.Errorf("unable to read Nix database: %w", err)
}
tempDB, err := createTempDB(dbContents)
td := tmpdir.FromContext(ctx)
if td == nil {
return nil, nil, fmt.Errorf("no temp dir factory in context")
}
tempDB, cleanupDB, err := createTempDB(td, dbContents)
if err != nil {
return nil, nil, fmt.Errorf("failed to create temporary database: %w", err)
}
defer os.RemoveAll(tempDB.Name())
// defer order is LIFO: cleanupDB (remove file) must run after db.Close and tempDB.Close
defer cleanupDB()
// close order is LIFO: db.Close() (SQLite conn) → tempDB.Close() (file handle) → cleanupDB (remove file)
defer tempDB.Close()
db, err := sql.Open("sqlite", tempDB.Name())
if err != nil {
return nil, nil, fmt.Errorf("failed to open database: %w", err)
}
db.SetConnMaxLifetime(0)
defer db.Close()
db.SetConnMaxLifetime(0)
packageEntries, err := extractV10DBPackages(config, db, dbLocation, resolver)
if err != nil {
@@ -232,18 +241,20 @@ func finalizeV10DBResults(db *sql.DB, packageEntries map[int]*dbPackageEntry, ca
return pkgs, relationships, nil
}
func createTempDB(content io.ReadCloser) (*os.File, error) {
tempFile, err := os.CreateTemp("", "nix-db-*.sqlite")
func createTempDB(td *tmpdir.TempDir, content io.ReadCloser) (*os.File, func(), error) {
noop := func() {}
tempFile, cleanup, err := td.NewFile("nix-db-*.sqlite") //nolint:gocritic // cleanup is returned to caller, not deferred here
if err != nil {
return nil, err
return nil, noop, err
}
_, err = io.Copy(tempFile, content)
if err != nil {
tempFile.Close()
os.Remove(tempFile.Name())
return nil, err
cleanup()
return nil, noop, err
}
return tempFile, nil
return tempFile, cleanup, nil
}
+1 -1
View File
@@ -25,7 +25,7 @@ func parseOpamPackage(ctx context.Context, _ file.Resolver, _ *generic.Environme
homepageRe := regexp.MustCompile(`(?m)homepage:\s*"(?P<url>[^"]+)"`)
urlRe := regexp.MustCompile(`(?m)url\s*{(?P<url>[^}]+)}`)
data, err := io.ReadAll(reader)
data, err := io.ReadAll(reader) //nolint:gocritic // regex matching requires full buffer
if err != nil {
log.WithFields("error", err).Trace("unable to read opam package")
return nil, nil, nil
+1 -1
View File
@@ -58,7 +58,7 @@ func parsePear(ctx context.Context, _ file.Resolver, _ *generic.Environment, rea
// parsePeclPearSerialized is a parser function for Pear metadata contents, returning "Default" php packages discovered.
func parsePeclPearSerialized(reader file.LocationReadCloser) (*peclPearData, error) {
data, err := io.ReadAll(reader)
data, err := io.ReadAll(reader) //nolint:gocritic // phpserialize requires []byte
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
+1 -1
View File
@@ -473,7 +473,7 @@ func Test_PackageCataloger_IgnorePackage(t *testing.T) {
t.Run(test.MetadataFixture, func(t *testing.T) {
resolver := file.NewMockResolverForPaths(test.MetadataFixture)
actual, _, err := NewInstalledPackageCataloger().Catalog(pkgtest.Context(), resolver)
actual, _, err := NewInstalledPackageCataloger().Catalog(pkgtest.Context(t), resolver)
require.NoError(t, err)
if len(actual) != 0 {
+1 -10
View File
@@ -4,10 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"github.com/anchore/syft/internal/cache"
@@ -100,13 +98,6 @@ func getLicenseFromPypiRegistry(baseURL, packageName, version string) (string, e
return "", fmt.Errorf("unable to get package from pypi registry")
}
bytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("unable to parse package from pypi registry: %w", err)
}
dec := json.NewDecoder(strings.NewReader(string(bytes)))
// Read "license" from the response
var pypiResponse struct {
Info struct {
@@ -115,7 +106,7 @@ func getLicenseFromPypiRegistry(baseURL, packageName, version string) (string, e
} `json:"info"`
}
if err := dec.Decode(&pypiResponse); err != nil {
if err := json.NewDecoder(resp.Body).Decode(&pypiResponse); err != nil {
return "", fmt.Errorf("unable to parse license from pypi registry: %w", err)
}
+1 -1
View File
@@ -143,7 +143,7 @@ func (ulp uvLockParser) uvLockPackages(ctx context.Context, reader file.Location
var parsedLockFileVersion uvLockFileVersion
// we cannot use the reader twice, so we read the contents first --uv.lock files tend to be small enough
contents, err := io.ReadAll(reader)
contents, err := io.ReadAll(reader) //nolint:gocritic // multi-pass parse requires []byte
if err != nil {
return nil, unknown.New(reader.Location, fmt.Errorf("failed to read uv lock file: %w", err))
}
+1 -7
View File
@@ -4,7 +4,6 @@ import (
"bufio"
"context"
"encoding/json"
"io"
"path"
"path/filepath"
"strings"
@@ -174,13 +173,8 @@ func fetchDirectURLData(resolver file.Resolver, metadataLocation file.Location)
}
defer internal.CloseAndLogError(directURLContents, directURLLocation.AccessPath)
buffer, err := io.ReadAll(directURLContents)
if err != nil {
return nil, nil, err
}
var directURLJson directURLOrigin
if err := json.Unmarshal(buffer, &directURLJson); err != nil {
if err := json.NewDecoder(directURLContents).Decode(&directURLJson); err != nil {
return nil, nil, err
}
+8 -13
View File
@@ -5,11 +5,11 @@ import (
"errors"
"fmt"
"io"
"os"
"strings"
rpmdb "github.com/anchore/go-rpmdb/pkg"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -22,21 +22,16 @@ import (
//
//nolint:funlen
func parseRpmDB(ctx context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
f, err := os.CreateTemp("", "rpmdb")
td := tmpdir.FromContext(ctx)
if td == nil {
return nil, nil, fmt.Errorf("no temp dir factory in context")
}
f, cleanup, err := td.NewFile("rpmdb-*")
if err != nil {
return nil, nil, fmt.Errorf("failed to create temp rpmdb file: %w", err)
}
defer func() {
err = f.Close()
if err != nil {
log.Errorf("failed to close temp rpmdb file: %+v", err)
}
err = os.Remove(f.Name())
if err != nil {
log.Errorf("failed to remove temp rpmdb file: %+v", err)
}
}()
defer cleanup()
defer f.Close()
_, err = io.Copy(f, reader)
if err != nil {
+1 -1
View File
@@ -85,6 +85,6 @@ func adaptToReadSeeker(reader io.Reader) (io.ReadSeeker, error) {
log.Debug("SBOM cataloger reader is not a ReadSeeker, reading entire SBOM into memory")
var buff bytes.Buffer
_, err := io.Copy(&buff, reader)
_, err := io.Copy(&buff, reader) //nolint:gocritic // buffering to ReadSeeker
return bytes.NewReader(buff.Bytes()), err
}
@@ -3,7 +3,6 @@ package swift
import (
"context"
"fmt"
"io"
"strings"
"go.yaml.in/yaml/v3"
@@ -28,12 +27,8 @@ type podfileLock struct {
// parsePodfileLock is a parser function for Podfile.lock contents, returning all cocoapods pods discovered.
func parsePodfileLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
bytes, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("unable to read file: %w", err)
}
var podfile podfileLock
if err = yaml.Unmarshal(bytes, &podfile); err != nil {
if err := yaml.NewDecoder(reader).Decode(&podfile); err != nil {
return nil, nil, fmt.Errorf("unable to parse yaml: %w", err)
}
+1 -1
View File
@@ -20,7 +20,7 @@ func parsePackPackage(ctx context.Context, resolver file.Resolver, _ *generic.En
homeRe := regexp.MustCompile(`home\(\s*'([^']+)'\s*\)`)
authorRe := regexp.MustCompile(`(author|packager)\(\s*'([^']+)'\s*(?:,\s*'([^']+)'\s*)?\)`)
data, err := io.ReadAll(reader)
data, err := io.ReadAll(reader) //nolint:gocritic // regex matching requires full buffer
if err != nil {
log.WithFields("error", err).Trace("unable to parse Rockspec app")
return nil, nil, nil
@@ -20,7 +20,7 @@ type terraformLockFile struct {
func parseTerraformLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var lockFile terraformLockFile
contents, err := io.ReadAll(reader)
contents, err := io.ReadAll(reader) //nolint:gocritic // hclsimple.Decode requires []byte
if err != nil {
return nil, nil, fmt.Errorf("failed to read terraform lock file: %w", err)
}
+1 -1
View File
@@ -411,7 +411,7 @@ func (b *licenseBuilder) licenseFromContentHash(content string) License {
}
func contentFromReader(r io.Reader) (string, error) {
bytes, err := io.ReadAll(r)
bytes, err := io.ReadAll(r) //nolint:gocritic // reading license content for storage
if err != nil {
return "", err
}