Skip to content

Commit be94a6a

Browse files
committed
feat: add lwn scraper
1 parent 193e953 commit be94a6a

3 files changed

Lines changed: 143 additions & 3 deletions

File tree

vulnerabilities/data_dump.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from vulnerabilities.models import ResolvedPackage
2828
from vulnerabilities.models import Vulnerability
2929
from vulnerabilities.models import VulnerabilityReference
30-
30+
import json
3131

3232
def debian_dump(extract_data, base_release='jessie'):
3333
"""
@@ -262,3 +262,30 @@ def rust_dump(extract_data):
262262
vulnerability=vulnerability,
263263
package=unaffected_package
264264
)
265+
266+
def lwn_dump(extract_data):
267+
for package_name in extract_data:
268+
for vuln in extract_data[package_name]:
269+
ap, _ = Package.objects.get_or_create(
270+
name=package_name,
271+
namespace=vuln['distributor'],
272+
)
273+
274+
vulnerability = Vulnerability.objects.create(
275+
summary=vuln['summary'],
276+
)
277+
278+
VulnerabilityReference.objects.create(
279+
vulnerability=vulnerability,
280+
url=vuln['advisory_link'],
281+
reference_id=vuln['advisory_id']
282+
)
283+
for cve in vuln['cve_ids']:
284+
vulnerability, _ = Vulnerability.objects.get_or_create(
285+
cve_id=cve,
286+
)
287+
VulnerabilityReference.objects.create(
288+
vulnerability=vulnerability,
289+
url=vuln['advisory_link'],
290+
reference_id=vuln['advisory_id']
291+
)

vulnerabilities/management/commands/import.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@
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, npm, ruby, rust
27+
from vulnerabilities.scraper import debian, ubuntu, archlinux, npm, ruby, rust, lwn
2828

2929
IMPORTERS = {
3030
'rust': lambda: dd.rust_dump(rust.import_vulnerabilities()),
3131
'ruby': lambda: dd.ruby_dump(ruby.import_vulnerabilities()),
3232
'npm': lambda: dd.npm_dump(npm.scrape_vulnerabilities()),
3333
'debian': lambda: dd.debian_dump(debian.scrape_vulnerabilities()),
3434
'ubuntu': lambda: dd.ubuntu_dump(ubuntu.scrape_cves()),
35-
'archlinux': lambda: dd.archlinux_dump(archlinux.scrape_vulnerabilities())
35+
'archlinux': lambda: dd.archlinux_dump(archlinux.scrape_vulnerabilities()),
36+
'lwn': lambda: dd.lwn_dump(lwn.scrape_vulnerabilities())
3637
}
3738

3839

vulnerabilities/scraper/lwn.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Author: Navonil Das (@NavonilDas)
2+
# Copyright (c) 2017 nexB Inc. and others. All rights reserved.
3+
# http://nexb.com and https://github.com/nexB/vulnerablecode/
4+
# The VulnerableCode software is licensed under the Apache License version 2.0.
5+
# Data generated with VulnerableCode require an acknowledgment.
6+
#
7+
# You may not use this software except in compliance with the License.
8+
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
9+
# Unless required by applicable law or agreed to in writing, software distributed
10+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
12+
# specific language governing permissions and limitations under the License.
13+
#
14+
# When you publish or redistribute any data created with VulnerableCode or any VulnerableCode
15+
# derivative work, you must accompany this data with the following acknowledgment:
16+
#
17+
# Generated with VulnerableCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES
18+
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
19+
# VulnerableCode should be considered or used as legal advice. Consult an Attorney
20+
# for any legal advice.
21+
# VulnerableCode is a free software code scanning tool from nexB Inc. and others.
22+
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
23+
24+
from bs4 import BeautifulSoup as bs
25+
import requests as rq
26+
import re
27+
28+
29+
base_url = "https://lwn.net/"
30+
31+
32+
def extractPackageData(advisoryLink,dist,advisoryId):
33+
34+
content = rq.get(advisoryLink).content
35+
soup = bs(content,"html.parser")
36+
text = soup.find('div',{'class':'ArticleText'}).get_text()
37+
phrases = text.split('\n')
38+
cves = []
39+
references = []
40+
summary = ""
41+
for i in range(len(phrases)):
42+
words = phrases[i].split()
43+
if phrases[i].startswith('Subject:'):
44+
summary = phrases[i+1].strip()
45+
for word in words:
46+
if word.startswith('CVE-') and word != 'CVE-ID':
47+
cves.append(word)
48+
elif word.startswith('https://') or word.startswith('http://'):
49+
references.append(word)
50+
51+
cves = list(set(cves))
52+
53+
dist = re.sub(r'\W+', '', dist).replace('_','').lower()
54+
55+
return {'cve_ids':cves,'references':references,'summary':summary,'advisory_id':advisoryId,'distributor':dist,'advisory_link':advisoryLink}
56+
57+
def getDistributors():
58+
url = base_url+"Alerts/"
59+
content = rq.get(url).content
60+
soup = bs(content,"html.parser")
61+
dists = []
62+
distsLinks = []
63+
tables = soup.find_all('table',{'cellspacing':"4",})
64+
65+
for table in tables:
66+
distsLinks += table.find_all('a')
67+
68+
for a in distsLinks:
69+
dists.append(a['href'])
70+
71+
return dists
72+
73+
def scrape_vulnerabilities():
74+
dists = getDistributors()
75+
packagesVulns = {}
76+
dists = dists[:5]
77+
for dist in dists:
78+
distUrl = base_url+"Alerts/"+dist+"?n=10"
79+
distContent = rq.get(distUrl).content
80+
distSoup = bs(distContent,"html.parser")
81+
articleSoup = distSoup.find('div',{'class':'ArticleText'})
82+
text = articleSoup.get_text()
83+
total = int(text[text.find("(")+1:text.find(")")].split()[0])
84+
curr_offset = 0
85+
while curr_offset < total:
86+
87+
table = articleSoup.find('table',{'cellpadding':4})
88+
89+
data = table.find_all('tr')
90+
data = data[1:]
91+
for row in data:
92+
rowElements = row.find_all('td')
93+
aTag = rowElements[0].find('a')
94+
advisoryLink = base_url[:-1]+aTag['href']
95+
advisoryId = aTag.get_text()
96+
package_names = rowElements[1].get_text().split(',')
97+
date = rowElements[2].get_text()
98+
for package_name in package_names:
99+
extracted_data = extractPackageData(advisoryLink,dist,advisoryId)
100+
if packagesVulns.get(package_name):
101+
packagesVulns[package_name].append(extracted_data)
102+
else:
103+
packagesVulns[package_name] = [extracted_data]
104+
105+
106+
curr_offset += 100
107+
distUrl = distUrl+"&offset="+str(curr_offset)
108+
distSoup = bs(distContent,"html.parser")
109+
articleSoup = distSoup.find('div',{'class':'ArticleText'})
110+
break
111+
112+
return packagesVulns

0 commit comments

Comments
 (0)