-
Notifications
You must be signed in to change notification settings - Fork 95
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
feat(python): add pypa support #96
Changes from 3 commits
f4e02b8
a33d1f9
8a06a66
95ea5cc
cfd2587
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -26,6 +26,7 @@ import ( | |||||
"github.com/aquasecurity/vuln-list-update/nvd" | ||||||
oracleoval "github.com/aquasecurity/vuln-list-update/oracle/oval" | ||||||
"github.com/aquasecurity/vuln-list-update/photon" | ||||||
"github.com/aquasecurity/vuln-list-update/pypa" | ||||||
redhatoval "github.com/aquasecurity/vuln-list-update/redhat/oval" | ||||||
"github.com/aquasecurity/vuln-list-update/redhat/securitydataapi" | ||||||
susecvrf "github.com/aquasecurity/vuln-list-update/suse/cvrf" | ||||||
|
@@ -156,6 +157,12 @@ func run() error { | |||||
return xerrors.Errorf("error in Photon update: %w", err) | ||||||
} | ||||||
commitMsg = "Photon Security Advisories" | ||||||
case "pypa": | ||||||
p := pypa.NewPypa() | ||||||
if err := p.Update(); err != nil { | ||||||
return xerrors.Errorf("error in Pypa update: %w", err) | ||||||
} | ||||||
commitMsg = "Pypa Security Advisories" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
case "ghsa": | ||||||
src := oauth2.StaticTokenSource( | ||||||
&oauth2.Token{AccessToken: githubToken}, | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,125 @@ | ||||||
package pypa | ||||||
|
||||||
import ( | ||||||
"context" | ||||||
"fmt" | ||||||
"io/fs" | ||||||
"io/ioutil" | ||||||
"path/filepath" | ||||||
|
||||||
"github.com/aquasecurity/vuln-list-update/types" | ||||||
"github.com/aquasecurity/vuln-list-update/utils" | ||||||
"github.com/cheggaaa/pb" | ||||||
"github.com/spf13/afero" | ||||||
"golang.org/x/xerrors" | ||||||
"gopkg.in/yaml.v2" | ||||||
) | ||||||
|
||||||
const ( | ||||||
pypaDir = "pypa" | ||||||
securityTrackerURL = "https://github.com/pypa/advisory-db/archive/refs/heads/main.zip" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AndreyLevchenko Could you check if we can use the above URL? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @knqyf263 I think I can use the url above, but I need to understand benefits better. For now it looks pretty same to me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The benefit is we can have a single implementation for some languages (Python, Go and Rust). |
||||||
retry = 3 | ||||||
yamlExt = ".yaml" | ||||||
) | ||||||
|
||||||
type options struct { | ||||||
url string | ||||||
dir string | ||||||
retry int | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
} | ||||||
type option func(*options) | ||||||
|
||||||
type Pypa struct { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
According to their website, PyPA is correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
opts *options | ||||||
AppFs afero.Fs | ||||||
} | ||||||
|
||||||
func WithURL(url string) option { | ||||||
return func(opts *options) { opts.url = url } | ||||||
} | ||||||
|
||||||
func WithDir(dir string) option { | ||||||
return func(opts *options) { opts.dir = dir } | ||||||
} | ||||||
|
||||||
func WithRetry(retry int) option { | ||||||
return func(opts *options) { opts.retry = retry } | ||||||
} | ||||||
|
||||||
func NewPypa(opts ...option) Pypa { | ||||||
o := &options{ | ||||||
url: securityTrackerURL, | ||||||
dir: filepath.Join(utils.VulnListDir(), pypaDir), | ||||||
retry: retry, | ||||||
} | ||||||
|
||||||
for _, opt := range opts { | ||||||
opt(o) | ||||||
} | ||||||
|
||||||
return Pypa{ | ||||||
opts: o, | ||||||
AppFs: afero.NewOsFs(), | ||||||
} | ||||||
|
||||||
} | ||||||
|
||||||
func (pypa *Pypa) Update() error { | ||||||
dir, err := utils.DownloadToTempDir(context.Background(), pypa.opts.url) | ||||||
|
||||||
if err != nil { | ||||||
return xerrors.Errorf("failed to download %s: %w", pypa.opts.url, err) | ||||||
} | ||||||
|
||||||
vulnDir := filepath.Join(dir, "advisory-db-main/vulns") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
|
||||||
yamlFiles, err := getYamlFiles(vulnDir) | ||||||
|
||||||
if err != nil { | ||||||
return xerrors.Errorf("failed to find vulnerability files in the directory %s: %w", vulnDir, err) | ||||||
} | ||||||
|
||||||
bar := pb.StartNew(len(yamlFiles)) | ||||||
|
||||||
for _, file := range yamlFiles { | ||||||
data, err := ioutil.ReadFile(file) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ioutil is deprecated
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
|
||||||
if err != nil { | ||||||
return xerrors.Errorf("unable to read %s: %w", file, err) | ||||||
} | ||||||
|
||||||
osv := &types.Osv{} | ||||||
|
||||||
err = yaml.Unmarshal(data, osv) | ||||||
|
||||||
if err != nil { | ||||||
return xerrors.Errorf("unable to parse yaml %s: %w", file, err) | ||||||
} | ||||||
|
||||||
if err := utils.WriteJSON(pypa.AppFs, pypa.opts.dir, fmt.Sprintf("%s.json", osv.Id), osv); err != nil { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should keep the directory structure as much as possible. It means we will have package dirs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
return xerrors.Errorf("failed to write file: %w", err) | ||||||
} | ||||||
|
||||||
bar.Increment() | ||||||
} | ||||||
bar.Finish() | ||||||
return nil | ||||||
} | ||||||
func getYamlFiles(vulnDir string) ([]string, error) { | ||||||
yamlFiles := make([]string, 0) | ||||||
|
||||||
err := filepath.WalkDir(vulnDir, | ||||||
func(path string, d fs.DirEntry, err error) error { | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
|
||||||
if !d.IsDir() && filepath.Ext(path) == yamlExt { | ||||||
yamlFiles = append(yamlFiles, path) | ||||||
} | ||||||
|
||||||
return nil | ||||||
}) | ||||||
return yamlFiles, err | ||||||
|
||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,78 @@ | ||||||||||||||
package pypa | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||||||||||
|
||||||||||||||
import ( | ||||||||||||||
"net/http" | ||||||||||||||
"net/http/httptest" | ||||||||||||||
"os" | ||||||||||||||
"path/filepath" | ||||||||||||||
"testing" | ||||||||||||||
|
||||||||||||||
"github.com/stretchr/testify/assert" | ||||||||||||||
"github.com/stretchr/testify/require" | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
func Test_Update(t *testing.T) { | ||||||||||||||
tests := []struct { | ||||||||||||||
name string | ||||||||||||||
inputArchive string | ||||||||||||||
expectedFiles []string | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||||||||||
wantErr string | ||||||||||||||
}{ | ||||||||||||||
{ | ||||||||||||||
name: "happy path", | ||||||||||||||
inputArchive: "testdata/pypa.zip", | ||||||||||||||
expectedFiles: []string{"PYSEC-2005-1.json", "PYSEC-2006-1.json", "PYSEC-2006-2.json"}, | ||||||||||||||
}, | ||||||||||||||
{ | ||||||||||||||
name: "sad path, unable to download archive", | ||||||||||||||
wantErr: "connection refused", | ||||||||||||||
}, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
for _, tt := range tests { | ||||||||||||||
t.Run(tt.name, func(t *testing.T) { | ||||||||||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||||||||||
b, err := os.ReadFile(tt.inputArchive) | ||||||||||||||
require.NoError(t, err) | ||||||||||||||
|
||||||||||||||
w.Write(b) | ||||||||||||||
})) | ||||||||||||||
|
||||||||||||||
defer ts.Close() | ||||||||||||||
|
||||||||||||||
// Intentionally close to induce network errors | ||||||||||||||
if tt.inputArchive == "" { | ||||||||||||||
ts.Close() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
dir := t.TempDir() | ||||||||||||||
c := NewPypa(WithURL(ts.URL+"/"+tt.inputArchive), WithDir(filepath.Join(dir)), WithRetry(0)) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have to pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. based on our chat we decided not to do it, because of |
||||||||||||||
err := c.Update() | ||||||||||||||
if tt.wantErr != "" { | ||||||||||||||
require.Error(t, err) | ||||||||||||||
assert.Contains(t, err.Error(), tt.wantErr) | ||||||||||||||
return | ||||||||||||||
} else { | ||||||||||||||
require.NoError(t, err) | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||||||||||
|
||||||||||||||
entries, err := os.ReadDir("testdata/golden") | ||||||||||||||
require.NoError(t, err) | ||||||||||||||
|
||||||||||||||
for _, e := range entries { | ||||||||||||||
if e.IsDir() { | ||||||||||||||
continue | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
filePath := e.Name() | ||||||||||||||
gotJSON, err := os.ReadFile(filepath.Join(dir, filePath)) | ||||||||||||||
require.NoError(t, err) | ||||||||||||||
|
||||||||||||||
wantJSON, err := os.ReadFile(filepath.Join("testdata", "golden", filepath.Base(filePath))) | ||||||||||||||
require.NoError(t, err) | ||||||||||||||
|
||||||||||||||
assert.JSONEq(t, string(wantJSON), string(gotJSON)) | ||||||||||||||
} | ||||||||||||||
}) | ||||||||||||||
} | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
{ | ||
"id": "PYSEC-2005-1", | ||
"modified": "2021-07-16T01:31:33.917972Z", | ||
"published": "2005-12-31T05:00:00Z", | ||
"aliases": [ | ||
"CVE-2005-4644" | ||
], | ||
"package": { | ||
"ecosystem": "PyPI", | ||
"name": "trac" | ||
}, | ||
"details": "Cross-site scripting (XSS) vulnerability in the HTML WikiProcessor in Edgewall Trac 0.9.2 allows remote attackers to inject arbitrary web script or HTML via javascript in the SRC attribute of an IMG tag.", | ||
"affects": { | ||
"ranges": [ | ||
{ | ||
"type": "ECOSYSTEM", | ||
"fixed": "0.10" | ||
} | ||
], | ||
"versions": [ | ||
"0.8.4", | ||
"0.9" | ||
] | ||
}, | ||
"references": [ | ||
{ | ||
"type": "WEB", | ||
"url": "http://projects.edgewall.com/trac/ticket/2473" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://www.securityfocus.com/bid/16198" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://secunia.com/advisories/18465" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://www.debian.org/security/2006/dsa-951" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://secunia.com/advisories/18555" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://trac.edgewall.org/ticket/2473" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://www.vupen.com/english/advisories/2006/0226" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/24183" | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
{ | ||
"id": "PYSEC-2006-1", | ||
"modified": "2021-07-05T00:01:17.388273Z", | ||
"published": "2006-02-22T02:02:00Z", | ||
"aliases": [ | ||
"CVE-2006-0847" | ||
], | ||
"package": { | ||
"ecosystem": "PyPI", | ||
"name": "cherrypy" | ||
}, | ||
"details": "Directory traversal vulnerability in the staticfilter component in CherryPy before 2.1.1 allows remote attackers to read arbitrary files via \"..\" sequences in unspecified vectors.", | ||
"affects": { | ||
"ranges": [ | ||
{ | ||
"type": "ECOSYSTEM", | ||
"fixed": "2.1.1" | ||
} | ||
], | ||
"versions": [ | ||
"0.10", | ||
"2.0.0-final", | ||
"2.0.0b", | ||
"2.1.0", | ||
"2.1.0-beta" | ||
] | ||
}, | ||
"references": [ | ||
{ | ||
"type": "WEB", | ||
"url": "http://sourceforge.net/project/shownotes.php?release_id=384316\u0026group_id=56099" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://groups.google.com/group/cherrypy-announce/browse_thread/thread/92b2972f774fe6df/2f63afc9433dc306#2f63afc9433dc306" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://www.securityfocus.com/bid/16760" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://www.cherrypy.org/" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://secunia.com/advisories/18944" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://www.gentoo.org/security/en/glsa/glsa-200605-16.xml" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://secunia.com/advisories/20344" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "http://www.vupen.com/english/advisories/2006/0677" | ||
}, | ||
{ | ||
"type": "WEB", | ||
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/24809" | ||
} | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed