Added parser for Pipfile.lock to cataloger (#473)

* Added parser for Pipfile.lock to cataloger

Signed-off-by: Nikita <33390074+Zilborg@users.noreply.github.com>

* make lint-fix

Signed-off-by: Nikita <33390074+Zilborg@users.noreply.github.com>

* Update syft/pkg/cataloger/python/parse_pipfile_lock.go

Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Nikita <33390074+Zilborg@users.noreply.github.com>

* fix _version

Signed-off-by: Nikita <33390074+Zilborg@users.noreply.github.com>

* swap method for trimming "==" prefix from pipfile pkg versions

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Nikita 2021-08-19 17:32:10 +03:00 committed by GitHub
parent b0e9b92a25
commit cba5b5723b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 182 additions and 0 deletions

View File

@ -12,6 +12,7 @@ func NewPythonIndexCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/*requirements*.txt": parseRequirementsTxt,
"**/poetry.lock": parsePoetryLock,
"**/Pipfile.lock": parsePipfileLock,
"**/setup.py": parseSetup,
}

View File

@ -0,0 +1,63 @@
package python
import (
"encoding/json"
"fmt"
"io"
"strings"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common"
)
type PipfileLock struct {
Meta struct {
Hash struct {
Sha256 string `json:"sha256"`
} `json:"hash"`
PipfileSpec int `json:"pipfile-spec"`
Requires struct {
PythonVersion string `json:"python_version"`
} `json:"requires"`
Sources []struct {
Name string `json:"name"`
URL string `json:"url"`
VerifySsl bool `json:"verify_ssl"`
} `json:"sources"`
} `json:"_meta"`
Default map[string]Dependency `json:"default"`
Develop map[string]Dependency `json:"develop"`
}
type Dependency struct {
Version string `json:"version"`
}
// integrity check
var _ common.ParserFn = parsePipfileLock
// parsePipfileLock is a parser function for Pipfile.lock contents, returning "Default" python packages discovered.
func parsePipfileLock(_ string, reader io.Reader) ([]pkg.Package, error) {
packages := make([]pkg.Package, 0)
dec := json.NewDecoder(reader)
for {
var lock PipfileLock
if err := dec.Decode(&lock); err == io.EOF {
break
} else if err != nil {
return nil, fmt.Errorf("failed to parse Pipfile.lock file: %w", err)
}
for name, pkgMeta := range lock.Default {
version := strings.TrimPrefix(pkgMeta.Version, "==")
packages = append(packages, pkg.Package{
Name: name,
Version: version,
Language: pkg.Python,
Type: pkg.PythonPkg,
})
}
}
return packages, nil
}

View File

@ -0,0 +1,49 @@
package python
import (
"os"
"testing"
"github.com/anchore/syft/syft/pkg"
)
func TestParsePipFileLock(t *testing.T) {
expected := map[string]pkg.Package{
"aio-pika": {
Name: "aio-pika",
Version: "6.8.0",
Language: pkg.Python,
Type: pkg.PythonPkg,
},
"aiodns": {
Name: "aiodns",
Version: "2.0.0",
Language: pkg.Python,
Type: pkg.PythonPkg,
},
"aiohttp": {
Name: "aiohttp",
Version: "3.7.4.post0",
Language: pkg.Python,
Type: pkg.PythonPkg,
},
"aiohttp-jinja2": {
Name: "aiohttp-jinja2",
Version: "1.4.2",
Language: pkg.Python,
Type: pkg.PythonPkg,
},
}
fixture, err := os.Open("test-fixtures/pipfile-lock/Pipfile.lock")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
actual, err := parsePipfileLock(fixture.Name(), fixture)
if err != nil {
t.Fatalf("failed to parse requirements: %+v", err)
}
assertPackagesEqual(t, actual, expected)
}

View File

@ -0,0 +1,69 @@
{
"_meta": {
"hash": {
"sha256": "a6b2dfd5367688bec81240eb04e7bde7f92b35491be5934fcb4e2e6ca9d275c0"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.8"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"aio-pika": {
"hashes": [
"sha256:1d4305a5f78af3857310b4fe48348cdcf6c097e0e275ea88c2cd08570531a369",
"sha256:e69afef8695f47c5d107bbdba21bdb845d5c249acb3be53ef5c2d497b02657c0"
],
"index": "pypi",
"version": "==6.8.0"
},
"aiodns": {
"hashes": [
"sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d",
"sha256:aaa5ac584f40fe778013df0aa6544bf157799bd3f608364b451840ed2c8688de"
],
"index": "pypi",
"version": "==2.0.0"
},
"aiohttp": {
"hashes": [
"sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe",
"sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe"
],
"index": "pypi",
"version": "==3.7.4.post0"
},
"aiohttp-jinja2": {
"hashes": [
"sha256:860da7582efa866744bad5883947557d0f82e457d69903ea65d666b66f8a69ca",
"sha256:9c22a0e48e3b277fc145c67dd8c3b8f609dab36bce9eb337f70dfe716663c9a0"
],
"index": "pypi",
"version": "==1.4.2"
}
},
"develop": {
"astroid": {
"hashes": [
"sha256:6b0ed1af831570e500e2437625979eaa3b36011f66ddfc4ce930128610258ca9",
"sha256:cd80bf957c49765dce6d92c43163ff9d2abc43132ce64d4b1b47717c6d2522df"
],
"version": "==2.5.2"
},
"autopep8": {
"hashes": [
"sha256:5454e6e9a3d02aae38f866eec0d9a7de4ab9f93c10a273fb0340f3d6d09f7514",
"sha256:f01b06a6808bc31698db907761e5890eb2295e287af53f6693b39ce55454034a"
],
"index": "pypi",
"version": "==1.5.6"
}
}
}