All articles
GoKubernetesSecurityCVEVEXOpen SourceKubescape

How I Found and Fixed 7 Hidden Bugs in a Kubernetes Security Tool

Hunting subtle data-ownership bugs, race conditions, cache inconsistency, and non-deterministic hashing in Kubescape's vulnerability scanner.

Jul 2026
9 min read

The Part of the System I Worked On

I worked on Kubescape Kubevuln, an open-source tool that scans container images for security vulnerabilities (CVEs). Over a few weeks, I found and fixed 7 bugs — all in the same part of the system.

These weren't simple typo fixes. They were bugs that only show up after real, repeated use: when the same image is scanned again and again, when outside data comes from other tools, and when specific edge cases line up. Finding bugs like this needs more than reading code — it needs understanding how the whole system is supposed to behave, and noticing where it quietly breaks that promise.

When Kubevuln scans an image, it finds CVEs and needs to record a decision for each one: is this CVE dangerous, or has someone decided it's safe to ignore? This record is stored in a format called VEX (Vulnerability Exploitability eXchange). Think of VEX as a decision log: 'This CVE was checked, and here is why it's marked safe or not safe.'

A CVE can get marked 'safe to ignore' in two different ways:

Infographic 01 — Kubevuln Architecture & 7 Bugs Taxonomy
Kubevuln Architecture & 7 Bugs Overview Diagram
Click to inspect
Kubescape Kubevuln container scanning pipeline and taxonomy of 7 VEX data integrity bugs.
  • 1. A person creates a rule (called a SecurityException) saying 'ignore this CVE.'
  • 2. An outside trusted source (like Red Hat or Debian advisories) already says 'this CVE doesn't apply here.'

The bugs I found all came from one repeated mistake in the code: it assumed every ignored CVE came from source #1, without actually checking. Once you see that pattern, you start finding it everywhere — and that's exactly what happened.

Bug 1 & Bug 2: Data Loss & False Safe Grouping

Bug 1: A Real, Unsolved CVE Was Being Hidden — A CVE marked 'under investigation' (meaning: not yet known if it's safe) was being treated the same as a CVE that was confirmed safe. It got hidden from scan results. Hiding an unresolved risk is worse than showing a false alarm. A security tool should never make an open question disappear. I traced the logic from CVE status to UI presentation and skipped grouping 'under investigation' with confirmed safe entries.

Bug 2: Ignored CVEs Disappeared Instead of Being Recorded — When someone ignored a CVE using a rule, the CVE vanished completely from the VEX report — no record it ever existed. In security, 'we chose to ignore this' and 'we forgot this exists' must never look the same. If someone audits the system later, they need proof a decision was made. I updated the system to retain the CVE in the report, marked explicitly as 'not affected, ignored on purpose, here's why.'

Bug 3 & Bug 4: Data Ownership & Untagged Records

Bug 3: The Tool Was Overwriting Outside Data It Didn't Own — Every time a scan ran, the code reset all VEX records — including ones that came from outside trusted sources, not from Kubevuln itself. So trusted external data was being silently destroyed and replaced. This is a data-ownership bug: the code had no way to tell 'data I created' apart from 'data someone else gave me.'

My fix: I gave every record Kubevuln creates a clear, unique tag (a namespaced ID). Now the reset logic only touches records with that tag — anything else is left untouched. I didn't build an over-engineered subsystem; I used an ID that the data already needed and made it self-describing.

Bug 4: A Follow-Up Bug the First Fix Didn't Catch — After fixing Bug 3, I found a related case: some outside records had no ID at all. The old code guessed 'no ID means it's probably ours,' which was wrong. I made the rule strict: something is only treated as 'ours' if it has our exact tag. No tag means 'leave it alone.'

The trade-off: A few legacy records would now be duplicated instead of updated. I chose this on purpose. A duplicate is easy to clean up later. Silently overwriting someone else's data is catastrophic. When in doubt, pick the recoverable failure mode.

Infographic 02 — VEX Lifecycle & Data Ownership
VEX Lifecycle & Data Ownership Diagram
Click to inspect
Namespaced record tagging preventing external advisory data from being overwritten during repeated scans.
Data Ownership Invariant
Never perform a bulk reset on heterogeneous datasets without verifying provenance tags. If a record lacks an explicit ownership signature, fail safe by treating it as external read-only data.

Bug 5 & Bug 6: Inaccurate Reasons & Cached Scan Reversion

Bug 5: The Wrong Reason Was Being Shown for Ignored CVEs — Every ignored CVE showed the same message: 'ignored by a SecurityException' — even when that wasn't true. Some were ignored by outside data, not a rule someone created. I made the code inspect provenance and display the authentic justification.

Bug 6: Cached Scans Un-Ignoring CVEs That Should Stay Ignored — If the same image was scanned twice, a CVE that was correctly ignored (from outside data) would sometimes come back as 'active' again on the second scan — because the code mistook it for a different kind of record. I tightened the validation to only treat records as exceptions when they contain exact exception markers.

Being honest about the fix's limits: The true root cause of this bug lived in a separate upstream repository. Rather than hiding this, I clearly documented the boundary in the PR, shipped the safe partial fix for immediate protection, and provided the upstream roadmap for full resolution.

Infographic 03 — Rescan Lifecycle & Cache Invalidation
Rescan Lifecycle & Cache Consistency Diagram
Click to inspect
Preventing cached image scans from un-ignoring valid vendor advisories.

Bug 7: Inconsistent Fingerprints From the Same Data (Non-Deterministic Hashing)

What was wrong: The system builds a fingerprint (a SHA-256 hash) for each VEX document to check if anything changed. But the exact same document was producing different fingerprints across runs.

I uncovered four separate causes of non-determinism:

Infographic 04 — Deterministic VEX Fingerprinting
Deterministic VEX Fingerprinting Diagram
Click to inspect
Eliminating Go map iteration randomness, delimiter collisions, and sort mutations to guarantee 100% deterministic cryptographic hashing.
  • 1. Go map iteration order is randomized by design in the Go runtime.
  • 2. Two fields were concatenated with no delimiter, allowing collisions (e.g., 'CVE-1' + '23' vs 'CVE-12' + '3').
  • 3. Sort comparisons lacked tie-breaker criteria for identical primary keys, leaving order undefined.
  • 4. The sort function mutated slice elements in place, creating subtle side-effects in caller routines.

How I fixed each one: Sorted map keys deterministically; added unambiguous boundary delimiters; introduced composite multi-key tie-breakers; and executed sorting exclusively on defensive slice copies.

Performance optimization: Full deep comparisons are expensive. I added a fast-path prefix comparison: most CVEs have distinct names and resolve in O(1), reserving multi-field tie-breaking for true collisions.

What This Project Shows & Senior Engineering Decision Framework

Looking back at all 7 fixes together, they share one theme: the code was making assumptions instead of checking facts — assuming who owns data, assuming things are always ordered the same way, assuming a missing value means one thing when it could mean another.

Finding and fixing these bugs needed root-cause thinking, careful trade-off decisions, rigorous test discipline, and transparent communication with maintainers.

Infographic 05 — Senior Engineering Decision Framework
Senior Engineering Decision Framework Diagram
Click to inspect
Core engineering principles: Root-cause identification, failing safe, defensive copying, and comprehensive regression suites.
Summary
None of these fixes needed a big rewrite. Each one was a small, precise change — the hard part was finding exactly where the system's logic broke its own promise, and fixing it without breaking anything else.
Himanshu Soni
Himanshu Soni
Open Source Contributor • Building full-stack systems, AI agents, and developer tools.