Embedded Expertise

My Project Has 1,000 CVEs — What Now?

Running a security scan and seeing 1,000 CVEs in your project can feel like a nightmare. The number alone is enough to induce panic—but here’s the truth: In reality, this is a common situation and there are practical, structured ways to make sense of it and bring it under control.

In today’s fast-paced development environment, vulnerabilities are inevitable. Modern software relies on countless dependencies—each one a potential attack surface.

This guide outlines how to respond rationally and effectively when faced with a mountain of reported CVEs . While the recommendations are generally applicable to any embedded development stack, we’ll focus primarily on Linux-based systems built with the Yocto Project, as it’s a widely used and highly customizable environment for embedded development.

But What Are Actually CVEs?

CVEs—short for Common Vulnerabilities and Exposures—are standardized identifiers assigned to publicly known cybersecurity vulnerabilities in software or firmware. Each CVE entry describes a specific issue, such as a potential buffer overflow, information leak, or privilege escalation, along with references to public resources, severity scores, and potential mitigations.

For example:

  • CVE-2024-12345 might refer to a specific version of libxml2 that allows random code execution under certain conditions that may be listed in the CVE descritpion.

The catalog of CVEs is managed and published by the MITRE organization, in coordination with the NVD (National Vulnerability Database), software vendors, and researchers.

But CVEs don’t apply in a vacuum. Just because a vulnerability exists doesn’t mean your system is affected in practice—it depends on your usage context, configuration, and exposure.

How CVEs Are Identified in Embedded Projects

In the world of embedded Linux, tools have emerged to help detect known vulnerabilities during the development cycle. If you’re using Yocto, one of the most powerful tools at your disposal is the built-in cve-check class.

When you enable cve-check in your Yocto build configuration, here’s what happens behind the scenes:

  1. Software Inventory: During each build, Yocto collects metadata on all software components included in your image, including their precise versions and source URLs.

  2. Database Query: It then queries one or more public CVE databases to check whether any CVEs are associated with those specific versions. Common data sources include:

  3. Report Generation: A report is produced, usually in both machine-readable and human-readable formats, listing all CVEs found, their severity scores (e.g., CVSS), descriptions, and links to more information.

It’s not unusual for this report to list hundreds—or even thousands—of CVEs. Why?

Because the database contains every known vulnerability for each version of each package, and many of them may not be relevant to your usage. Some may already be patched in a newer upstream version, some may not be triggered unless the software is misconfigured, and others might simply not be used at runtime.

Why Your CVE Count Might Be Lying to You

Here’s something crucial to understand right away:

The tools reporting CVEs usually work purely on metadata: basically the name and version of a package.

They do not analyze how that software is configured, compiled, or actually used in your project.

This distinction is key to demystifying those overwhelming numbers.

Let’s take a common example from embedded Linux: your project includes Linux kernel version 5.10. The CVE scanner will flag every known vulnerability associated with that version—regardless of whether the affected code is even present in your build. There are 3,333 of them at the moment of this writing.

Say one CVE concerns a FireWire stack vulnerability. If your kernel configuration has CONFIG_FIREWIRE=n, then the relevant code isn’t compiled in. That CVE? Not applicable. CVE count: minus one, at least in your own mental model.

But here’s the catch: the tool may still report it. Disabling features won’t usually make your CVE report any shorter, while removing packages will. Housekeeping doesn’t always reduce the reported number—it just makes many of those CVEs irrelevant to your specific system.

This is where disciplined configuration pays off. When you minimize what’s included in your image:

  • You reduce your actual attack surface.

  • You shrink the subset of CVEs you need to analyze.

  • You gain clarity: what remains is more likely to matter.

In practice, this can be dramatic. The Linux kernel is often the largest single contributor to the CVE count in an embedded project. But that count includes vulnerabilities from all subsystems and drivers, most of which are unused in a typical configuration. By comparing CVEs against your actual .config, the number of vulnerabilities that truly need investigation can drop by 80–90%.

