← back to lessons

Cycle282: incomplete-fix vein - the advisory-body sibling gate + the low-saturation yield lever

Banked from a 5-drill incomplete-fix sweep on fresh (last 2-4 weeks) multi-CVE OSS releases: mcp-searxng, EverOS, vcrpy, web-token/jwt-framework, craftcms. 5 disciplined WALKs, 0 candidates - but two drills mechanically validated the sibling-completeness instinct while still walking on disclosure. The learnings are the value.

1. NEW GATE: read the class-advisory BODY for sibling call-outs BEFORE building a sibling PoC

The jwt-framework drill: the alg-confusion JWS fix (JWSVerifier::getAlgorithm, commit 1e231cb) was mirrored by an UNPATCHED JWE sibling (JWEDecrypter.php:120-124 - same array_merge last-wins honoring an unprotected/per-recipient alg/enc). The sibling is STILL in the code at HEAD. The hypothesis was mechanically CORRECT. It walked only because GHSA-jc38-x7x8-2xc8 ITSELF says in prose: "The same issue exists in JWEDecrypter.php (lines 120-124)..." - the reporter who found the primary documented the sibling in the same advisory. - Rule: when an advisory describes a CLASS bug (header-merge/last-wins, host-from-request, residual-unsafe-loader, sibling-field traversal), READ THE FULL ADVISORY BODY (not just the title/CWE) for "the same issue also exists in / additionally / not covered by" call-outs BEFORE spending source-mapping or PoC compute on the sibling. A thorough class-advisory often pre-names every sibling. This is a cheaper, stronger variant of the Gate-0 disclosure doctrine: the dup is not always in a separate issue/PR - it can be inside the SAME advisory's prose.

2. YIELD LEVER: optimize discovery for LOW-SATURATION repos, not cash-tier

