Skip to content

Commit 2a9f725

Browse files
authored
Merge pull request #241 from sbs2001/use_last_date
Check date before downloading data
2 parents 594af84 + 76bb59a commit 2a9f725

2 files changed

Lines changed: 53 additions & 33 deletions

File tree

vulnerabilities/importers/debian.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright (c) 2017 nexB Inc. and others. All rights reserved.
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
33
# http://nexb.com and https://github.com/nexB/vulnerablecode/
44
# The VulnerableCode software is licensed under the Apache License version 2.0.
55
# Data generated with VulnerableCode require an acknowledgment.
@@ -18,16 +18,17 @@
1818
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
1919
# VulnerableCode should be considered or used as legal advice. Consult an Attorney
2020
# for any legal advice.
21-
# VulnerableCode is a free software code scanning tool from nexB Inc. and others.
21+
# VulnerableCode is a free software tool from nexB Inc. and others.
2222
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
23+
2324
import dataclasses
24-
import json
25+
from dateutil import parser as dateparser
2526
from typing import Any
2627
from typing import List
2728
from typing import Mapping
2829
from typing import Set
29-
from urllib.request import urlopen
3030

31+
import requests
3132
from packageurl import PackageURL
3233
from schema import Optional
3334
from schema import Or
@@ -85,8 +86,12 @@ class DebianDataSource(DataSource):
8586
CONFIG_CLASS = DebianConfiguration
8687

8788
def __enter__(self):
88-
self._api_response = self._fetch()
89-
validate_schema(self._api_response)
89+
if self.response_is_new():
90+
self._api_response = self._fetch()
91+
validate_schema(self._api_response)
92+
93+
else:
94+
self._api_response = {}
9095

9196
def updated_advisories(self) -> Set[Advisory]:
9297
advisories = []
@@ -97,8 +102,7 @@ def updated_advisories(self) -> Set[Advisory]:
97102
return self.batch_advisories(advisories)
98103

99104
def _fetch(self) -> Mapping[str, Any]:
100-
with urlopen(self.config.debian_tracker_url) as response:
101-
return json.load(response)
105+
return requests.get(self.config.debian_tracker_url).json()
102106

103107
def _parse(self, pkg_name: str, records: Mapping[str, Any]) -> List[Advisory]:
104108
advisories = []
@@ -150,3 +154,8 @@ def _parse(self, pkg_name: str, records: Mapping[str, Any]) -> List[Advisory]:
150154
))
151155

152156
return advisories
157+
158+
def response_is_new(self):
159+
date_str = requests.head(self.config.debian_tracker_url).headers.get('last-modified')
160+
last_modified_date = dateparser.parse(date_str)
161+
return self.config.last_run_date < last_modified_date

vulnerabilities/tests/test_debian.py

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,31 @@
2323
import json
2424
import os
2525
from unittest.mock import patch
26+
from unittest.mock import MagicMock
2627

2728
from django.test import TestCase
2829

2930
from vulnerabilities import models
3031
from vulnerabilities.import_runner import ImportRunner
32+
from vulnerabilities.importers import DebianDataSource
3133

3234
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
33-
TEST_DATA = os.path.join(BASE_DIR, 'test_data/')
35+
TEST_DATA = os.path.join(BASE_DIR, "test_data/")
3436

3537

3638
class DebianImportTest(TestCase):
37-
3839
@classmethod
3940
def setUpClass(cls) -> None:
40-
fixture_path = os.path.join(TEST_DATA, 'debian.json')
41+
fixture_path = os.path.join(TEST_DATA, "debian.json")
4142
with open(fixture_path) as f:
4243
cls.mock_response = json.load(f)
4344

4445
cls.importer = models.Importer.objects.create(
45-
name='debian_unittests',
46-
license='',
47-
last_run=None,
48-
data_source='DebianDataSource',
49-
data_source_cfg={
50-
'debian_tracker_url': 'https://security.example.com/json',
51-
},
46+
name="debian_unittests",
47+
license="",
48+
last_run="2019-08-05 13:14:17.733232+05:30",
49+
data_source="DebianDataSource",
50+
data_source_cfg={"debian_tracker_url": "https://security.example.com/json"},
5251
)
5352

5453
@classmethod
@@ -59,31 +58,43 @@ def test_import(self):
5958
runner = ImportRunner(self.importer, 5)
6059

6160
with patch(
62-
'vulnerabilities.importers.DebianDataSource._fetch',
63-
return_value=self.mock_response
61+
"vulnerabilities.importers.DebianDataSource._fetch", return_value=self.mock_response
6462
):
65-
runner.run()
63+
with patch(
64+
"vulnerabilities.importers.DebianDataSource.response_is_new", return_value=True
65+
):
66+
runner.run()
6667

6768
assert models.Vulnerability.objects.count() == 3
6869
assert models.VulnerabilityReference.objects.count() == 3
6970
assert models.PackageRelatedVulnerability.objects.filter(is_vulnerable=True).count() == 2
70-
assert models.PackageRelatedVulnerability.objects.filter(
71-
is_vulnerable=False).count() == 8
71+
assert models.PackageRelatedVulnerability.objects.filter(is_vulnerable=False).count() == 8
7272
assert models.Package.objects.count() == 6
7373

74-
self.assert_for_package('librsync', '0.9.7-10', 'jessie', cve_ids={'CVE-2014-8242'})
75-
self.assert_for_package('librsync', '0.9.7-10', 'buster', cve_ids={'CVE-2014-8242'})
76-
self.assert_for_package('mimetex', '1.50-1.1', 'stretch')
77-
self.assert_for_package('mimetex', '1.74-1', 'stretch')
78-
self.assert_for_package('mimetex', '1.50-1.1', 'buster')
79-
self.assert_for_package('mimetex', '1.76-1', 'buster')
74+
self.assert_for_package("librsync", "0.9.7-10", "jessie", cve_ids={"CVE-2014-8242"})
75+
self.assert_for_package("librsync", "0.9.7-10", "buster", cve_ids={"CVE-2014-8242"})
76+
self.assert_for_package("mimetex", "1.50-1.1", "stretch")
77+
self.assert_for_package("mimetex", "1.74-1", "stretch")
78+
self.assert_for_package("mimetex", "1.50-1.1", "buster")
79+
self.assert_for_package("mimetex", "1.76-1", "buster")
80+
81+
def test_response_is_new(self):
82+
83+
test_data_source = self.importer.make_data_source(batch_size=1)
84+
mock_resp = MagicMock()
85+
mock_resp.headers = {"last-modified": "Wed, 05 Aug 2021 09:12:19 GMT"}
86+
87+
with patch("vulnerabilities.importers.debian.requests.head", return_value=mock_resp):
88+
assert test_data_source.response_is_new() is True
89+
90+
mock_resp.headers = {"last-modified": "Wed, 05 Aug 2019 09:12:19 GMT"}
91+
92+
with patch("vulnerabilities.importers.debian.requests.head", return_value=mock_resp):
93+
assert test_data_source.response_is_new() is False
8094

8195
def assert_for_package(self, name, version, release, cve_ids=None):
8296
qs = models.Package.objects.filter(
83-
name=name,
84-
version=version,
85-
type='deb',
86-
namespace='debian',
97+
name=name, version=version, type="deb", namespace="debian",
8798
)
8899
qs = qs.filter(qualifiers__distro=release)
89100
assert qs

0 commit comments

Comments
 (0)