So do your housekeeping:

  • Prune unneeded kernel options.

  • Minimize your userland packages.

  • Disable unused features at every level.

But remember: this won’t necessarily reduce the number in the report dramatically. What it gives you is the ability to say, with justification: “Out of 1000 CVEs, these 800 don’t apply to my system.”

But How Do You Know Which Ones Apply?

That’s the next step: triage. Once you’ve stripped your system down to what you really need, it’s time to examine the CVE list with a critical eye and determine which issues are:

  • Clearly irrelevant

  • Technically present but non-exploitable

  • Legitimate risks requiring an update or a patch

We’ll walk through that process in the next section.

Triage: Sorting Out What Really Matters

Now that you’ve done your housekeeping and cut away the cruft, it’s time to face what remains: the list of reported CVEs. This is where triage begins—the process of evaluating each vulnerability and deciding whether it’s relevant, and if so, how urgent it is.

Triage is not just about technical analysis. It’s also about traceability and accountability. Every CVE must end up in one of two buckets:

  1. Action Required: This CVE affects your actual system and needs to be fixed, mitigated, or worked around.

  2. Irrelevant: This CVE is not applicable due to configuration, usage, or system architecture.

Here’s how to proceed.

Step 1: Identify the Component and Context

For each CVE:

  • What component does it affect?

  • What version is the CVE known to impact?

  • Is the affected feature compiled in and enabled in your build?

  • Is the component accessible or used in a way that makes the issue exploitable?

For example:

  • If a CVE affects the Bluetooth stack but your kernel is built with CONFIG_BT=n, it’s irrelevant.

  • If a flaw is found in a web interface your system doesn’t deploy or expose, same conclusion.

Step 2: Document Your Decision

This step is critical: even if a CVE is determined to be irrelevant, that conclusion must be recorded and justified. Don’t just ignore it—prove it was reviewed.

You should maintain a CVE whitelist—a structured file (CSV, YAML, JSON, etc.) where you track:

  • CVE ID

  • Component name and version

  • Date of review

  • Decision (e.g., “Not applicable: feature disabled in config”)

  • Reviewer identity (optional but good practice)

  • Reference to configuration evidence or usage constraints

This whitelist becomes part of your project’s quality records. It shows that nothing was ignored or missed—it was all triaged.

Step 3: Focus on the Actionable Set

Once triage is complete, you’ll have a much shorter list: CVEs that are relevant and need action. These are the ones that should:

  • Be shown in your final security report

  • Trigger patching, package upgrades, or mitigations

  • Be tracked through your issue or defect management system

This refined list is no longer overwhelming. It’s real, manageable, and actionable. You’ve gone from 1000+ abstract warnings to a concrete set of security tasks backed by engineering judgment.

But How Do You Audit 1000 CVEs?

Let’s be realistic: manually reading and analyzing a thousand CVEs isn’t feasible in the context of a commercial project. But neither is relying blindly on a report generator. What you need is triage—real decision-making—at scale. That means using tools that help you do more than count vulnerabilities. You need tools that help you filter, classify, and justify each item, one by one.

Triage Is Not Reporting

Many CVE scanners are great at collecting data. But triage is a different task. It involves:

  • Understanding what each CVE actually affects

  • Matching it against your build configuration

  • Making a clear decision: does this apply or not?

  • Recording that decision in a traceable way

Tools like:

  • Yocto’s cve-check (paired with external tools)

  • cve-bin-tool

  • Google’s OSV-Scanner

…can assist with portions of this process. But here’s the hard truth: No tool covers all cases.

The CVE descriptions are written by humans, often without consistent editorial standards. Some are precise and insightful; others are vague or lack context entirely. For example, they don’t always say:

  • Which kernel options or library features must be enabled for the vulnerability to apply

  • Whether the attack surface is local, remote, or theoretical

  • What mitigations might already be in place in your stack

So what do you do? You combine multiple tools—each giving you part of the puzzle—and fill the gaps with manual analysis. That’s where experience and expertise makes the difference.

What We Do at Embedded Expertise

