mirror of
https://github.com/anchore/syft.git
synced 2026-11-03 07:56:36 +01:00
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:
+2
-2
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user