diff --git a/docs/user-guide/providers/image/getting-started-image.mdx b/docs/user-guide/providers/image/getting-started-image.mdx index d3d74ddba1..6b5d30b72e 100644 --- a/docs/user-guide/providers/image/getting-started-image.mdx +++ b/docs/user-guide/providers/image/getting-started-image.mdx @@ -96,6 +96,29 @@ Install Trivy using one of the following methods: For additional installation methods, see the [Trivy installation guide](https://trivy.dev/latest/getting-started/installation/). +### Vulnerability Database Cache + + + +Trivy keeps its vulnerability database in a cache directory. By default Prowler gives it a temporary one and removes it when the scan ends, so the database is downloaded again for every scan. + +Set `TRIVY_CACHE_DIR` to a directory that persists and the database is downloaded once and reused: + +```bash +export TRIVY_CACHE_DIR="$HOME/.cache/trivy" +prowler image --image +``` + +Prowler never deletes a directory you supply. Trivy still creates and updates its cache and database files inside it. + + +A host with no internet access needs a pre-populated vulnerability database in a persistent directory, with `TRIVY_CACHE_DIR` pointing at it. Populate the directory on a machine that does have access and copy it across. + +Trivy tries to refresh the database when it considers it stale, and that download fails without network access. Set `TRIVY_SKIP_DB_UPDATE=true` (and `TRIVY_SKIP_JAVA_DB_UPDATE=true` if Java scanning is enabled) so it uses the supplied database as is. + +The database ages. A scan run against an old one reports only the vulnerabilities known when it was built, and nothing in the output says so, so keep track of when it was last refreshed. + + ### Supported Scanners diff --git a/prowler/changelog.d/trivy-cache-dir-configurable.fixed.md b/prowler/changelog.d/trivy-cache-dir-configurable.fixed.md new file mode 100644 index 0000000000..fd2f0ca117 --- /dev/null +++ b/prowler/changelog.d/trivy-cache-dir-configurable.fixed.md @@ -0,0 +1 @@ +The Image provider now uses the directory named by `TRIVY_CACHE_DIR` when one is set, instead of a fresh temporary directory it deletes afterwards, so a deployment can supply a vulnerability database it already holds and one with network access stops re-downloading the database for every image it scans diff --git a/prowler/providers/image/image_provider.py b/prowler/providers/image/image_provider.py index 97a014d9f1..2709003bf1 100644 --- a/prowler/providers/image/image_provider.py +++ b/prowler/providers/image/image_provider.py @@ -115,10 +115,15 @@ class ImageProvider(Provider): self._session = None self._identity = "prowler" self._listing_only = False - self._trivy_cache_dir_obj = tempfile.TemporaryDirectory( - prefix="prowler-trivy-cache-" - ) - self._trivy_cache_dir = self._trivy_cache_dir_obj.name + # A supplied cache dir is never deleted: it may hold a DB we cannot refetch + configured_cache_dir = os.environ.get("TRIVY_CACHE_DIR", "").strip() + if configured_cache_dir: + self._trivy_cache_dir = configured_cache_dir + else: + self._trivy_cache_dir_obj = tempfile.TemporaryDirectory( + prefix="prowler-trivy-cache-" + ) + self._trivy_cache_dir = self._trivy_cache_dir_obj.name # Registry authentication (follows IaC pattern: explicit params, env vars internal) self.registry_username = registry_username or os.environ.get( diff --git a/tests/providers/image/image_provider_test.py b/tests/providers/image/image_provider_test.py index cb3bbf2899..94c6a7181b 100644 --- a/tests/providers/image/image_provider_test.py +++ b/tests/providers/image/image_provider_test.py @@ -50,6 +50,11 @@ def _make_provider(**kwargs): return ImageProvider(**defaults) +@pytest.fixture(autouse=True) +def _no_configured_cache_dir(monkeypatch): + monkeypatch.delenv("TRIVY_CACHE_DIR", raising=False) + + class TestImageProvider: def test_image_provider(self): """Test default initialization.""" @@ -999,6 +1004,34 @@ class TestCleanup: provider.cleanup() provider.cleanup() + def test_configured_cache_dir_is_used(self, monkeypatch, tmp_path): + """A deployment that supplies a cache directory gets that one.""" + monkeypatch.setenv("TRIVY_CACHE_DIR", str(tmp_path)) + + provider = _make_provider() + + assert provider._trivy_cache_dir == str(tmp_path) + + def test_configured_cache_dir_survives_cleanup(self, monkeypatch, tmp_path): + """A supplied directory is not the provider's to delete: it holds a + database the deployment may have no way to fetch again.""" + monkeypatch.setenv("TRIVY_CACHE_DIR", str(tmp_path)) + provider = _make_provider() + + provider.cleanup() + + assert os.path.isdir(str(tmp_path)) + + def test_unset_cache_dir_keeps_the_temporary_one(self, monkeypatch): + """Without one configured, nothing changes for existing deployments.""" + monkeypatch.delenv("TRIVY_CACHE_DIR", raising=False) + + provider = _make_provider() + + assert os.path.isdir(provider._trivy_cache_dir) + provider.cleanup() + assert not os.path.isdir(provider._trivy_cache_dir) + def test_cleanup_removes_trivy_cache_dir(self): """Test that cleanup removes the temporary Trivy cache directory.""" provider = _make_provider()