Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Limit Binary Artifact file reads to first 1024 bytes #3923

Merged
merged 3 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions checks/fileparser/listing.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@
CaseSensitive bool
}

// DoWhileTrueOnFileReader takes a filepath, its reader and
// optional variadic args. It returns a boolean indicating whether
// iterating over next files should continue.
type DoWhileTrueOnFileReader func(path string, reader io.Reader, args ...interface{}) (bool, error)

// OnMatchingFileReaderDo matches all files listed by `repoClient` against `matchPathTo`
// and on every successful match, runs onFileReader fn on the file's reader.
// Continues iterating along the matched files until onFileReader returns
// either a false value or an error.
func OnMatchingFileReaderDo(repoClient clients.RepoClient, matchPathTo PathMatcher,
onFileReader DoWhileTrueOnFileReader, args ...interface{},
) error {
return onMatchingFileDo(repoClient, matchPathTo, onFileReader, args...)
}

// DoWhileTrueOnFileContent takes a filepath, its content and
// optional variadic args. It returns a boolean indicating whether
// iterating over next files should continue.
Expand All @@ -75,6 +90,12 @@
// either a false value or an error.
func OnMatchingFileContentDo(repoClient clients.RepoClient, matchPathTo PathMatcher,
onFileContent DoWhileTrueOnFileContent, args ...interface{},
) error {
return onMatchingFileDo(repoClient, matchPathTo, onFileContent, args...)
}

func onMatchingFileDo(repoClient clients.RepoClient, matchPathTo PathMatcher,
onFile any, args ...interface{},
) error {
predicate := func(filepath string) (bool, error) {
// Filter out test files.
Expand All @@ -95,17 +116,28 @@
}

for _, file := range matchedFiles {
rc, err := repoClient.GetFileReader(file)
reader, err := repoClient.GetFileReader(file)
if err != nil {
return fmt.Errorf("error during GetFileReader: %w", err)
}
content, err := io.ReadAll(rc)
rc.Close()
if err != nil {
return fmt.Errorf("reading from file: %w", err)
}

continueIter, err := onFileContent(file, content, args...)
var continueIter bool
switch f := onFile.(type) {
case DoWhileTrueOnFileReader:
continueIter, err = f(file, reader, args...)
reader.Close()
case DoWhileTrueOnFileContent:
var content []byte
content, err = io.ReadAll(reader)
reader.Close()
if err != nil {
return fmt.Errorf("reading from file: %w", err)
}

Check warning on line 135 in checks/fileparser/listing.go

View check run for this annotation

Codecov / codecov/patch

checks/fileparser/listing.go#L134-L135

Added lines #L134 - L135 were not covered by tests
continueIter, err = f(file, content, args...)
default:
msg := fmt.Sprintf("invalid type (%T) passed to onMatchingFileDo", f)
return sce.WithMessage(sce.ErrScorecardInternal, msg)

Check warning on line 139 in checks/fileparser/listing.go

View check run for this annotation

Codecov / codecov/patch

checks/fileparser/listing.go#L137-L139

Added lines #L137 - L139 were not covered by tests
}
if err != nil {
return err
}
Expand Down
25 changes: 17 additions & 8 deletions checks/raw/binary_artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import (
"errors"
"fmt"
"io"
"path/filepath"
"regexp"
"strings"
Expand All @@ -39,6 +40,9 @@
gradleWrapperValidationActionVersionConstraint = mustParseConstraint(`>= 1.0.0`)
)

// how many bytes are considered when determining if a file is text or binary.
const binaryTestLen = 1024

// mustParseConstraint attempts parse of semver constraint, panics if fail.
func mustParseConstraint(c string) *semver.Constraints {
if c, err := semver.NewConstraint(c); err != nil {
Expand All @@ -52,10 +56,10 @@
func BinaryArtifacts(req *checker.CheckRequest) (checker.BinaryArtifactData, error) {
c := req.RepoClient
files := []checker.File{}
err := fileparser.OnMatchingFileContentDo(c, fileparser.PathMatcher{
err := fileparser.OnMatchingFileReaderDo(c, fileparser.PathMatcher{
Pattern: "*",
CaseSensitive: false,
}, checkBinaryFileContent, &files)
}, checkBinaryFileReader, &files)
if err != nil {
return checker.BinaryArtifactData{}, fmt.Errorf("%w", err)
}
Expand Down Expand Up @@ -96,17 +100,17 @@
return files, nil
}

var checkBinaryFileContent fileparser.DoWhileTrueOnFileContent = func(path string, content []byte,
var checkBinaryFileReader fileparser.DoWhileTrueOnFileReader = func(path string, reader io.Reader,
args ...interface{},
) (bool, error) {
if len(args) != 1 {
return false, fmt.Errorf(
"checkBinaryFileContent requires exactly one argument: %w", errInvalidArgLength)
"checkBinaryFileReader requires exactly one argument: %w", errInvalidArgLength)

Check warning on line 108 in checks/raw/binary_artifact.go

View check run for this annotation

Codecov / codecov/patch

checks/raw/binary_artifact.go#L108

Added line #L108 was not covered by tests
}
pfiles, ok := args[0].(*[]checker.File)
if !ok {
return false, fmt.Errorf(
"checkBinaryFileContent requires argument of type *[]checker.File: %w", errInvalidArgType)
"checkBinaryFileReader requires argument of type *[]checker.File: %w", errInvalidArgType)

Check warning on line 113 in checks/raw/binary_artifact.go

View check run for this annotation

Codecov / codecov/patch

checks/raw/binary_artifact.go#L113

Added line #L113 was not covered by tests
}

binaryFileTypes := map[string]bool{
Expand Down Expand Up @@ -138,8 +142,13 @@
"wasm": true,
"whl": true,
}

content, err := io.ReadAll(io.LimitReader(reader, binaryTestLen))
if err != nil {
return false, fmt.Errorf("reading file: %w", err)
}

Check warning on line 149 in checks/raw/binary_artifact.go

View check run for this annotation

Codecov / codecov/patch

checks/raw/binary_artifact.go#L148-L149

Added lines #L148 - L149 were not covered by tests

var t types.Type
var err error
if len(content) == 0 {
return true, nil
}
Expand Down Expand Up @@ -169,12 +178,12 @@
return true, nil
}

// determines if the first 1024 bytes are text
// determines if the first binaryTestLen bytes are text
//
// A version of golang.org/x/tools/godoc/util modified to allow carriage returns
// and utf8.RuneError (0xFFFD), as the file may not be utf8 encoded.
func isText(s []byte) bool {
const max = 1024 // at least utf8.UTFMax
const max = binaryTestLen // at least utf8.UTFMax (4)
if len(s) > max {
s = s[0:max]
}
Expand Down
Loading