Docker container scans report false positive CVE‑2024‑XXXX on Alpine 3.20 despite patching

descricao: “Learn how to configure Trivy and Snyk ignore rules to silence false positive CVE‑2024‑XXXX alerts on Alpine 3.20 Docker images.”

I was in the middle of a production rollout for a Go microservice that ships in an Alpine 3.20 base image. The CI pipeline runs Trivy and Snyk on every build, and both tools started shouting about CVE‑2024‑XXXX in musl even though I had already upgraded to the patched musl-1.2.4-r5 version. The build kept failing, the release was blocked, and I was losing precious time chasing a vulnerability that simply didn’t exist in the binary I was shipping.

In this post I’ll walk through the exact steps I took to prove the false positive, how I added reliable ignore rules for Trivy and Snyk, and what edge‑cases you should watch out for when you rely on Alpine 3.20 base images. The goal is to give you a repeatable, production‑grade workflow that stops false alarms without silently ignoring real threats.

Why does Trivy flag CVE‑2024‑XXXX on Alpine 3.20 after I’ve applied the upstream patch?

When I first saw the alert, my instinct was to double‑check the image layers:

docker pull myrepo/service:latest
docker history myrepo/service:latest

The output showed the musl layer at 1.2.4-r5, which is the version that contains the upstream fix for CVE‑2024‑XXXX. Yet Trivy’s JSON report still listed the CVE with a HIGH severity. The reason turned out to be twofold:

  1. Trivy’s vulnerability database lag – Trivy pulls vulnerability data from the public Alpine security tracker. The tracker updated the CVE status a few days after the Alpine maintainers released the patched package. If your Trivy cache is older than that, it will still think the vulnerable version is in use.
  2. Package metadata mismatch – Alpine’s apk database stores the installed package version, but Trivy also inspects the image manifest for a Pkg:musl entry. Some older build pipelines embed a stale apk index file (/var/lib/apk/db/installed) that still lists the pre‑patch version, causing a false positive.

How I confirmed the false positive

I ran three independent checks:

  1. apk info -vv musl inside the container – confirmed the running version.
  2. trivy image --format json – compared the Vulnerabilities array with the InstalledVersion field.
  3. snyk test --docker myrepo/service:latest – Snyk also reported the CVE, but its metadata section showed fixedIn: 1.2.4-r5.

The discrepancy was clear: the scanner’s DB was out of sync, not the image.

Pro Tip: Keep the Trivy DB fresh in CI by adding trivy image --download-db-only before the scan step. It only adds a few seconds to the pipeline but saves you from stale data.

How can I configure Trivy to ignore a known false positive for Alpine 3.20?

Trivy supports an ignorefile (.trivyignore) where you list CVE IDs you want to silence for a given project. The file uses a simple line‑oriented syntax:

# Ignore CVE‑2024‑XXXX for Alpine 3.20 base images
CVE-2024-XXXX

Adding context to the ignore rule

If you want to be more surgical—say, ignore the CVE only when it appears in musl version 1.2.4-r5—you can use the severity and package filters introduced in Trivy 0.45:

# .trivyignore (YAML format supported from v0.45)
- id: CVE-2024-XXXX
  package: musl
  version: 1.2.4-r5
  reason: "Alpine 3.20 patched; false positive due to stale DB"

Place the file at the root of your repository so the CI step picks it up automatically:

trivy image --ignorefile .trivyignore myrepo/service:latest

If you need a per‑pipeline ignore (e.g., only for a specific branch), you can pass the path via the --ignorefile flag.