At Embedded Expertise, we’ve developed a toolbox to accelerate and document CVE triage for embedded projects. But let’s be clear: there’s no silver bullet.

Our approach isn’t one polished product. It’s a tailored blend of scripts, lookups, and AI-driven summaries—adapted to each project’s structure and technology stack. For every customer, we tune:

  • Which APIs we query

  • How we match packages and configs

  • What decision logic is automated vs. manual

These tools help reduce triage time by an order of magnitude—but they still require engineering judgment.

In the end, it’s always a mix: tools to crunch the data, and people to make the call.

The result is not just a shorter list of CVEs, but a documented decision history showing which CVEs were reviewed, which were ignored (with justification), and which require real action.

What to Do with Actionable CVEs

After your configuration has been stripped down and a proper triage is complete, you’re left with a focused list of actionable CVEs—the ones that genuinely affect your system. These must now be dealt with methodically. Depending on the specifics of the CVE, there are several paths that can be followed.

Option 1: Update

The most straightforward way to fix a CVE is to update the affected component to a version where the vulnerability has been resolved. If the fix exists upstream, and you’re not locked into a specific version, this is usually your best starting point.

However, in embedded development—especially in mature or long-maintenance-cycle projects—you almost never want to blindly update to the latest version. That path often leads to:

  • Functional regressions

  • API incompatibilities

  • Broken builds due to dependency mismatches

Be Smart: Stick to Patch-Level Updates When Possible

Most versioning schemes follow the MAJOR.MINOR.PATCH format. Here’s what that means for you:

  • MAJOR version bumps may break compatibility and require significant rework.

  • MINOR versions add features but can still introduce subtle changes.

  • PATCH versions are typically safe—they’re supposed to include only bug and security fixes.

So if your system is using version 2.14.3 and a CVE is fixed in 2.14.7, the safest and smartest choice is to update to that patch release, staying within the same MAJOR.MINOR line. This approach balances security with stability.

➤ But Never Skip the Tests

Even patch-level updates can cause side effects. That’s why every version change must be verified, ideally through automated testing. This is yet another reason to invest in a complete and fluent CI/CD and IVVQ flow.

A strong automated pipeline will:

  • Rebuild your image after any dependency change

  • Run functional, regression, and integration tests automatically

  • Catch side effects early, before they reach the field

Without this safety net, even the smallest update can be a risk. With it, you gain the confidence to stay secure without constantly firefighting regressions.

Option 2: Patch

If updating isn’t feasible—due to project constraints, integration risks, or regulatory certification—you can either look for an existing patch, or backport the upstream fix into your current version.

This is common in long-lived embedded platforms. The process:

  • Identify the upstream commit that fixes the CVE

  • Extract the relevant patch

  • Apply it via a .bbappend file in Yocto

  • Document it with references to the CVE ID and upstream source

Patching takes more effort than updating but allows you to stay secure while maintaining stability.

Option 3: Mitigate

In some cases, the vulnerable code needs to stay—but the risk can be contained through runtime constraints or system design.

Examples:

  • Disable the service or interface exposing the vulnerability

  • Restrict network access to the component

  • Use seccomp, SELinux, or AppArmor to limit its capabilities

Mitigations are valid, but they must be documented and maintained. A mitigation that silently breaks under future changes isn’t a mitigation at all.

Option 4: Justify and Accept (With Care)

Some vulnerabilities may remain unpatched—deliberately—because they pose no real risk in your context.

Examples:

  • A local DoS in a service that is never installed

  • A flaw in a media parser that’s not included in your build

  • A vulnerability in an internal-only tool never exposed to users

In these cases, it’s acceptable to whitelist the CVE—but only with a documented justification. This includes:

  • The CVE ID

  • Your rationale

  • References to your system’s configuration or usage

This shows auditors (and future you) that the decision was deliberate, not negligent.

The Goal: Zero Unhandled CVEs

At the end of this process, there should be no unexplained CVEs left in your reports.

