Skip to content

Commit

Permalink
fix(python): add package name and version validation for `requirement…
Browse files Browse the repository at this point in the history
…s.txt` files. (#6804)
  • Loading branch information
DmitriyLewen authored May 29, 2024
1 parent a447f6b commit ea3a124
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 30 deletions.
36 changes: 32 additions & 4 deletions pkg/dependency/parser/python/pip/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"golang.org/x/text/transform"
"golang.org/x/xerrors"

version "github.com/aquasecurity/go-pep440-version"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
)

Expand All @@ -22,10 +24,14 @@ const (
endExtras string = "]"
)

type Parser struct{}
type Parser struct {
logger *log.Logger
}

func NewParser() *Parser {
return &Parser{}
return &Parser{
logger: log.WithPrefix("pip"),
}
}

func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
Expand All @@ -40,8 +46,8 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
var lineNumber int
for scanner.Scan() {
lineNumber++
line := scanner.Text()
line = strings.ReplaceAll(line, " ", "")
text := scanner.Text()
line := strings.ReplaceAll(text, " ", "")
line = strings.ReplaceAll(line, `\`, "")
line = removeExtras(line)
line = rStripByKey(line, commentMarker)
Expand All @@ -51,6 +57,12 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
if len(s) != 2 {
continue
}

if !isValidName(s[0]) || !isValidVersion(s[1]) {
p.logger.Debug("Invalid package name/version in requirements.txt.", log.String("line", text))
continue
}

pkgs = append(pkgs, ftypes.Package{
Name: s[0],
Version: s[1],
Expand Down Expand Up @@ -83,3 +95,19 @@ func removeExtras(line string) string {
}
return line
}

func isValidName(name string) bool {
for _, r := range name {
// only characters [A-Z0-9._-] are allowed (case insensitive)
// cf. https://peps.python.org/pep-0508/#names
if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '.' && r != '_' && r != '-' {
return false
}
}
return true
}

func isValidVersion(ver string) bool {
_, err := version.Parse(ver)
return err == nil
}
66 changes: 40 additions & 26 deletions pkg/dependency/parser/python/pip/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package pip

import (
"os"
"path"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -12,57 +11,72 @@ import (
)

func TestParse(t *testing.T) {
vectors := []struct {
file string
want []ftypes.Package
tests := []struct {
name string
filePath string
want []ftypes.Package
}{
{
file: "testdata/requirements_flask.txt",
want: requirementsFlask,
name: "happy path",
filePath: "testdata/requirements_flask.txt",
want: requirementsFlask,
},
{
file: "testdata/requirements_comments.txt",
want: requirementsComments,
name: "happy path with comments",
filePath: "testdata/requirements_comments.txt",
want: requirementsComments,
},
{
file: "testdata/requirements_spaces.txt",
want: requirementsSpaces,
name: "happy path with spaces",
filePath: "testdata/requirements_spaces.txt",
want: requirementsSpaces,
},
{
file: "testdata/requirements_no_version.txt",
want: requirementsNoVersion,
name: "happy path with dependency without version",
filePath: "testdata/requirements_no_version.txt",
want: requirementsNoVersion,
},
{
file: "testdata/requirements_operator.txt",
want: requirementsOperator,
name: "happy path with operator",
filePath: "testdata/requirements_operator.txt",
want: requirementsOperator,
},
{
file: "testdata/requirements_hash.txt",
want: requirementsHash,
name: "happy path with hash",
filePath: "testdata/requirements_hash.txt",
want: requirementsHash,
},
{
file: "testdata/requirements_hyphens.txt",
want: requirementsHyphens,
name: "happy path with hyphens",
filePath: "testdata/requirements_hyphens.txt",
want: requirementsHyphens,
},
{
file: "testdata/requirement_exstras.txt",
want: requirementsExtras,
name: "happy path with exstras",
filePath: "testdata/requirement_exstras.txt",
want: requirementsExtras,
},
{
file: "testdata/requirements_utf16le.txt",
want: requirementsUtf16le,
name: "happy path. File uses utf16le",
filePath: "testdata/requirements_utf16le.txt",
want: requirementsUtf16le,
},
{
name: "happy path with templating engine",
filePath: "testdata/requirements_with_templating_engine.txt",
want: nil,
},
}

for _, v := range vectors {
t.Run(path.Base(v.file), func(t *testing.T) {
f, err := os.Open(v.file)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := os.Open(tt.filePath)
require.NoError(t, err)

got, _, err := NewParser().Parse(f)
require.NoError(t, err)

assert.Equal(t, v.want, got)
assert.Equal(t, tt.want, got)
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
%ifcookiecutter.command_line_interface|lower=='click'-%}
foo|bar=='1.2.4'
foo{bar}==%%
foo,bar&&foobar==1.2.3
foo=='invalid-version'

0 comments on commit ea3a124

Please sign in to comment.