mutated × shared — the answer changes between runs

Plugin Registration / Dispatch-Order Divergence

Handlers auto-register via a decorator whose effective order depends on import/await timing -- so when two claim the same route, which one wins changes run to run.

01the recipe

In the wild

example.py
# SMELL: handlers self-register at import; last writer wins, order undefined.
# (macro & mixin misuse x async I/O misuse)
HANDLERS = {}
def route(path):
    def deco(fn):
        HANDLERS[path] = fn      # silently overwrites any prior handler
        return fn
    return deco
# two plugins both @route('/pay'); whichever module imports last wins,
# and lazy/async import makes that order nondeterministic.

# RIGHT: register explicitly with a priority; reject silent duplicates.
def register(path, fn, priority):
    cur = HANDLERS.get(path)
    if cur and cur.priority >= priority:
        return
    HANDLERS[path] = Handler(fn, priority)   # deterministic resolution
A decorator/metaclass that registers code out of sight (macro & mixin misuse) makes the registry a function of import order; when plugins load lazily or via async readiness, that order is unspecified, so duplicate routes resolve to a different handler between runs. Relying on that order is reliance on unspecified behavior (CWE-758). Register in one synchronous phase with explicit priorities and duplicate detection.
// observed
implicit: /pay resolves to a different plugin depending on import timing
right: explicit priority -> the same handler wins every run
02weakness catalog

Mapped weaknesses (CWE)

On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.