//nefariousplan

Todo That Shipped

The fix was in a comment. The code shipped without it. Audit your oldest TODOs.

There is a comment in the codebase that names the fix. The comment says, in some form, that the code below needs validation, or escaping, or a length check, or a type guard. The comment has been there for years. The code has been there for the same years. The fix has not.

A security researcher finds the comment. The comment is the researcher's map. It tells them what the original author knew was wrong, and what the original author never got around to doing. The researcher writes the exploit that targets the exact gap the comment documents. The CVE that follows reads like a sentence the codebase wrote about itself, long before the exploit existed.

The pattern is the TODO that shipped. Not as a stylistic annoyance. As a published record of a known vulnerability that the project chose to leave in place.

Mechanism

A TODO comment is contemporary documentation. Someone who understood the code well enough to write the function also understood its gap well enough to name the gap. At the moment of writing, the author knew what was missing. They chose to ship without it. The decision may have been deliberate (ship now, fix in a follow-up) or accidental (the follow-up never happened, the person left, the codebase lost the context).

The shape this pattern describes is the case where the comment's claim is a security-relevant property: input validation, authentication, escaping, bounds checking, type guarding, authorization. If the comment says "TODO: verify this path is under the base directory" and the code concatenates the input path into a filesystem call without the verify, the codebase has declared its own path-traversal vulnerability. The comment is not evidence of bad faith. The comment is testimony that the issue was seen, named, and deferred.

Attackers read commented-intentions-now-public the same way maintainers should: as a shortlist of where the code self-identifies as weak. Grepping a repository for TODO, FIXME, XXX, HACK across security-sensitive code paths is a cheap and consistently rewarding exercise. Shipped TODOs compound over time because every year of uncorrected TODO is a year of additional certainty that nobody with commit access is going to fix this one. CVE issuance for long-standing TODOs consistently reveals that the comment pre-dates the exploit by years.

The pattern is not that TODO comments exist. Every codebase has them. The pattern is that a specific TODO in a specific file describes a specific security failure that the code below the TODO performs, and the combination has shipped long enough for researchers to find it.

Exhibits

CVE-2026-1368: The Patch Is Two Characters. The Line Was in the File.. This is the pattern in its strict-canonical form. The catalog's boundary clause on todo-that-shipped distinguishes it from commented-out-code-is-testimony by saying the named control's only manifestation must be the comment. CVE-2026-1368 is one step further along that spectrum: the control's only manifestation is a working line of code with two characters of comment marker in front of it. check_ajax_referer( '_nonce_zvc_security', 'noncce' ) was written. Someone added // and shipped. The line lived through seventeen point releases of a multi-year commercial WordPress plugin while the bundled WebSDK kept POSTing the field name the commented line was guarding. The patch removes two characters. The fix did not have to be authored. It had to be uncommented.

CVE-2026-9082: The Comment Said Use the Array Values. A 2020 Refactor Started Using the Keys.. The canonical Todo That Shipped is a comment naming a control that was never written. This post documents the variant where the control was written, lived under the comment for five and a half years, and was deleted by a 2020 refactor that left the comment standing. The 2026 patch is array_values(), the exact function call the comment names. The grep target for both attacker and defender is the same line of source.

CVE-2026-27478: Unity Catalog's JWT Verifier Asks the Token Where Its Key Lives. The Javadoc on grantToken in commit 9717a3b (August 26, 2024) named the gap correctly: "Currently the issuer for the incoming token to validate is not constrained to a specific identity provider, rather as long as the token is signed by the matching issuer the validation succeeds. Eventually this should be constrained to a specific identity provider..." The fix landed in commit 89b91863 on March 12, 2026, eighteen and a half months later, replacing the Javadoc with one that names the new server.allowed-issuers allowlist. Unlike most exhibits of this pattern, the TODO was not a stray // TODO deep inside a method; it was the public-facing API documentation directly above the route handler, the contract every downstream developer reading the file consulted to understand what the endpoint did. The bug was the documented behavior.