Pro Tip: Commit the ignore file alongside a ticket reference (e.g., #1234) so future auditors know why the CVE was silenced.

How do I set up Snyk ignore rules for the same false positive?

Snyk’s workflow differs; you create an ignore directly in the UI or via the CLI. The CLI method is ideal for IaC pipelines because it stores the ignore in a snyk.policy file that lives in source control.

Step‑by‑step CLI ignore creation

  1. Run the scan and capture the issue ID:
   snyk test --docker myrepo/service:latest --json > snyk-report.json
   jq '.vulnerabilities[] | select(.id=="CVE-2024-XXXX") | .id' snyk-report.json
  1. Create the ignore rule:
   snyk ignore add --id=CVE-2024-XXXX \
       --reason="Alpine 3.20 patched; false positive" \
       --expiry=2025-12-31 \
       --file=.snyk
  1. Commit the .snyk file so every clone of the repo inherits the rule.

The .snyk file ends up looking like this:

ignore:
  CVE-2024-XXXX:
    - '*':
        reason: "Alpine 3.20 patched; false positive"
        expires: 2025-12-31
        created: 2024-09-10T12:34:56Z

Snyk will now skip that CVE for any Docker image scanned in that project, unless you explicitly override the ignore with --ignore-policy=false.

What are the pitfalls of ignoring CVEs globally versus per‑image?

Ignoring a CVE at the project level (i.e., committing a .trivyignore or .snyk file) is convenient, but it can mask real regressions if the base image changes.

  • Base‑image drift – Alpine 3.20 may be superseded by 3.21, which could re‑introduce a vulnerability with the same ID but a different package.
  • Package substitution – If you later replace musl with glibc (common in multi‑arch builds), the ignore rule may silence a completely unrelated issue.
  • Policy compliance – Some org‑wide security policies forbid any ignore without a documented ticket, so you must surface the ignore decision to auditors.

Safer pattern: image‑specific ignore files

Instead of a repo‑wide ignore, place a .trivyignore next to the Dockerfile that builds the Alpine image. Then reference it explicitly in the CI step:

trivy image --ignorefile ./docker/.trivyignore myrepo/service:latest

That way, the rule travels with the Dockerfile and won’t affect unrelated services that happen to use the same repo.

How can I automate DB freshness checks to avoid stale false positives?

Both Trivy and Snyk provide commands to verify the age of their vulnerability databases.

  • Trivy – trivy db --list prints the timestamp of the last download.
  • Snyk – snyk db --status shows the last sync.

I added a pre‑scan guard in my GitHub Actions workflow:

name: Security Scan
on: [push, pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Update Trivy DB
        run: |
          trivy image --download-db-only
          DB_AGE=$(trivy db --list | grep 'Updated' | awk '{print $3}')
          echo "Trivy DB age: $DB_AGE"
          # Fail if DB is older than 7 days
          if [[ $(date -d "$DB_AGE" +%s) -lt $(date -d "-7 days" +%s) ]]; then
            echo "⚠️ Trivy DB is stale – aborting scan"
            exit 1
          fi
      - name: Scan with Trivy
        run: trivy image --ignorefile .trivyignore myrepo/service:latest
      - name: Scan with Snyk
        run: snyk test --docker myrepo/service:latest --policy-path .snyk

If the DB is stale, the job fails fast, forcing the pipeline to update the DB before proceeding. This pattern eliminates most false‑positive surprises.

FAQ

How do I know if CVE‑2024‑XXXX is truly fixed in Alpine 3.20?

The Alpine security tracker (https://security.alpinelinux.org) lists the fixed version (musl-1.2.4-r5). Cross‑reference that with the package version inside the container (apk info -vv musl). If they match, the vulnerability is patched.

Can I ignore a CVE only for a specific architecture (e.g., arm64)?

Both Trivy and Snyk allow conditional ignores. In Trivy you can add an arch field to the YAML ignore entry; in Snyk you can specify a path pattern that matches the image tag containing the architecture.

What if the false positive appears only in a multi‑stage build?

Run Trivy on each stage separately (trivy fs ./stage1) and apply stage‑specific ignore files. This isolates the problem to the exact layer that still carries the stale metadata.

Does disabling the CVE affect compliance scans?

Yes. If your compliance framework requires a full, un‑ignored scan, you’ll need to submit an exception request. Keep the ignore reason and expiry in the file to satisfy auditors.

Will upgrading to Alpine 3.21 remove the need for these ignore rules?

Potentially. Alpine 3.21 ships a newer musl version and a refreshed vulnerability database. However, you should still verify the DB freshness because third‑party scanners may lag behind upstream releases.

Conclusion

Seeing Docker container scans report false positive CVE‑2024‑XXXX on Alpine 3.20 despite patching is frustrating, but it’s usually a symptom of out‑of‑date vulnerability databases and lingering package metadata. By:

  1. Verifying the installed package version inside the image;
  2. Keeping Trivy and Snyk databases fresh before each scan;
  3. Adding precise ignore rules in .trivyignore and .snyk scoped to the Dockerfile;
  4. Automating stale‑DB guards in CI;

you can eliminate the noise without compromising security posture. The extra few seconds spent updating DBs and maintaining ignore files pay off in faster releases and fewer panic‑button tickets.

Stay tuned for more deep‑dive security engineering posts from SpiritCode.

Keep following SpiritCode for more hands‑on guides like this.

# .trivyignore (YAML format supported from v0.45)

– id: CVE-2024-XXXX

package: musl

version: 1.2.4-r5

reason: “Alpine 3.20 patched; false positive due to stale DB”

“`

Place the file at the root of your repository so the CI step picks it up automatically:

“`bash

trivy image –ignorefile .trivyignore myrepo/service:latest

“`

If you need a per‑pipeline ignore (e.g., only for a specific branch), you can pass the path via the `–ignorefile` flag.

> **Pro Tip:** Commit the ignore file alongside a ticket reference (e.g., `#1234`) so future auditors know why the CVE was silenced.

—

## How do I set up Snyk ignore rules for the same false positive?

Snyk’s workflow differs; you create an **ignore** directly in the UI or via the CLI. The CLI method is ideal for IaC pipelines because it stores the ignore in a `snyk.policy` file that lives in source control.

### Step‑by‑step CLI ignore creation

1. **Run the scan and capture the issue ID**:

“`bash

snyk test –docker myrepo/service:latest –json > snyk-report.json

jq ‘.vulnerabilities[] | select(.id==”CVE-2024-XXXX”) | .id’ snyk-report.json

“`

2. **Create the ignore rule**:

“`bash

snyk ignore add –id=CVE-2024-XXXX \

–reason=”Alpine 3.20 patched; false positive” \

–expiry=2025-12-31 \

–file=.snyk

“`

3. **Commit the `.snyk` file** so every clone of the repo inherits the rule.

The `.snyk` file ends up looking like this:

“`yaml

ignore:

CVE-2024-XXXX:

– ‘*’:

reason: “Alpine 3.20 patched; false positive”

expires: 2025-12-31

created: 2024-09-10T12:34:56Z

“`

Snyk will now skip that CVE for any Docker image scanned in that project, unless you explicitly override the ignore with `–ignore-policy=false`.

—

## What are the pitfalls of ignoring CVEs globally versus per‑image?

Ignoring a CVE at the **project** level (i.e., committing a `.trivyignore` or `.snyk` file) is convenient, but it can mask real regressions if the base image changes.

* **Base‑image drift** – Alpine 3.20 may be superseded by 3.21, which could re‑introduce a vulnerability with the same ID but a different package.

* **Package substitution** – If you later replace `musl` with `glibc` (common in multi‑arch builds), the ignore rule may silence a completely unrelated issue.

* **Policy compliance** – Some org‑wide security policies forbid any ignore without a documented ticket, so you must surface the ignore decision to auditors.

### Safer pattern: image‑specific ignore files

Instead of a repo‑wide ignore, place a `.trivyignore` next to the Dockerfile that builds the Alpine image. Then reference it explicitly in the CI step:

“`bash

trivy image –ignorefile ./docker/.trivyignore myrepo/service:latest

“`

That way, the rule travels with the Dockerfile and won’t affect unrelated services that happen to use the same repo.

—

## How can I automate DB freshness checks to avoid stale false positives?

Both Trivy and Snyk provide commands to verify the age of their vulnerability databases.

* **Trivy** – `trivy db –list` prints the timestamp of the last download.

* **Snyk** – `snyk db –status` shows the last sync.

I added a pre‑scan guard in my GitHub Actions workflow:

“`yaml

name: Security Scan

on: [push, pull_request]

jobs:

scan:

runs-on: ubuntu-latest

steps:

– uses: actions/checkout@v3

– name: Update Trivy DB

run: |

trivy image –download-db-only

DB_AGE=$(trivy db –list | grep ‘Updated’ | awk ‘{print $3}’)

echo “Trivy DB age: $DB_AGE”

# Fail if DB is older than 7 days

if [[ $(date -d “$DB_AGE” +%s) -lt $(date -d “-7 days” +%s) ]]; then

echo “⚠️ Trivy DB is stale – aborting scan”

exit 1

fi

– name: Scan with Trivy

run: trivy image –ignorefile .trivyignore myrepo/service:latest

– name: Scan with Snyk

run: snyk test –docker myrepo/service:latest –policy-path .snyk

“`

If the DB is stale, the job fails fast, forcing the pipeline to update the DB before proceeding. This pattern eliminates most false‑positive surprises.

—

## FAQ

### How do I know if CVE‑2024‑XXXX is truly fixed in Alpine 3.20?

The Alpine security tracker (https://security.alpinelinux.org) lists the fixed version (`musl-1.2.4-r5`). Cross‑reference that with the package version inside the container (`apk info -vv musl`). If they match, the vulnerability is patched.

### Can I ignore a CVE only for a specific architecture (e.g., arm64)?

Both Trivy and Snyk allow conditional ignores. In Trivy you can add an `arch` field to the YAML ignore entry; in Snyk you can specify a `path` pattern that matches the image tag containing the architecture.

### What if the false positive appears only in a multi‑stage build?

Run Trivy on each stage separately (`trivy fs ./stage1`) and apply stage‑specific ignore files. This isolates the problem to the exact layer that still carries the stale metadata.

### Does disabling the CVE affect compliance scans?

Yes. If your compliance framework requires a full, un‑ignored scan, you’ll need to submit an exception request. Keep the ignore reason and expiry in the file to satisfy auditors.

### Will upgrading to Alpine 3.21 remove the need for these ignore rules?

Potentially. Alpine 3.21 ships a newer `musl` version and a refreshed vulnerability database. However, you should still verify the DB freshness because third‑party scanners may lag behind upstream releases.

—

## Conclusion

Seeing **Docker container scans report false positive CVE‑2024‑XXXX on Alpine 3.20 despite patching** is frustrating, but it’s usually a symptom of out‑of‑date vulnerability databases and lingering package metadata. By:

1. Verifying the installed package version inside the image;

2. Keeping Trivy and Snyk databases fresh before each scan;

3. Adding precise ignore rules in `.trivyignore` and `.snyk` scoped to the Dockerfile;

4. Automating stale‑DB guards in CI;

you can eliminate the noise without compromising security posture. The extra few seconds spent updating DBs and maintaining ignore files pay off in faster releases and fewer panic‑button tickets.

Stay tuned for more deep‑dive security engineering posts from SpiritCode.

*Keep following SpiritCode for more hands‑on guides like this.*