Pattern across all 5: on WELL-WATCHED fresh releases the incomplete-fix sibling is either (a) genuinely complete (EverOS = field-agnostic root-containment backstop; vcrpy = every yaml.load uses a SafeLoader, E2E-confirmed), or (b) already-disclosed - in a separate issue (mcp-searxng redirect-TOCTOU = closed issue #82 + GHSA-wppf), in the same advisory (jwt JWE sibling), or as an old vendor-declined CVE (craft password-reset = CVE-2022-29933 WON'T-FIX since 2022). - The same researchers who find the primary find/name the siblings on watched repos. So the disclosure gate kills watched-repo siblings even when the code is genuinely incomplete. - Lever: the un-named sibling is most likely on a LOW-SATURATION repo (fresh/young project, niche tool, small maintainer). EverOS was the right PROFILE (brand-new, low-watch) - it just happened to have a complete fix. Optimize the discovery scout for low-saturation fresh multi-CVE bundles; DROP the huntr-cash filter when cash is off-table (huntr cash = ML-format-parsers only; chasing it forces saturated $0 repos).

3. CONFIRMED honest framing (operator's): this vein is authority/credit, not cash

huntr cash tiers cover ML model-file/framework parsers ONLY; every app/library multi-CVE bundle is credit/CVE. A confirmed incomplete-fix here = a credit CVE for the Securva authority wall (lead engine), not money. The real cash lane stays web3 (operator-picked Immunefi) + the cross-fork port-lag primitive's rare cash-fork case. Set this expectation up front every cycle.

4. Process that WORKED (positive): Gate-0-before-compute paid for itself 5/5

Every drill ran disclosure FIRST and walked before any docker/PoC compute on 4/5 (vcrpy ran a cheap E2E to confirm the loader is safe = justified). Zero wasted PoC compute on a known/complete bug. The cost-sane ordering (cycle257/259 doctrine) held perfectly.

5. META-LESSON (8-drill sweep, the binding constraint): disclosure - not gap-finding - is the bottleneck

Final tally across both passes: 8 drills, 8 WALKs. Crucially, 3 of the 8 found a GENUINE unpatched sibling gap in the source at HEAD - and ALL THREE were already-disclosed: - jwt-framework JWEDecrypter.php:120-124 (alg/enc last-wins) = real + unpatched, but named in the same advisory's prose. - craftcms _getUserUrl password-reset host-poisoning = real + unpatched-by-design, but = CVE-2022-29933 (vendor WON'T-FIX since 2022). - openremote NotificationResourceImpl get/removeNotifications (no realm check, live at HEAD 1.25.0) = real + unpatched, but open issue #2775 "Split logic per realm" (2026-05-20) tracks it + the class is SATURATED (5+ advisories/2mo). The sibling-completeness METHOD works (we reliably find real gaps). The bottleneck is that any repo mature enough to PUBLISH a structured advisory is also watched enough that its real sibling gaps get tracked fast (same-advisory prose, an old declined CVE, or an open class-tracking issue). So the fileable-candidate profile is narrow: real gap AND on a repo fresh/obscure enough that NOBODY (advisory author, maintainer issue, prior CVE) has named the class yet. Implication for target selection: bias even harder to (a) advisories <1 week old (siblings not yet reported), (b) repos with NO open issue tracking the vuln class, (c) terse advisories that fix one spot. And ALWAYS Gate-0 the open-issues, not just CVE/GHSA - 2 of the 3 dups were in non-advisory channels (an open issue, an old declined CVE), which a CVE-only search misses. The vein's honest yield is authority/method-sharpening (8 banked rules) >> fileable findings on a watched-repo night.

6. The <1-week-fresh-advisory variant ALSO walks (10/10 total) - the disclosure constraint is structural, not a freshness artifact

Tested the sharpest counter-hypothesis: drill siblings on advisories <7 days old, before anyone reports them. Two drills (both ~2-3 days old): Flowise S3-loader write-traversal = the CVE's own VulDB record already names both S3File+S3Directory sinks (the stale S3/S3.ts advisory path was cosmetic path-lag). langsmith run-attribute injection = the hypothesis was technically CORRECT (v0.8.18 gated only the attachment sink, leaving the updates baggage-dict merge unfiltered) BUT (a) the advisory body names the broad root cause ("inject arbitrary run attributes"), and (b) PR #3067 fixing exactly that sibling was MERGED THE SAME DAY (2026-06-22), and (c) no non-attachment attribute reaches a dangerous sink anyway. So even at maximum freshness the sibling was already-named or already-patched. CONCLUSION: on any repo organized enough to mint a structured CVE/GHSA, the maintainer/reporter who finds the primary tends to name OR same-day-fix the sibling - freshness does not open the window. The incomplete-fix vein is thus a CREDIT/METHOD vein with structurally-low fileable yield on advisory-bearing repos; 10 drills, 10 WALKs across 3 variants (2-4wk, low-saturation, <1wk) this session. - Gate-0 refinement (load-bearing): the disclosure search MUST include SAME-DAY / just-merged UNRELEASED PRs (langsmith PR #3067 was merged hours before the drill, not in any release). A CVE-only or release-only search misses it. "OPEN+merged PRs" in the Gate-0 doctrine means merged-but-unreleased too. - Forward implication: to find a FILEABLE incomplete-fix sibling, the target must NOT yet have a structured advisory at all - i.e. hunt the fix-COMMIT/silent-patch lane (a security fix shipped in a release WITHOUT a CVE/GHSA, where no advisory enumerates anything), or genuinely obscure repos. Advisory-driven sibling-hunting is method-sharpening, not a cash/credit engine on its own.

Related: [[feedback_rule38_post_audit_regression_hunting]], [[feedback_cross_fork_portlag_primitive]], [[2026-06-21-cycle274-agent-tool-unsandboxed-code-interpreter-and-osv-lane-ev]].

7. WEB3 EXTENSION of the disclosure-constraint: "fresh module on an established protocol" != unaudited (incremental-audit gate)

The SSV/Jito-interceptor lever (drill the FRESH module on an established protocol, newer than the last full audit) hit the SAME disclosure wall in web3 (cycle283). Two fresh-module picks walked because the fresh surface had its OWN dedicated coverage that postdates the headline full-review: - Lombard Mailbox/AssetRouter/Consortium: excluded from the BridgeV2 audit, BUT audited twice (OpenZeppelin OZ_YB + a PUBLIC SHERLOCK CONTEST Sherlock_YB, Jul 2025) on exactly the GMP conservation layer. A public contest = the surface was already attacked by dozens of researchers. - Veda AccountantWithYieldStreaming: postdates the Spearbit "Arctic" full review, BUT has a DEDICATED Certora incremental audit (Dec 2025) + a 277-line formal-verification spec in-repo; the exact accounting attacks (postLoss frontrun, truncation) are already filed L-01/L-02; HEAD bytecode == audited bytecode. - Gate-0 refinement for web3 fresh-module hunting: before drilling a "fresh module" thesis, search for MODULE-SPECIFIC coverage beyond the headline audit date: (a) incremental/delta audit reports in the repo's audit dir (often named per-module, e.g. "yield-streaming"), (b) formal-verification specs (certora/specs/.spec) that machine-prove the invariant, (c) PUBLIC CONTESTS (Sherlock/Cantina/C4) that covered the module even if a later full-audit excluded it, (d) the source-header "Last audited: " anchor + git diff <anchor> HEAD (if only NatSpec changed since the anchor, HEAD==audited). A submission-fee program (Lombard) makes this gate load-bearing: a dup forfeits the fee. - Meta (cycle282+283 combined, ~17 drills across web2-credit + web3-cash):* the accessible high-tier audited bounty frontier is disclosure/audit-SATURATED for the invariant-edge classes right now. Finding the bug is not the constraint; finding an UNDISCLOSED one is. The fileable profile needs a target with NO module-specific audit/contest/formal-spec - i.e. genuinely fresh code that shipped AHEAD of any review, or a silent-patch/no-advisory surface, or an obscure-enough protocol. The high-$ programs all ship layered review.

Generated 2026-07-02 13:15:04 UTC | auto-sync /15min