Skip to content

Commit b435fe8

Browse files
committed
fix comments
1 parent 2a887d5 commit b435fe8

6 files changed

Lines changed: 148 additions & 30 deletions

File tree

src/vunnel/providers/chainguard/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ class Config:
2727
osv_url: str = "https://packages.cgr.dev/chainguard/v2/osv/all.json"
2828
# Override with VUNNEL_PROVIDERS_CHAINGUARD_USE_OSV
2929
use_osv: bool = False
30-
# Override with VUNNEL_PROVIDERS_CHAINGUARD_SKIP_REDOWNLOAD
31-
skip_redownload: bool = False
3230
# Override with VUNNEL_PROVIDERS_CHAINGUARD_OSV_MAX_WORKERS
3331
osv_max_workers: int = 8
3432

@@ -58,7 +56,7 @@ def __init__(self, root: str, config: Config | None = None):
5856
namespace=self._namespace,
5957
download_timeout=self.config.request_timeout,
6058
logger=self.logger,
61-
skip_redownload=self.config.skip_redownload,
59+
skip_download=self.config.runtime.skip_download,
6260
max_workers=self.config.osv_max_workers,
6361
)
6462
self.schema = schema.OSVSchema(version="1.7.0")
@@ -69,7 +67,7 @@ def __init__(self, root: str, config: Config | None = None):
6967
namespace=self._namespace,
7068
download_timeout=self.config.request_timeout,
7169
logger=self.logger,
72-
skip_redownload=self.config.skip_redownload,
70+
skip_download=self.config.runtime.skip_download,
7371
)
7472
self.feed_url = self.config.secdb_url
7573
self.schema = schema.OSSchema()
@@ -85,6 +83,10 @@ def name(cls) -> str:
8583
def tags(cls) -> list[str]:
8684
return ["vulnerability", "os"]
8785

86+
@classmethod
87+
def supports_skip_download(cls) -> bool:
88+
return True
89+
8890
def update(self, last_updated: datetime.datetime | None) -> tuple[list[str], int]:
8991
with timer(self.name(), self.logger):
9092
with self.results_writer() as writer, self.parser:

src/vunnel/providers/wolfi/parser.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import copy
66
import logging
77
import os
8+
import re
89
from typing import TYPE_CHECKING, Any
910
from urllib.parse import urlparse
1011

@@ -13,7 +14,7 @@
1314

1415
from vunnel.tool import fixdate
1516
from vunnel.utils import http_wrapper as http
16-
from vunnel.utils import vulnerability
17+
from vunnel.utils import vulnerability, osv
1718

