Skip to content

Commit

Permalink
feat(python): rewrite requirements parser
Browse files Browse the repository at this point in the history
  • Loading branch information
iseki-working committed Nov 14, 2023
1 parent 7332eb6 commit 1fdbb8a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 12 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/antchfx/xmlquery v1.3.17
github.com/bahlo/generic-list-go v0.2.0
github.com/denisbrodbeck/machineid v1.0.1
github.com/dlclark/regexp2 v1.10.0
github.com/go-git/go-git/v5 v5.8.1
github.com/google/uuid v1.3.1
github.com/iseki0/go-yarnlock v0.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
Expand Down
65 changes: 53 additions & 12 deletions module/python/requirements_parser.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,66 @@
package python

import (
"github.com/dlclark/regexp2"
"github.com/murphysecurity/murphysec/utils/must"
"github.com/repeale/fp-go"
"regexp"
"strings"
)

const pyName = `(?:[A-Za-z0-9][A-Za-z0-9._-]*[A-Za-z0-9]|[A-Za-z0-9])`
const pyVersion = `(?:[A-Za-z0-9_.!-]+)`
const pyVersionOp = `(?<![=!<>])(?:=|<=|==|>=|===)`
const pyVersionSeg = pyVersionOp + `\s*['""]?(` + pyVersion + `)`

var pyVersionSegPattern = regexp2.MustCompile(pyVersionSeg, regexp2.Compiled)
var pyNamePrefixPattern = regexp.MustCompile("^" + pyName)

func parseRequirements(data string) map[string]string {
var pattern = regexp.MustCompile(`^([\w_.-]+)[>=<]+([\w.]+)$`)
var deps = make(map[string]string)
for _, s := range strings.Split(data, "\n") {
s = strings.TrimSpace(s)
m := pattern.FindStringSubmatch(s)
if m == nil {
continue
var lines []string
var lineContinuation = false
for _, s := range fp.Map(func(s string) string { return strings.TrimRight(s, "\r") })(strings.Split(data, "\n")) {
s = strings.TrimRight(s, "\r")
var currentLine = strings.TrimSuffix(s, "\\")
if lineContinuation {
lines[len(lines)-1] = lines[len(lines)-1] + currentLine
} else {
lines = append(lines, currentLine)
}
lineContinuation = strings.HasSuffix(s, "\\")
}
lines = fp.Map(func(t string) string {
var i = strings.IndexRune(t, '#')
if i > -1 {
return t[:i]
}
k := strings.TrimSpace(m[1])
if k == "" {
return t
})(lines)
lines = fp.Map(func(t string) string { return strings.TrimSpace(t) })(lines)
lines = fp.Filter(func(t string) bool { return t != "" })(lines)

var m = make(map[string]string)
for _, line := range lines {
i := strings.IndexRune(line, ';')
if i > -1 {
line = line[:i]
}
line = strings.TrimSpace(line)
name := pyNamePrefixPattern.FindString(line)
if name == "" {
continue
}
v := strings.TrimSpace(m[2])
deps[k] = v
var version string
var versions []string
match := must.A(pyVersionSegPattern.FindStringMatch(line))
for match != nil {
versions = append(versions, match.GroupByNumber(1).String())
match = must.A(pyVersionSegPattern.FindNextMatch(match))
}
if len(versions) == 1 {
version = versions[0]
}
m[name] = version
}
return deps
return m
}

0 comments on commit 1fdbb8a

Please sign in to comment.