A result showing “0 vulnerabilities” does not always mean that every relevant dependency was actually analyzed.

In August 2026, while investigating a Python dependency-graph issue in dependabot-core, a defect was identified in the way Dependabot’s Python FileParser evaluates certain environment-marker expressions in requirements.txt.

The problem is subtle: a valid dependency can be removed from the resolved dependency information before downstream security analysis can use it.

A proposed fix exists in Dependabot Pull Request #15875, “Align Python marker parsing between FileParser and UpdateChecker to avoid dropping requirements.”

At the time of our verification, PR #15875 was still a Draft, remained a work in progress, required review, and had not been merged.

Dependabot pull request 15875 shown as Draft
Dependabot PR #15875 during our verification on August 27, 2026.

What are Python environment markers?

Python dependencies can be conditional on characteristics of the environment in which a project runs.

For example:

greenlet==3.5.4 ; python_version >= "3.9" and platform_machine == "x86_64"

This means that greenlet==3.5.4 applies only when both conditions are true:

Environment markers are a normal part of Python packaging. The problem begins when a tool evaluates the expression incorrectly and decides that a valid dependency should not be included.

The defect identified in Dependabot

PR #15875 documents inconsistent behavior in Dependabot’s legacy marker handling.

An expression using or could retain the dependency:

python_version >= "3.9" or platform_machine == "x86_64"

while an expression combining the same types of conditions using and could cause the dependency to be discarded:

python_version >= "3.9" and platform_machine == "x86_64"

The PR author narrowed the defect to the legacy marker handling in FileParser when Python-version and platform conditions are combined with and. The regression tests added in the proposed fix verify that valid dependencies such as greenlet remain present in the resolved dependency graph.

The proposed fix moves marker evaluation toward a shared evaluator so that FileParser and UpdateChecker apply the logic consistently. The PR also includes dependency-graph regression coverage for compiled requirements containing marker expressions.

Why this matters for dependency security

The problem is not simply that a dependency graph may become incomplete.

If a dependency disappears before vulnerabilities are matched against resolved package versions, the absence of a finding becomes ambiguous.

These two outcomes are fundamentally different:

Dependency analyzed → no vulnerability found

and:

Dependency excluded before analysis → no vulnerability reported

Both can eventually appear to the user as “nothing found.”

From a security perspective, however, they are not equivalent.

This is one of the more difficult failure modes in dependency automation: it is necessary to verify not only the findings produced by a scanner, but also whether the correct dependency set reached the scanner in the first place.

Testing the same marker structure with LegacyFixer

LegacyFixer uses pip-audit in its passive scanning path for a repository containing requirements.txt.

That path is detect-only: it does not invoke --fix, create branches, commit changes, or push modifications to the repository.

We tested one specific question:

Does the LegacyFixer passive-scanning path reproduce the same failure mode for a python_version AND platform_machine expression?

We did not assume the answer.

The test environment was:

Python 3.12.3
Linux x86_64
pip-audit 2.10.1

Both conditions in the following marker were therefore true:

greenlet==3.5.4 ; python_version >= "3.9" and platform_machine == "x86_64"

pip-audit retained the dependency explicitly:

greenlet==3.5.4

We then changed only the Python condition:

greenlet==3.5.4 ; python_version < "3.9" and platform_machine == "x86_64"

In the same environment, that expression is false.

greenlet was excluded.

This negative control matters because it shows that the marker was actually being evaluated rather than simply ignored.

Testing through the actual LegacyFixer passive-scan routine

We then tested the real LegacyFixer passive-scan function rather than calling pip-audit alone.

To make the result directly observable, we used:

requests==2.19.1

because this version produces known vulnerability findings in the test environment.

The positive case was:

requests==2.19.1 ; python_version >= "3.9" and platform_machine == "x86_64"

LegacyFixer reported:

PASSIVE PIP-AUDIT FINDINGS

and the output contained vulnerability findings for requests==2.19.1.

For the negative control, we used:

requests==2.19.1 ; python_version < "3.9" and platform_machine == "x86_64"

LegacyFixer reported:

PASSIVE PIP-AUDIT CLEAN

because the false environment marker correctly excluded the dependency in that environment.

The screenshot below shows the positive and negative cases from the controlled LegacyFixer run. Both use an and expression: the Python condition alone is changed so that the marker is true in one case and false in the other.

LegacyFixer marker test showing positive findings and negative clean result
Controlled positive and negative marker tests through the LegacyFixer passive-scan routine.

The result can be summarized as follows:

Test case Result
Satisfied environment marker Dependency retained and audited
Unsatisfied environment marker Dependency excluded
Satisfied python_version AND platform_machine The Dependabot failure mode documented in PR #15875 was not reproduced

What this test demonstrates — and what it does not

The experiment demonstrates that, in the case we tested, the LegacyFixer detect-only path did not reproduce the specific marker-evaluation failure mode documented in Dependabot PR #15875.

It does not demonstrate that LegacyFixer is immune to every possible environment-marker problem.

It does not demonstrate that LegacyFixer is generally more correct than Dependabot.

And this was not a benchmark in which both products were executed side by side against the same repository.

The Dependabot defect is documented upstream through its own regression test and proposed fix. Our LegacyFixer experiment was performed separately using the same logical marker structure.

That distinction matters.

Detection first, modification later

The case also illustrates why LegacyFixer separates dependency detection from repository modification.

In passive mode, the first question is:

What problems exist?

The scanner can answer that question without automatically turning every result into a branch or Pull Request.

A separate decision can then determine whether a repository modification should be proposed.

This architecture cannot eliminate every possible scanning error. No dependency-security tool can make that assumption.

But it reduces the number of stages in which an uncertain detection result immediately produces repository changes.

In dependency automation, more automation does not automatically mean more certainty.

Sometimes the safer sequence is simpler:

First verify the signal. Then decide whether the code should change.