Every reported CVE must fall into one of three categories:

  • Whitelisted – Verified as irrelevant to your system

  • Fixed – Patched, updated, or mitigated

  • Justified – Risk accepted with clear rationale

Achieving this clean state is essential. It means:

  • Your team is not flooded by a hundred irrelevant warnings every build

  • The truly important vulnerabilities stand out at the moment they show up

  • Your development process is secure, auditable, and professional

Bonus benefit: zero unhandled CVEs is not just a security goal—it’s a productivity goal.

Staying Clean: Long-Term CVE Hygiene

Fixing CVEs once is good. Staying on top of them continuously is better.

Security isn’t a one-time task—it’s a process. The sooner your team integrates vulnerability tracking into its normal development lifecycle, the less disruptive it becomes over time. Here’s how to make CVE management a seamless part of your workflow.

Integrate CVE Checks into Your CI Pipeline

As discussed earlier, a clean CVE report doesn’t mean zero vulnerabilities—it means zero unhandled vulnerabilities.

Track your triaged CVEs in a versioned, human-readable whitelist file:

  • Clearly mark which CVEs are irrelevant, patched, or justified

  • Store the whitelist alongside your code (e.g., in your Git repo)

  • Update it as your system evolves or dependencies are upgraded

This ensures traceability, audit readiness, and peace of mind.

Schedule Periodic Re-Audits

Some CVEs may not apply today—but if your configuration or threat model changes, they might become relevant later.

Schedule regular reviews of:

  • Your whitelist (are the justifications still valid?)

  • Your kernel/userland configurations (any new features enabled?)

  • Vulnerability databases (new CVEs for your existing versions?)

Security is never static. Treat it like a maintenance task, not a fire drill.

Automate Where Possible, Review Where Needed

Automation is your best ally for:

  • Fetching CVE data

  • Matching it to components

  • Generating reports and diffs

  • Populating baseline triage records

But tools alone aren’t enough. As discussed earlier, no tool understands your exact configuration, threat model, or deployment context. You’ll always need human judgment to make the final call.

At Embedded Expertise, we use a combination of scripted triage tools and hands-on analysis to keep our clients’ projects clean, secure, and audit-ready. Every system is different, and there’s no “one-size-fits-all” solution—but a disciplined approach always works.

A Word of Caution: CVEs Only Show Part of the Picture

It’s worth repeating: a clean CVE report is not a guarantee of system security. It simply means that—as far as known, published vulnerabilities go—you’ve handled everything that’s been documented.

But CVE-based tooling only sees the tip of the iceberg. It does not detect:

  • Zero-days — vulnerabilities that are not yet discovered or disclosed

  • Bugs in closed-source software, including:

    • Proprietary third-party libraries

    • Firmware blobs

    • Driver stacks or middleware delivered as binaries

  • Vulnerabilities in your own code, unless you’ve published them and assigned CVEs (which is rare for internal projects)

This means CVE handling is just one part of a full security strategy. To go further, you need:

  • Static analysis to catch bugs in your own code

  • Fuzzing and penetration testing to discover edge cases and exploit paths

  • Security reviews focused on your architecture, interfaces, and threat model

  • Hardening measures like sandboxing, least privilege, and secure defaults

CVEs are a useful mirror—but you still need to check the blind spots.

Key Takeaways

A thousand CVEs is not a crisis—it’s a wake-up call. It means your system is composed of real software, used in the real world, where vulnerabilities happen. The key is to cut through the noise, identify the real risks, and deal with them methodically.

By:

  • Stripping unneeded features,

  • Triage-auditing every CVE,

  • Fixing what matters and documenting the rest,

  • And maintaining a clean report at every stage of your development flow,

…you turn security from a source of fear into just another aspect of solid engineering.

 

The goal is not perfection. The goal is confidence.
Confidence that every CVE has been seen, understood, and handled.

Bonus: Download the CVE Workflow Cheat Sheet

Want a printable one-page summary of everything covered in this article?

👉 Download the CVE Processing Workflow Cheat Sheet (PDF)

Use it to guide your triage process, build your whitelist, and train your team.