Safeguard
Security Guides•

Code coverage percentage and security: myth versus fact

Does 90 percent coverage mean your code is secure? Five myths about coverage numbers, and what to measure instead for security testing in practice.

Safeguard Security Team
3 min read

High line coverage does not mean secure code. Coverage says a line ran during a test, not that the test checked anything about it. Use the number to find untested code, and measure security with assertions, negative cases and findings fixed instead.

Myth 1: 90 percent coverage means 90 percent safe

Fact: coverage counts executed lines. A test can run a function and assert nothing. Delete the authorization check and the line count barely moves, while the risk jumps.

Myth 2: coverage targets drive quality

Fact: a target pushes people toward easy tests. Getters and trivial branches get covered, while the hard parts, such as error paths and permission failures, stay untested. If a team is measured on the number, the number will rise whether or not security improves.

Myth 3: 100 percent branch coverage proves no bugs

Fact: coverage cannot see inputs you did not try. A branch can run once with a friendly value and fail with a hostile one. This is where property tests and fuzzing add value, since they vary the inputs and not just the paths.

Myth 4: low coverage areas are the dangerous ones

Fact: sometimes. A better rule is to cross coverage with risk. Code that handles authentication, parsing of untrusted input, deserialization, payments and file paths deserves a much higher bar than an internal report formatter. Look at uncovered lines inside those modules first.

Myth 5: coverage from end to end tests counts the same as unit coverage

Fact: it depends on what is asserted. A broad browser test may touch many lines without checking any security property. Count coverage where the test makes a claim.

What to measure instead

  • Security assertions per risky module: denied cases, rejected inputs, tamper checks.
  • Mutation score on the critical modules, which asks whether a test fails when the code is changed. See /resources/blog/mutation-testing-to-check-ai-written-tests.
  • Fuzz target coverage for parsers, reported by the fuzzing tool.
  • The share of fixed vulnerabilities that have a regression test.
  • Time from finding to fix.

A worked example

Take a login handler with a lockout rule. A single happy path test gives you high line coverage of the handler. Yet the lockout branch, the disabled account branch and the expired password branch are all unexecuted, and each is a security rule. Reading the uncovered lines in that file tells you exactly which negative tests to write. That is the right use of the number: a map of what is untested, read with risk in mind.

A practical way to use the number

Set a floor so coverage does not slide, review the uncovered lines in risky modules at each release, and never reward a team for the percentage alone. Treat a sudden drop as a prompt to look, not as proof of a problem.

Quick check

Is there a good coverage target for security code?

There is no universal number. Choose a higher floor for authentication and input handling, require negative tests there, and watch the trend rather than a single threshold.

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.