CVE-2026-42779: Apache MINA Shipped the Fix on One Branch. The Same Day, Two Other Branches Shipped the Bug.. The 2.2.7 patch carries two TODOs in the same function. The comment on the type-1 path reads "Use initialize=false to prevent static block execution during class loading"; the line below passes true. Earlier, the 2024-11-06 commit that introduced the acceptMatchers feature deleted (rather than uncommented) a years-old line invoking Apache Commons IO's ValidatingObjectInputStream, which is the correctly-layered allowlisting deserializer. The codebase named the right control twice and, in the same commit each time, declined to install it. The CVE that follows is the gap each comment described.

CVE-2024-22120: Zabbix's Audit Log Is the Read Primitive. The patch for CVE-2024-22120 added three lines of comment to nodecommand.c explaining that the project's IPv6 address validator could not distinguish RFC-legal zone identifiers from injection payloads, so input validation on clientip was abandoned at the dispatch site, with the future-tense promise that it could be added back if the validator improved. The named control has no implementation in the file. The comment is the only manifestation. What makes this exhibit specific is the timing: the TODO did not pre-date the CVE by years; it landed inside the security commit, naming the gap that would feed the next sink to use clientip in a string-building context.

Boundaries

Not every TODO is this pattern. Most TODOs are operational: "clean up this API", "add logging here", "factor out this duplication". They name ergonomic or maintenance debts, not security properties. The pattern specifically requires that the TODO names a security-relevant control (auth, validation, escaping) AND the adjacent code operates without that control.

Not every commented-out block. Removed code that was meant to be restored and never was is a different shape, closer to commented-out-code-is-testimony (reader reads the comment for capability). Todo-that-shipped requires that no code for the named fix was ever written; the comment is the fix's only manifestation.

Not every deferred fix. Some teams mark security issues via tickets in a tracker rather than TODOs in source. A ticket that sits in a backlog for five years is the same organizational problem with a different storage location. The pattern is specifically about the in-source artifact: comments attackers can read by cloning the repository.

Defender playbook

Grep your own security-sensitive codebases for TODO, FIXME, XXX, HACK in paths that handle authentication, input parsing, filesystem operations, subprocess spawning, or template rendering. Sort by age (git blame). Anything older than eighteen months is accumulated technical debt with security implications; act on it or remove the comment with an explicit decision.

Treat a TODO next to unsafe code as a shipped vulnerability, not a note. If the code is in production and the TODO describes a control that is not in place, you have the vulnerability regardless of whether anyone has weaponized it yet. The comment is the codebase telling you it exists.

When reviewing dependencies, grep their source the same way. grep -rE 'TODO|FIXME|XXX|HACK' vendor/ is a cheap audit that consistently surfaces latent issues in libraries you depend on. Upstream a fix or plan a migration before the library's own CVE makes the decision for you.

Do not accept "that's from the previous team" as a disposition. Code you run is code you own. If a TODO pre-dates your tenure and the control is absent, the control is absent in your deploy. Fix it or accept it as a risk with a signed owner, but do not let it sit unattributed.

In code review, reject new commits that add a TODO next to unsafe code unless the commit also includes a ticket reference with a bounded timeline. A TODO with no owner and no date is a forever-TODO, and forever-TODOs become shipped vulnerabilities by default.

Kinship

Commented Out Code Is Testimony. Sibling reading skill. Both patterns treat comments as evidence about what the author knew. Commented-out-code-is-testimony reads what was built and withheld. Todo-that-shipped reads what was named and not built. Same discipline, different artifact.

Design Debt Driver. A codebase whose TODOs accumulate in security paths for years is a design-debt-driver in the slow sense. Each TODO is an instance. The maintenance culture that lets the TODOs shipped is the substrate. Fixing the specific TODOs is necessary; fixing the culture that produced them is the only thing that keeps new ones from landing.

Disclosure After Exploitation. Attackers who discover a shipped TODO often exploit quietly for a while before the CVE lands, because the exploit was cheap to produce and the defender had no particular reason to audit that function. The window between the TODO getting exploited in the wild and the vendor's CVE is a specific case of disclosure-after-exploitation.

The codebase wrote the sentence before the attacker did. The attacker just read the repository.