1819
if TYPE_CHECKING:
1920
from collections.abc import Generator
@@ -35,8 +36,8 @@ def __init__( # noqa: PLR0913
3536
download_timeout: int = 125,
3637
logger: logging.Logger | None = None,
3738
security_reference_url: str | None = None,
38-
skip_redownload: bool = False,
39-
max_workers: int = 8,
39+
skip_download: bool = False,
40+
max_workers: int = 64,
4041
):
4142
if not fixdater:
4243
fixdater = fixdate.default_finder(workspace)
@@ -49,7 +50,7 @@ def __init__( # noqa: PLR0913
4950
self.security_reference_url = (
5051
security_reference_url.strip("/") if security_reference_url else self._security_reference_url_
5152
)
52-
self.skip_redownload = skip_redownload
53+
self.skip_download = skip_download
5354
self.max_workers = max_workers
5455

5556
if not logger:
@@ -110,7 +111,7 @@ def __init__(# noqa: PLR0913
110111
download_timeout: int = 125,
111112
logger: logging.Logger | None = None,
112113
security_reference_url: str | None = None,
113-
skip_redownload: bool = False,
114+
skip_download: bool = False,
114115
max_workers: int = 8,
115116
):
116117
self._db_filename = self._extract_filename_from_url(url)
@@ -122,11 +123,15 @@ def __init__(# noqa: PLR0913
122123
download_timeout,
123124
logger,
124125
security_reference_url,
125-
skip_redownload=skip_redownload,
126+
skip_download=skip_download,
126127
max_workers=max_workers,
127128
)
128129

129130
def _download(self) -> None:
131+
if self.skip_download:
132+
self.logger.info(f"skip_download is enabled for {self.namespace} secdb feed")
133+
return
134+
130135
if not os.path.exists(self.input_dir_path):
131136
os.makedirs(self.input_dir_path, exist_ok=True)
132137

@@ -136,11 +141,6 @@ def _download(self) -> None:
136141
self.logger.info(f"downloading {self.namespace} secdb {self.url}")
137142
r = http.get(self.url, self.logger, stream=True, timeout=self.download_timeout)
138143
file_path = os.path.join(self.input_dir_path, self._db_filename)
139-
# if the file already exists and skip_redownload is True, skip writing the file again. This is to avoid
140-
# unnecessary redownloading and rewriting of the same file, which can save time on subsequent runs.
141-
if self.skip_redownload and os.path.exists(file_path):
142-
self.logger.info(f"skipping download of {self.namespace} secdb since file already exists at {file_path}")
143-
return
144144
with open(file_path, "wb") as fp:
145145
for chunk in r.iter_content():
146146
fp.write(chunk)
@@ -256,18 +256,23 @@ def _normalize(self, release: str, data: dict[str, Any]) -> dict[str, Any]: # n
256256

257257
class OSVParser(Parser):
258258
_input_dir_ = "osv"
259+
_cga_id_re = re.compile(r"^CGA(-[23456789cfghjmpqrvwx]{4}){3}$")
259260

260261
def _download(self) -> None:
261262
'''
262263
Download all OSV entry files based on the index file at self.url, which should point to the
263264
top level all.json file. For each entry in the index, we construct the URL for the individual
264265
entry file and download it to the input directory.
265266
'''
267+
self.fixdater.download()
268+
269+
if self.skip_download:
270+
self.logger.info(f"skip_download is enabled for {self.namespace} osv feed")
271+
return
272+
266273
if not os.path.exists(self.input_dir_path):
267274
os.makedirs(self.input_dir_path, exist_ok=True)
268275

269-
self.fixdater.download()
270-
271276
try:
272277
self.logger.info(f"downloading {self.namespace} osv index {self.url}")
273278
# self.url should point to the top level all.json file, e.g.
@@ -281,10 +286,15 @@ def _download(self) -> None:
281286
# We construct the URL for each entry by appending the entry ID and .json to the base URL
282287
# e.g. https://packages.cgr.dev/chainguard/v2/osv/CGA-2255-2h2p-73q2.json
283288
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
284-
futures = [
285-
executor.submit(self._download_single_file, f"{base_url}/{entry['id']}.json", f"{entry['id']}.json")
286-
for entry in index
287-
]
289+
futures = []
290+
for entry in index:
291+
entry_id = entry["id"]
292+
if not entry_id or not self._cga_id_re.match(entry_id):
293+
self.logger.warning(f"skipping osv entry with invalid id: {entry_id!r}")
294+
continue
295+
futures.append(
296+
executor.submit(self._download_single_file, f"{base_url}/{entry_id}.json", f"{entry_id}.json"),
297+
)
288298
# surface the first exception (if any) — matches prior behavior where a single
289299
# failure aborted the batch via the outer try/except
290300
done, _not_done = concurrent.futures.wait(futures, return_when=concurrent.futures.FIRST_EXCEPTION)
@@ -298,12 +308,6 @@ def _download_single_file(self, url: str, filename: str) -> None:
298308
Download a single OSV entry file given its URL and the desired filename.
299309
'''
300310
file_path = os.path.join(self.input_dir_path, filename)
301-
# if the file already exists and skip_redownload is True, skip writing the file again. This is to avoid
302-
# unnecessary redownloading and rewriting of the same file, which can save time on subsequent
303-
# runs.
304-
if self.skip_redownload and os.path.exists(file_path):
305-
self.logger.info(f"skipping download of {self.namespace} osv entry {filename} since file already exists")
306-
return
307311
self.logger.info(f"downloading {self.namespace} osv entry {filename}")
308312
r = http.get(url, self.logger, stream=True, timeout=self.download_timeout)
309313
with open(file_path, "wb") as fp:
@@ -339,4 +343,5 @@ def _normalize(self, release: str, data: dict[str, Any]) -> dict[str, Any]: # n
339343
# we map the osv id to the osv data to keep consistency in the secdb parser, which
340344
# does this for ease of identifying the associated vulnerability when writing records.
341345
# IE: {"CGA-1234-5678-9abc": {<full osv record>}}
346+
osv.patch_fix_date(data, self.fixdater)
342347
return {data['id']: data}

tests/unit/providers/chainguard/test-fixtures/snapshots/osv/chainguard:rolling/CGA-224q-ccj5-2p53.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@
2424
},
2525
"ranges": [
2626
{
27+
"database_specific": {
28+
"anchore": {
29+
"fixes": [
30+
{
31+
"date": "2026-01-07",
32+
"kind": "advisory",
33+
"version": "2.2.34-r0"
34+
}
35+
]
36+
}
37+
},
2738
"events": [
2839
{
2940
"introduced": "0"
@@ -44,6 +55,17 @@
4455
},
4556
"ranges": [
4657
{
58+
"database_specific": {
59+
"anchore": {
60+
"fixes": [
61+
{
62+
"date": "2026-01-07",
63+
"kind": "advisory",
64+
"version": "2.8.18-r0"
65+
}
66+
]
67+
}
68+
},
4769
"events": [
4870
{
4971
"introduced": "0"
@@ -64,6 +86,17 @@
6486
},
6587
"ranges": [
6688
{
89+
"database_specific": {
90+
"anchore": {
91+
"fixes": [
92+
{
93+
"date": "2026-01-07",
94+
"kind": "advisory",
95+
"version": "3.0.10-r0"
96+
}
97+
]
98+
}
99+
},
67100
"events": [
68101
{
69102
"introduced": "0"
@@ -84,6 +117,17 @@
84117
},
85118
"ranges": [
86119
{
120+
"database_specific": {
121+
"anchore": {
122+
"fixes": [
123+
{
124+
"date": "2026-01-07",
125+
"kind": "advisory",
126+
"version": "3.1.7-r0"
127+
}
128+
]
129+
}
130+
},
87131
"events": [
88132
{
89133
"introduced": "0"

tests/unit/providers/chainguard/test-fixtures/snapshots/osv/chainguard:rolling/CGA-22hv-wp9q-4779.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@
2424
},
2525
"ranges": [
2626
{
27+
"database_specific": {
28+
"anchore": {
29+
"fixes": [
30+
{
31+
"date": "2026-02-24",
32+
"kind": "advisory",
33+
"version": "3.153.0-r0"
34+
}
35+
]
36+
}
37+
},
2738
"events": [
2839
{
2940
"introduced": "0"
@@ -57,6 +68,17 @@
5768
},
5869
"ranges": [
5970
{
71+
"database_specific": {
72+
"anchore": {
73+
"fixes": [
74+
{
75+
"date": "2026-02-24",
76+
"kind": "advisory",
77+
"version": "3.152.0-r0"
78+
}
79+
]
80+
}
81+
},
6082
"events": [
6183
{
6284
"introduced": "0"
@@ -77,6 +99,17 @@
7799
},
78100
"ranges": [
79101
{
102+
"database_specific": {
103+
"anchore": {
104+
"fixes": [
105+
{
106+
"date": "2026-02-24",
107+
"kind": "advisory",
108+
"version": "3.153.0-r0"
109+
}
110+
]
111+
}
112+
},
80113
"events": [
81114
{
82115
"introduced": "0"

tests/unit/providers/chainguard/test-fixtures/snapshots/osv/chainguard:rolling/CGA-xcpc-gm23-prj9.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@
2424
},
2525
"ranges": [
2626
{
27+
"database_specific": {
28+
"anchore": {
29+
"fixes": [
30+
{
31+
"date": "2026-02-20",
32+
"kind": "advisory",
33+
"version": "2.0.14-r1"
34+
}
35+
]
36+
}
37+
},
2738
"events": [
2839
{
2940
"introduced": "0"
@@ -44,6 +55,17 @@
4455
},
4556
"ranges": [
4657
{
58+
"database_specific": {
59+
"anchore": {
60+
"fixes": [
61+
{
62+
"date": "2026-02-20",
63+
"kind": "advisory",
64+
"version": "2.0.14-r1"
65+
}
66+
]
67+
}
68+
},
4769
"events": [
4870
{
4971
"introduced": "0"
@@ -99,6 +121,17 @@
99121
},
100122
"ranges": [
101123
{
124+
"database_specific": {
125+
"anchore": {
126+
"fixes": [
127+
{
128+
"date": "2026-02-20",
129+
"kind": "advisory",
130+
"version": "2.0.14-r1"
131+
}
132+
]
133+
}
134+
},
102135
"events": [
103136
{
104137
"introduced": "0"

tests/unit/providers/chainguard/test_chainguard.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,20 @@ def test_parser_selection(
4646
def test_config_propagates_to_parser(helpers, auto_fake_fixdate_finder, use_osv, expected_parser_cls):
4747
workspace = helpers.provider_workspace_helper(name=Provider.name())
4848

49-
c = Config(use_osv=use_osv, skip_redownload=True, osv_max_workers=16)
49+
c = Config(use_osv=use_osv, osv_max_workers=16)
50+
c.runtime.skip_download = True
5051
c.runtime.result_store = result.StoreStrategy.FLAT_FILE
5152
p = Provider(root=workspace.root, config=c)
5253

5354
assert isinstance(p.parser, expected_parser_cls)
54-
assert p.parser.skip_redownload is True
55+
assert p.parser.skip_download is True
5556
if use_osv:
5657
assert p.parser.max_workers == 16
5758

5859

5960
def test_config_defaults():
6061
c = Config()
61-
assert c.skip_redownload is False
62+
assert c.runtime.skip_download is False
6263
assert c.osv_max_workers == 8
6364

6465

0 commit comments

Comments
 (0)