Safeguard
Developer Tools•

Can you mark a failing security test as xfail without hiding the bug?

Use xfail with strict mode and a ticket reference. The test keeps running, and the suite fails the day the bug is fixed so the marker gets removed.

Safeguard Security Team
4 min read

You can, if you use it carefully. In pytest, xfail runs the test and expects it to fail, so the bug stays visible in reports. Turn on strict mode so a fix makes the suite fail until the marker is removed, and always attach the ticket that tracks the defect.

xfail versus skip

The pytest documentation explains the difference. A skipped test does not run. An xfail test does run and is expected to fail. If it fails, no traceback is shown and the result is reported as XFAIL. If it unexpectedly passes, the result is XPASS.

For security work this distinction matters a lot. A skipped security test is invisible. An xfail test is a standing, running statement: the exploit still works today.

Why strict mode is the important part

By default, neither XFAIL nor XPASS fails the suite. That is a trap. Someone fixes the bug, the test starts passing, the run says XPASS, and nobody notices. The marker stays forever and the test no longer protects anything, because a regression would flip it back to XFAIL silently.

Setting strict=True on the test makes an XPASS count as a failure. You can set the default for all tests with the xfail_strict ini option. With strict on, the fix commit breaks the build, which prompts the developer to delete the marker and turn the test into a normal regression test. That is exactly the lifecycle you want.

Steps

  1. Write the security test as if the bug were fixed: it asserts the safe behavior.
  2. Mark it xfail with strict enabled and a reason that names the ticket.
  3. Run the suite and confirm it reports XFAIL, not a pass.
  4. Add a due date in the ticket, not in the code.
  5. When the fix lands, the strict failure appears. Remove the marker in the same pull request.
  6. Confirm the test now passes as a normal test.

When it is acceptable

Situationxfail OK?
Confirmed bug, fix scheduled, ticket openYes
Confirmed bug, no ownerNo, assign an owner first
Flaky test you do not understandNo, investigate it
Risk formally accepted by the ownerYes, with the acceptance recorded in the ticket
You simply want a green buildNo

The last row is the honest one. Xfail is for documenting a known gap, not for hiding one.

Individual cases inside a parametrized test

The pytest docs show that a single case in a parametrized test can be marked by wrapping it with pytest.param and an xfail mark, so only that case reports as xfailed while the rest pass. That is useful when one attacker input still works and the others are blocked.

Reporting

Count xfailed security tests in your release notes or dashboard. A rising number is a risk signal. Run pytest with the option that lists xfail results so reviewers see them, rather than scrolling past a summary line.

Review the list monthly. Anything older than your agreed fix window needs an escalation, not another month of silence.

Try it yourself

Click through it at your own pace, or open it full screen. Look at how a gate turns a quiet signal into a visible decision, which is what strict mode does for xfail.

Safeguard offers policies and gates that can block merges on rules you set. A free scan shows the dependency findings you would be gating on.

Quick check

Does xfail still run the test?

Yes. Unlike skip, xfail runs the test and expects a failure, unless you set run to false.

What does strict do?

It makes an unexpected pass fail the suite, so the marker cannot linger after the bug is fixed.

Sources

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.