Skip to content

Commit c95c4f8

Browse files
committed
Collect NPM #101
Signed-off-by: Navonil Das <[email protected]>
1 parent af561ae commit c95c4f8

File tree

4 files changed

+141
-1
lines changed

4 files changed

+141
-1
lines changed

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ lxml==4.3.3
55
django==2.2.4
66
djangorestframework==3.9.2
77
django-filter==2.1.0
8+
semantic-version==2.8.2
89

910
# Tests
1011
pytest==3.2.3

vulnerabilities/data_dump.py

+33
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,36 @@ def archlinux_dump(extract_data):
147147
package=package_fixed,
148148
repository='https://security.archlinux.org/package/{}'.format(package_name)
149149
)
150+
151+
152+
def npm_dump(extract_data):
153+
for data in extract_data:
154+
vulnerability = Vulnerability.objects.create(
155+
summary=data.get('summary'),
156+
)
157+
VulnerabilityReference.objects.create(
158+
vulnerability=vulnerability,
159+
reference_id=data.get('vulnerability_id'),
160+
)
161+
162+
affected_versions = data.get('affected_version',[])
163+
for version in affected_versions:
164+
package_affected = Package.objects.create(
165+
name = data.get('package_name'),
166+
version = version,
167+
)
168+
ImpactedPackage.objects.create(
169+
vulnerability=vulnerability,
170+
package=package_affected
171+
)
172+
173+
fixed_versions = data.get('fixed_version',[])
174+
for version in fixed_versions:
175+
package_fixed = Package.objects.create(
176+
name = data.get('package_name'),
177+
version = version
178+
)
179+
ResolvedPackage.objects.create(
180+
vulnerability=vulnerability,
181+
package=package_fixed
182+
)

vulnerabilities/management/commands/import.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
from django.core.management.base import BaseCommand, CommandError
2525

2626
from vulnerabilities import data_dump as dd
27-
from vulnerabilities.scraper import debian, ubuntu, archlinux
27+
from vulnerabilities.scraper import debian, ubuntu, archlinux , npm
2828

2929
IMPORTERS = {
30+
'npm': lambda: dd.npm_dump(npm.scrape_vulnerabilities()),
3031
'debian': lambda: dd.debian_dump(debian.scrape_vulnerabilities()),
3132
'ubuntu': lambda: dd.ubuntu_dump(ubuntu.scrape_cves()),
3233
'archlinux': lambda: dd.archlinux_dump(archlinux.scrape_vulnerabilities()),

vulnerabilities/scraper/npm.py

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Author: Navonil Das (@NavonilDas)
2+
import semantic_version
3+
import re
4+
import json
5+
from urllib.request import urlopen
6+
7+
NPM_URL = 'https://registry.npmjs.org{}'
8+
PAGE = '/-/npm/v1/security/advisories?page=1'
9+
10+
# Removes spaces and v Charecter in front of version
11+
def remove_spaces(x):
12+
x = re.sub(r" +"," ",x) # Replace Multiple spaces to one
13+
# Remove space after the Relational operator
14+
x = re.sub(r'< +','<',x)
15+
x = re.sub(r'> +','>',x)
16+
x = re.sub(r'<= +','<=',x)
17+
x = re.sub(r'>= +','>=',x)
18+
# Remove v at starting of version
19+
x = re.sub(r'>=[vV]','>=',x)
20+
x = re.sub(r'<=[vV]','<=',x)
21+
x = re.sub(r'>[vV]','>',x)
22+
x = re.sub(r'<[vV]','<',x)
23+
return x
24+
25+
26+
# Returns all available for a module
27+
def get_all_version(package_name):
28+
package_url = NPM_URL.format('/'+package_name)
29+
response = urlopen(package_url).read()
30+
data = json.loads(response)
31+
versions = data.get('versions',{})
32+
all_version = [obj for obj in versions]
33+
return all_version
34+
35+
# Seperate list of Affected version and fixed version from all version
36+
# using the range specified
37+
def extract_version(package_name,aff_version_range,fixed_version_range):
38+
if aff_version_range == '' or fixed_version_range == '':
39+
return ([],[])
40+
41+
aff_spec = semantic_version.NpmSpec(remove_spaces(aff_version_range))
42+
fix_spec = semantic_version.NpmSpec(remove_spaces(fixed_version_range))
43+
all_ver = get_all_version(package_name)
44+
aff_ver = []
45+
fix_ver = []
46+
for ver in all_ver:
47+
cur_version = semantic_version.Version(ver)
48+
if cur_version in aff_spec:
49+
aff_ver.append(ver)
50+
else:
51+
if cur_version in fix_spec:
52+
fix_ver.append(ver)
53+
54+
return (aff_ver,fix_ver)
55+
56+
57+
# Extract module name, summary, vulnerability id,severity
58+
def extract_data(JSON):
59+
package_vulnerabilities = []
60+
for obj in JSON.get('objects',[]):
61+
if 'module_name' not in obj:
62+
continue
63+
package_name = obj['module_name']
64+
summary = obj.get('overview','')
65+
severity = obj.get('severity','')
66+
67+
vulnerability_id = obj.get('cves',[])
68+
if len(vulnerability_id) > 0:
69+
vulnerability_id = vulnerability_id[0]
70+
else:
71+
vulnerability_id = ''
72+
73+
affected_version,fixed_version = extract_version(
74+
package_name,
75+
obj.get('vulnerable_versions',''),
76+
obj.get('patched_versions','')
77+
)
78+
79+
package_vulnerabilities.append({
80+
'package_name': package_name,
81+
'summary': summary,
82+
'vulnerability_id':vulnerability_id,
83+
'fixed_version':fixed_version,
84+
'affected_version':affected_version,
85+
'severity':severity
86+
})
87+
return package_vulnerabilities
88+
89+
90+
# Extract JSON From NPM registry
91+
def scrape_vulnerabilities():
92+
cururl = NPM_URL.format(PAGE)
93+
response = urlopen(cururl).read()
94+
package_vulnerabilities = []
95+
while True:
96+
data = json.loads(response)
97+
package_vulnerabilities = package_vulnerabilities + extract_data(data)
98+
next_page = data.get('urls',{}).get('next',False)
99+
if next_page:
100+
cururl = NPM_URL.format(next_page)
101+
response = urlopen(cururl).read()
102+
else:
103+
break
104+
return package_vulnerabilities
105+

0 commit comments

Comments
 (0)