mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 07:40:48 -08:00
142 lines
3.2 KiB
Go
142 lines
3.2 KiB
Go
package utils
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/bmatcuk/doublestar/v4"
|
|
"github.com/samber/lo"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/log"
|
|
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
|
)
|
|
|
|
var PathSeparator = fmt.Sprintf("%c", os.PathSeparator)
|
|
|
|
func CacheDir() string {
|
|
cacheDir, err := os.UserCacheDir()
|
|
if err != nil {
|
|
cacheDir = os.TempDir()
|
|
}
|
|
return cacheDir
|
|
}
|
|
|
|
func IsCommandAvailable(name string) bool {
|
|
if _, err := exec.LookPath(name); err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func IsGzip(f *bufio.Reader) bool {
|
|
buf, err := f.Peek(3)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x8
|
|
}
|
|
|
|
func IsExecutable(fileInfo os.FileInfo) bool {
|
|
// For Windows
|
|
if filepath.Ext(fileInfo.Name()) == ".exe" {
|
|
return true
|
|
}
|
|
|
|
mode := fileInfo.Mode()
|
|
if !mode.IsRegular() {
|
|
return false
|
|
}
|
|
|
|
// Check unpackaged file
|
|
if mode.Perm()&0o111 != 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func IsBinary(content xio.ReadSeekerAt, fileSize int64) (bool, error) {
|
|
headSize := int(math.Min(float64(fileSize), 300))
|
|
head := make([]byte, headSize)
|
|
if _, err := content.Read(head); err != nil {
|
|
return false, err
|
|
}
|
|
if _, err := content.Seek(0, io.SeekStart); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
// cf. https://github.com/file/file/blob/f2a6e7cb7db9b5fd86100403df6b2f830c7f22ba/src/encoding.c#L151-L228
|
|
for _, b := range head {
|
|
if b < 7 || b == 11 || (13 < b && b < 27) || (27 < b && b < 0x20) || b == 0x7f {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func CleanSkipPaths(skipPaths []string) []string {
|
|
return lo.Map(skipPaths, func(skipPath string, _ int) string {
|
|
skipPath = filepath.ToSlash(filepath.Clean(skipPath))
|
|
return strings.TrimLeft(skipPath, "/")
|
|
})
|
|
}
|
|
|
|
func SkipPath(path string, skipPaths []string) bool {
|
|
path = strings.TrimLeft(path, "/")
|
|
|
|
// skip files
|
|
for _, pattern := range skipPaths {
|
|
match, err := doublestar.Match(pattern, path)
|
|
if err != nil {
|
|
return false // return early if bad pattern
|
|
} else if match {
|
|
log.Debug("Skipping path", log.String("path", path))
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func ExtractPrintableBytes(content xio.ReadSeekerAt) ([]byte, error) {
|
|
const minLength = 4 // Minimum length of strings to extract
|
|
var result []byte
|
|
currentPrintableLine := new(bytes.Buffer)
|
|
|
|
current := make([]byte, 1) // buffer for 1 byte reading
|
|
|
|
for {
|
|
if n, err := content.Read(current); err == io.EOF {
|
|
break
|
|
} else if n != 1 {
|
|
continue
|
|
} else if err != nil {
|
|
return nil, xerrors.Errorf("failed to read a byte: %w", err)
|
|
}
|
|
if unicode.IsPrint(rune(current[0])) {
|
|
_ = currentPrintableLine.WriteByte(current[0])
|
|
continue
|
|
}
|
|
if currentPrintableLine.Len() > minLength {
|
|
// add a newline between printable lines to separate them
|
|
_ = currentPrintableLine.WriteByte('\n')
|
|
result = append(result, currentPrintableLine.Bytes()...)
|
|
}
|
|
currentPrintableLine.Reset()
|
|
}
|
|
if currentPrintableLine.Len() > minLength {
|
|
// add a newline between printable lines to separate them
|
|
_ = currentPrintableLine.WriteByte('\n')
|
|
result = append(result, currentPrintableLine.Bytes()...)
|
|
}
|
|
return result, nil
|
|
}
|